]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-90473: Make chmod a dummy on WASI, skip chmod tests (GH-93534)
authorChristian Heimes <christian@python.org>
Mon, 6 Jun 2022 17:24:11 +0000 (19:24 +0200)
committerGitHub <noreply@github.com>
Mon, 6 Jun 2022 17:24:11 +0000 (19:24 +0200)
WASI does not have the ``chmod(2)`` syscall yet.

20 files changed:
Lib/test/support/os_helper.py
Lib/test/test_argparse.py
Lib/test/test_dbm_dumb.py
Lib/test/test_import/__init__.py
Lib/test/test_netrc.py
Lib/test/test_os.py
Lib/test/test_pathlib.py
Lib/test/test_posix.py
Lib/test/test_posixpath.py
Lib/test/test_py_compile.py
Lib/test/test_pydoc.py
Lib/test/test_shutil.py
Lib/test/test_stat.py
Lib/test/test_tarfile.py
Lib/test/test_tempfile.py
Lib/test/test_uu.py
Lib/test/test_zipapp.py
Misc/NEWS.d/next/Tests/2022-06-05-10-16-45.gh-issue-90473.QMu7A8.rst [new file with mode: 0644]
Modules/posixmodule.c
Tools/wasm/config.site-wasm32-wasi

index a6ec1cfa25ebe03ae5179e5dbd41db5c90e56200..ff2fc338cbf239f45761666d6813ff0d25864cfc 100644 (file)
@@ -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)
index 273db45c00f7ac54c622e0d946e77902bd73dc25..299eb30b4332bf03501a619012ab5255d0bda9b7 100644 (file)
@@ -45,6 +45,7 @@ class TestCase(unittest.TestCase):
         env['COLUMNS'] = '80'
 
 
+@os_helper.skip_unless_working_chmod
 class TempDirMixin(object):
 
     def setUp(self):
index 73cff638f1e1a4ded3043937358f9b129699ccf6..a481175b3bfdbdebab2c8c0a77db54e8c2fe3479 100644 (file)
@@ -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')
index 35c260bd634fc6008ed552d0e668ba51916f0fe6..be2e91ddd9a9abb687baf5aef3fa182abde8e85a 100644 (file)
@@ -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.
index 05a23e5e8a3ce2c79c9423fe83a0e820eb168508..573d636de956d16649a97a053d807a830188853a 100644 (file)
@@ -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.
index c0321dcdcc071a20e12de2201e57bd26e38560e6..29f69a8f475b206fc097b7daf9e7cb36b941aff6 100644 (file)
@@ -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"):
index 2215134c7b5885ac3604ace592709557654e565e..a42619853399f2bfa904bb6876ce6295b874966c 100644 (file)
@@ -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()
index 7efe3c5ceb6b379178b016d9acf0e7e54b9cfb62..4130cdd0c022ad0a83915a336d7528f076ab6258 100644 (file)
@@ -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)
index 97d3e9ea15bf33bef64c8fa35e960ce0d0c30e8f..c644f881e460fedce631664a4f336f1e74a472b1 100644 (file)
@@ -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:
index 794d6436b61ab02e0d8f6de8d32f421a23a8a200..f494aed42feae33b5e2d0a75ee5688a0c4cc6725 100644 (file)
@@ -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.
index ac181effe49bb683927006e1564f87e64d50294c..b705b7aee8136d48636d7768e3afeb98be5203fa 100644 (file)
@@ -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)
index c94390589af3ed00680eb6f4348b2edc1a9a1453..18421fca9c21b60750c7810843d7dc9a73de2958 100644 (file)
@@ -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)
index 193a0fc15d9bca35e88d114f63304e00fc930fc3..4ba37aed2dc9db3469f618538c94b9da355a4b15 100644 (file)
@@ -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)
index a364043d3d9dd81f9edc291c99b5554b3733cba9..5c084688dc24d562b70045e976c8589891e24846 100644 (file)
@@ -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")
index f056e5ccb17f9280ee914f0743acd8252e2feab7..20f88d8c71e89a69c0a1f4bd2eb6cde3a048ee35 100644 (file)
@@ -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
 
index 316a04af1cdaa766ab711c55951e63b14ad0c603..0493aae4fc67bec6198f66ef84054decb2b433b3 100644 (file)
@@ -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
index d135c68865812f83ed795b330e4f57fd744a02d1..f1c6b2d97621ee5bb2c1041801e6c73bb58d6289 100644 (file)
@@ -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
 
@@ -317,6 +318,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'
diff --git a/Misc/NEWS.d/next/Tests/2022-06-05-10-16-45.gh-issue-90473.QMu7A8.rst b/Misc/NEWS.d/next/Tests/2022-06-05-10-16-45.gh-issue-90473.QMu7A8.rst
new file mode 100644 (file)
index 0000000..6c76b7f
--- /dev/null
@@ -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`.
index 6883ad0c12ccf1d76a5d73fe27e8966224a3e099..d41e19e6527147dfa107461b977fc0d5c5fc6b49 100644 (file)
@@ -3282,6 +3282,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;
index a6fcbed48fa8110b8553c9fe1707fd8af5aec3e1..f151b7bc5ab0c93aeb7b5561525774a92b8153cc 100644 (file)
@@ -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