mirror of https://github.com/python/cpython
gh-90473: Make chmod a dummy on WASI, skip chmod tests (GH-93534) (GH-93550)
WASI does not have the ``chmod(2)`` syscall yet.
(cherry picked from commit 22fed605e0
)
Co-authored-by: Christian Heimes <christian@python.org>
This commit is contained in:
parent
986ce4e613
commit
6787a8f146
|
@ -237,6 +237,42 @@ def skip_unless_xattr(test):
|
|||
return test if ok else unittest.skip(msg)(test)
|
||||
|
||||
|
||||
_can_chmod = None
|
||||
|
||||
def can_chmod():
|
||||
global _can_chmod
|
||||
if _can_chmod is not None:
|
||||
return _can_chmod
|
||||
if not hasattr(os, "chown"):
|
||||
_can_chmod = False
|
||||
return _can_chmod
|
||||
try:
|
||||
with open(TESTFN, "wb") as f:
|
||||
try:
|
||||
os.chmod(TESTFN, 0o777)
|
||||
mode1 = os.stat(TESTFN).st_mode
|
||||
os.chmod(TESTFN, 0o666)
|
||||
mode2 = os.stat(TESTFN).st_mode
|
||||
except OSError as e:
|
||||
can = False
|
||||
else:
|
||||
can = stat.S_IMODE(mode1) != stat.S_IMODE(mode2)
|
||||
finally:
|
||||
os.unlink(TESTFN)
|
||||
_can_chmod = can
|
||||
return can
|
||||
|
||||
|
||||
def skip_unless_working_chmod(test):
|
||||
"""Skip tests that require working os.chmod()
|
||||
|
||||
WASI SDK 15.0 cannot change file mode bits.
|
||||
"""
|
||||
ok = can_chmod()
|
||||
msg = "requires working os.chmod()"
|
||||
return test if ok else unittest.skip(msg)(test)
|
||||
|
||||
|
||||
def unlink(filename):
|
||||
try:
|
||||
_unlink(filename)
|
||||
|
|
|
@ -45,6 +45,7 @@ class TestCase(unittest.TestCase):
|
|||
env['COLUMNS'] = '80'
|
||||
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
class TempDirMixin(object):
|
||||
|
||||
def setUp(self):
|
||||
|
|
|
@ -42,6 +42,7 @@ class DumbDBMTestCase(unittest.TestCase):
|
|||
self.read_helper(f)
|
||||
|
||||
@unittest.skipUnless(hasattr(os, 'umask'), 'test needs os.umask()')
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_dumbdbm_creation_mode(self):
|
||||
try:
|
||||
old_umask = os.umask(0o002)
|
||||
|
@ -265,6 +266,7 @@ class DumbDBMTestCase(unittest.TestCase):
|
|||
"'r', 'w', 'c', or 'n'"):
|
||||
dumbdbm.open(_fname, flag)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_readonly_files(self):
|
||||
with os_helper.temp_dir() as dir:
|
||||
fname = os.path.join(dir, 'db')
|
||||
|
|
|
@ -557,6 +557,7 @@ class FilePermissionTests(unittest.TestCase):
|
|||
|
||||
@unittest.skipUnless(os.name == 'posix',
|
||||
"test meaningful only on posix systems")
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_cached_mode_issue_2051(self):
|
||||
# permissions of .pyc should match those of .py, regardless of mask
|
||||
mode = 0o600
|
||||
|
@ -573,6 +574,7 @@ class FilePermissionTests(unittest.TestCase):
|
|||
|
||||
@unittest.skipUnless(os.name == 'posix',
|
||||
"test meaningful only on posix systems")
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_cached_readonly(self):
|
||||
mode = 0o400
|
||||
with temp_umask(0o022), _ready_to_import() as (name, path):
|
||||
|
@ -886,6 +888,7 @@ class PycacheTests(unittest.TestCase):
|
|||
@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
|
||||
"due to varying filesystem permission semantics (issue #11956)")
|
||||
@skip_if_dont_write_bytecode
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_unwritable_directory(self):
|
||||
# When the umask causes the new __pycache__ directory to be
|
||||
# unwritable, the import still succeeds but no .pyc file is written.
|
||||
|
|
|
@ -272,6 +272,7 @@ class NetrcTestCase(unittest.TestCase):
|
|||
|
||||
@unittest.skipUnless(os.name == 'posix', 'POSIX only test')
|
||||
@unittest.skipIf(pwd is None, 'security check requires pwd module')
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_security(self):
|
||||
# This test is incomplete since we are normally not run as root and
|
||||
# therefore can't test the file ownership being wrong.
|
||||
|
|
|
@ -1670,7 +1670,7 @@ class MakedirTests(unittest.TestCase):
|
|||
os.removedirs(path)
|
||||
|
||||
|
||||
@unittest.skipUnless(hasattr(os, 'chown'), "Test needs chown")
|
||||
@os_helper.skip_unless_working_chmod
|
||||
class ChownFileTests(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
|
@ -3784,7 +3784,6 @@ class OSErrorTests(unittest.TestCase):
|
|||
def test_oserror_filename(self):
|
||||
funcs = [
|
||||
(self.filenames, os.chdir,),
|
||||
(self.filenames, os.chmod, 0o777),
|
||||
(self.filenames, os.lstat,),
|
||||
(self.filenames, os.open, os.O_RDONLY),
|
||||
(self.filenames, os.rmdir,),
|
||||
|
@ -3805,6 +3804,8 @@ class OSErrorTests(unittest.TestCase):
|
|||
(self.filenames, os.rename, "dst"),
|
||||
(self.filenames, os.replace, "dst"),
|
||||
))
|
||||
if os_helper.can_chmod():
|
||||
funcs.append((self.filenames, os.chmod, 0o777))
|
||||
if hasattr(os, "chown"):
|
||||
funcs.append((self.filenames, os.chown, 0, 0))
|
||||
if hasattr(os, "lchown"):
|
||||
|
|
|
@ -1902,6 +1902,7 @@ class _BasePathTest(object):
|
|||
with p:
|
||||
pass
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_chmod(self):
|
||||
p = self.cls(BASE) / 'fileA'
|
||||
mode = p.stat().st_mode
|
||||
|
@ -1916,6 +1917,7 @@ class _BasePathTest(object):
|
|||
|
||||
# On Windows, os.chmod does not follow symlinks (issue #15411)
|
||||
@only_posix
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_chmod_follow_symlinks_true(self):
|
||||
p = self.cls(BASE) / 'linkA'
|
||||
q = p.resolve()
|
||||
|
@ -1931,6 +1933,7 @@ class _BasePathTest(object):
|
|||
|
||||
# XXX also need a test for lchmod.
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_stat(self):
|
||||
p = self.cls(BASE) / 'fileA'
|
||||
st = p.stat()
|
||||
|
|
|
@ -786,7 +786,7 @@ class PosixTester(unittest.TestCase):
|
|||
self.assertRaises(TypeError, chown_func, first_param, uid, t(gid))
|
||||
check_stat(uid, gid)
|
||||
|
||||
@unittest.skipUnless(hasattr(posix, 'chown'), "test needs os.chown()")
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_chown(self):
|
||||
# raise an OSError if the file does not exist
|
||||
os.unlink(os_helper.TESTFN)
|
||||
|
@ -796,6 +796,7 @@ class PosixTester(unittest.TestCase):
|
|||
os_helper.create_empty_file(os_helper.TESTFN)
|
||||
self._test_all_chown_common(posix.chown, os_helper.TESTFN, posix.stat)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
@unittest.skipUnless(hasattr(posix, 'fchown'), "test needs os.fchown()")
|
||||
def test_fchown(self):
|
||||
os.unlink(os_helper.TESTFN)
|
||||
|
@ -809,6 +810,7 @@ class PosixTester(unittest.TestCase):
|
|||
finally:
|
||||
test_file.close()
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
@unittest.skipUnless(hasattr(posix, 'lchown'), "test needs os.lchown()")
|
||||
def test_lchown(self):
|
||||
os.unlink(os_helper.TESTFN)
|
||||
|
|
|
@ -193,8 +193,7 @@ class PosixPathTest(unittest.TestCase):
|
|||
self.assertIs(posixpath.ismount('/\x00'), False)
|
||||
self.assertIs(posixpath.ismount(b'/\x00'), False)
|
||||
|
||||
@unittest.skipUnless(os_helper.can_symlink(),
|
||||
"Test requires symlink support")
|
||||
@os_helper.skip_unless_symlink
|
||||
def test_ismount_symlinks(self):
|
||||
# Symlinks are never mountpoints.
|
||||
try:
|
||||
|
|
|
@ -119,6 +119,7 @@ class PyCompileTestsBase:
|
|||
'non-root user required')
|
||||
@unittest.skipIf(os.name == 'nt',
|
||||
'cannot control directory permissions on Windows')
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_exceptions_propagate(self):
|
||||
# Make sure that exceptions raised thanks to issues with writing
|
||||
# bytecode.
|
||||
|
|
|
@ -933,6 +933,7 @@ class PydocImportTest(PydocBaseTest):
|
|||
self.assertEqual(out.getvalue(), '')
|
||||
self.assertEqual(err.getvalue(), '')
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_apropos_empty_doc(self):
|
||||
pkgdir = os.path.join(TESTFN, 'walkpkg')
|
||||
os.mkdir(pkgdir)
|
||||
|
|
|
@ -311,6 +311,7 @@ class TestRmTree(BaseTest, unittest.TestCase):
|
|||
"This test can't be run on Cygwin (issue #1071513).")
|
||||
@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
|
||||
"This test can't be run reliably as root (issue #1076467).")
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_on_error(self):
|
||||
self.errorState = 0
|
||||
os.mkdir(TESTFN)
|
||||
|
|
|
@ -113,6 +113,7 @@ class TestFilemode:
|
|||
else:
|
||||
self.assertFalse(func(mode))
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_mode(self):
|
||||
with open(TESTFN, 'w'):
|
||||
pass
|
||||
|
@ -151,6 +152,7 @@ class TestFilemode:
|
|||
self.assertEqual(self.statmod.S_IFMT(st_mode),
|
||||
self.statmod.S_IFREG)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_directory(self):
|
||||
os.mkdir(TESTFN)
|
||||
os.chmod(TESTFN, 0o700)
|
||||
|
|
|
@ -630,6 +630,7 @@ class MiscReadTestBase(CommonReadTest):
|
|||
data = f.read()
|
||||
self.assertEqual(sha256sum(data), sha256_regtype)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_extractall(self):
|
||||
# Test if extractall() correctly restores directory permissions
|
||||
# and times (see issue1735).
|
||||
|
@ -660,6 +661,7 @@ class MiscReadTestBase(CommonReadTest):
|
|||
tar.close()
|
||||
os_helper.rmtree(DIR)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_extract_directory(self):
|
||||
dirtype = "ustar/dirtype"
|
||||
DIR = os.path.join(TEMPDIR, "extractdir")
|
||||
|
|
|
@ -450,6 +450,7 @@ class TestMkstempInner(TestBadTempdir, BaseTestCase):
|
|||
support.gc_collect() # For PyPy or other GCs.
|
||||
os.rmdir(dir)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_file_mode(self):
|
||||
# _mkstemp_inner creates files with the proper mode
|
||||
|
||||
|
@ -787,6 +788,7 @@ class TestMkdtemp(TestBadTempdir, BaseTestCase):
|
|||
finally:
|
||||
os.rmdir(dir)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_mode(self):
|
||||
# mkdtemp creates directories with the proper mode
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ class UUTest(unittest.TestCase):
|
|||
with self.assertRaises(TypeError):
|
||||
uu.encode(inp, out, "t1", 0o644, True)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_decode(self):
|
||||
for backtick in True, False:
|
||||
inp = io.BytesIO(encodedtextwrapped(0o666, "t1", backtick=backtick))
|
||||
|
@ -199,6 +200,8 @@ class UUFileTest(unittest.TestCase):
|
|||
s = fout.read()
|
||||
self.assertEqual(s, encodedtextwrapped(0o644, self.tmpin))
|
||||
|
||||
# decode() calls chmod()
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_decode(self):
|
||||
with open(self.tmpin, 'wb') as f:
|
||||
f.write(encodedtextwrapped(0o644, self.tmpout))
|
||||
|
@ -211,6 +214,7 @@ class UUFileTest(unittest.TestCase):
|
|||
self.assertEqual(s, plaintext)
|
||||
# XXX is there an xp way to verify the mode?
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_decode_filename(self):
|
||||
with open(self.tmpin, 'wb') as f:
|
||||
f.write(encodedtextwrapped(0o644, self.tmpout))
|
||||
|
@ -221,6 +225,7 @@ class UUFileTest(unittest.TestCase):
|
|||
s = f.read()
|
||||
self.assertEqual(s, plaintext)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_decodetwice(self):
|
||||
# Verify that decode() will refuse to overwrite an existing file
|
||||
with open(self.tmpin, 'wb') as f:
|
||||
|
@ -231,6 +236,7 @@ class UUFileTest(unittest.TestCase):
|
|||
with open(self.tmpin, 'rb') as f:
|
||||
self.assertRaises(uu.Error, uu.decode, f)
|
||||
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_decode_mode(self):
|
||||
# Verify that decode() will set the given mode for the out_file
|
||||
expected_mode = 0o444
|
||||
|
|
|
@ -9,6 +9,7 @@ import unittest
|
|||
import zipapp
|
||||
import zipfile
|
||||
from test.support import requires_zlib
|
||||
from test.support import os_helper
|
||||
|
||||
from unittest.mock import patch
|
||||
|
||||
|
@ -301,6 +302,7 @@ class ZipAppTest(unittest.TestCase):
|
|||
# (Unix only) tests that archives with shebang lines are made executable
|
||||
@unittest.skipIf(sys.platform == 'win32',
|
||||
'Windows does not support an executable bit')
|
||||
@os_helper.skip_unless_working_chmod
|
||||
def test_shebang_is_executable(self):
|
||||
# Test that an archive with a shebang line is made executable.
|
||||
source = self.tmpdir / 'source'
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
WASI does not have a ``chmod(2)`` syscall. :func:`os.chmod` is now a dummy
|
||||
function on WASI. Skip all tests that depend on working :func:`os.chmod`.
|
|
@ -3308,6 +3308,10 @@ os_chmod_impl(PyObject *module, path_t *path, int mode, int dir_fd,
|
|||
{
|
||||
#ifdef HAVE_CHMOD
|
||||
result = chmod(path->narrow, mode);
|
||||
#elif defined(__wasi__)
|
||||
// WASI SDK 15.0 does not support chmod.
|
||||
// Ignore missing syscall for now.
|
||||
result = 0;
|
||||
#else
|
||||
result = -1;
|
||||
errno = ENOSYS;
|
||||
|
|
|
@ -35,6 +35,11 @@ ac_cv_func_fdopendir=no
|
|||
# WASIX stubs we don't want to use.
|
||||
ac_cv_func_kill=no
|
||||
|
||||
# WASI SDK 15.0 does not have chmod.
|
||||
# Ignore WASIX stubs for now.
|
||||
ac_cv_func_chmod=no
|
||||
ac_cv_func_fchmod=no
|
||||
|
||||
# WASI sockets are limited to operations on given socket fd and inet sockets.
|
||||
# Disable AF_UNIX and AF_PACKET support, see socketmodule.h.
|
||||
ac_cv_header_sys_un_h=no
|
||||
|
|
Loading…
Reference in New Issue