[Python-checkins] gh-90473: Make chmod a dummy on WASI, skip chmod tests (GH-93534) (GH-93550)

ambv webhook-mailer at python.org
Mon Jun 6 14:09:55 EDT 2022


https://github.com/python/cpython/commit/6787a8f1466737f813c5095df93ae4dcd699c6df
commit: 6787a8f1466737f813c5095df93ae4dcd699c6df
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: ambv <lukasz at langa.pl>
date: 2022-06-06T20:09:51+02:00
summary:

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 22fed605e096eb74f3aa33f6d25aee76fdc2a3fa)

Co-authored-by: Christian Heimes <christian at python.org>

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

diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py
index a6ec1cfa25ebe..ff2fc338cbf23 100644
--- a/Lib/test/support/os_helper.py
+++ b/Lib/test/support/os_helper.py
@@ -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)
diff --git a/Lib/test/test_argparse.py b/Lib/test/test_argparse.py
index 273db45c00f7a..299eb30b4332b 100644
--- a/Lib/test/test_argparse.py
+++ b/Lib/test/test_argparse.py
@@ -45,6 +45,7 @@ def setUp(self):
         env['COLUMNS'] = '80'
 
 
+ at os_helper.skip_unless_working_chmod
 class TempDirMixin(object):
 
     def setUp(self):
diff --git a/Lib/test/test_dbm_dumb.py b/Lib/test/test_dbm_dumb.py
index 73cff638f1e1a..a481175b3bfdb 100644
--- a/Lib/test/test_dbm_dumb.py
+++ b/Lib/test/test_dbm_dumb.py
@@ -42,6 +42,7 @@ def test_dumbdbm_creation(self):
             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 @@ def test_invalid_flag(self):
                                         "'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')
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index 35c260bd634fc..be2e91ddd9a9a 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -557,6 +557,7 @@ def test_creation_mode(self):
 
     @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 @@ def test_cached_mode_issue_2051(self):
 
     @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 @@ def test_import_pyc_path(self):
     @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.
diff --git a/Lib/test/test_netrc.py b/Lib/test/test_netrc.py
index 05a23e5e8a3ce..573d636de956d 100644
--- a/Lib/test/test_netrc.py
+++ b/Lib/test/test_netrc.py
@@ -272,6 +272,7 @@ def test_comment_at_end_of_machine_line_pass_has_hash(self):
 
     @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.
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
index c0321dcdcc071..29f69a8f475b2 100644
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -1670,7 +1670,7 @@ def tearDown(self):
         os.removedirs(path)
 
 
- at unittest.skipUnless(hasattr(os, 'chown'), "Test needs chown")
+ at os_helper.skip_unless_working_chmod
 class ChownFileTests(unittest.TestCase):
 
     @classmethod
@@ -3784,7 +3784,6 @@ class Str(str):
     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 @@ def test_oserror_filename(self):
                 (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"):
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
index e2da115501abe..3b88c0848a044 100644
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -1902,6 +1902,7 @@ def test_with(self):
             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 @@ def test_chmod(self):
 
     # 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 @@ def test_chmod_follow_symlinks_true(self):
 
     # 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()
diff --git a/Lib/test/test_posix.py b/Lib/test/test_posix.py
index 7efe3c5ceb6b3..4130cdd0c022a 100644
--- a/Lib/test/test_posix.py
+++ b/Lib/test/test_posix.py
@@ -786,7 +786,7 @@ def check_stat(uid, gid):
             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 @@ def test_chown(self):
         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 @@ def test_fchown(self):
         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)
diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py
index 97d3e9ea15bf3..c644f881e460f 100644
--- a/Lib/test/test_posixpath.py
+++ b/Lib/test/test_posixpath.py
@@ -193,8 +193,7 @@ def test_ismount_non_existent(self):
         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:
diff --git a/Lib/test/test_py_compile.py b/Lib/test/test_py_compile.py
index 794d6436b61ab..f494aed42feae 100644
--- a/Lib/test/test_py_compile.py
+++ b/Lib/test/test_py_compile.py
@@ -119,6 +119,7 @@ def test_relative_path(self):
                      '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.
diff --git a/Lib/test/test_pydoc.py b/Lib/test/test_pydoc.py
index ac181effe49bb..b705b7aee8136 100644
--- a/Lib/test/test_pydoc.py
+++ b/Lib/test/test_pydoc.py
@@ -933,6 +933,7 @@ def test_apropos_with_unreadable_dir(self):
         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)
diff --git a/Lib/test/test_shutil.py b/Lib/test/test_shutil.py
index c94390589af3e..18421fca9c21b 100644
--- a/Lib/test/test_shutil.py
+++ b/Lib/test/test_shutil.py
@@ -311,6 +311,7 @@ def onerror(*args):
                      "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)
diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py
index 193a0fc15d9bc..4ba37aed2dc9d 100644
--- a/Lib/test/test_stat.py
+++ b/Lib/test/test_stat.py
@@ -113,6 +113,7 @@ def assertS_IS(self, name, mode):
             else:
                 self.assertFalse(func(mode))
 
+    @os_helper.skip_unless_working_chmod
     def test_mode(self):
         with open(TESTFN, 'w'):
             pass
@@ -151,6 +152,7 @@ def test_mode(self):
             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)
diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py
index a364043d3d9dd..5c084688dc24d 100644
--- a/Lib/test/test_tarfile.py
+++ b/Lib/test/test_tarfile.py
@@ -630,6 +630,7 @@ def test_extract_hardlink(self):
                 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 @@ def format_mtime(mtime):
             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")
diff --git a/Lib/test/test_tempfile.py b/Lib/test/test_tempfile.py
index f056e5ccb17f9..20f88d8c71e89 100644
--- a/Lib/test/test_tempfile.py
+++ b/Lib/test/test_tempfile.py
@@ -450,6 +450,7 @@ def test_choose_directory(self):
             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 @@ def test_choose_directory(self):
         finally:
             os.rmdir(dir)
 
+    @os_helper.skip_unless_working_chmod
     def test_mode(self):
         # mkdtemp creates directories with the proper mode
 
diff --git a/Lib/test/test_uu.py b/Lib/test/test_uu.py
index 316a04af1cdaa..0493aae4fc67b 100644
--- a/Lib/test/test_uu.py
+++ b/Lib/test/test_uu.py
@@ -74,6 +74,7 @@ def test_encode(self):
         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 @@ def test_encode(self):
             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 @@ def test_decode(self):
         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 @@ def test_decode_filename(self):
             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 @@ def test_decodetwice(self):
         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
diff --git a/Lib/test/test_zipapp.py b/Lib/test/test_zipapp.py
index 69f2e55d56384..371e60118fff9 100644
--- a/Lib/test/test_zipapp.py
+++ b/Lib/test/test_zipapp.py
@@ -9,6 +9,7 @@
 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 @@ def test_content_of_copied_archive(self):
     # (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
index 0000000000000..6c76b7f4990e4
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2022-06-05-10-16-45.gh-issue-90473.QMu7A8.rst
@@ -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`.
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 40158894411ba..d7cac2b67ffdf 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -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;
diff --git a/Tools/wasm/config.site-wasm32-wasi b/Tools/wasm/config.site-wasm32-wasi
index a6fcbed48fa81..f151b7bc5ab0c 100644
--- a/Tools/wasm/config.site-wasm32-wasi
+++ b/Tools/wasm/config.site-wasm32-wasi
@@ -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



More information about the Python-checkins mailing list