diff --git a/Lib/test/support/os_helper.py b/Lib/test/support/os_helper.py index a6ec1cfa25ebe0..ff2fc338cbf239 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 273db45c00f7ac..299eb30b4332bf 100644 --- a/Lib/test/test_argparse.py +++ b/Lib/test/test_argparse.py @@ -45,6 +45,7 @@ def setUp(self): env['COLUMNS'] = '80' +@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 73cff638f1e1a4..a481175b3bfdbd 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 35c260bd634fc6..be2e91ddd9a9ab 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 05a23e5e8a3ce2..573d636de956d1 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 c0321dcdcc071a..29f69a8f475b20 100644 --- a/Lib/test/test_os.py +++ b/Lib/test/test_os.py @@ -1670,7 +1670,7 @@ def tearDown(self): 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 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 2215134c7b5885..a42619853399f2 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 28e5e90297e242..f45fae7edb8e4d 100644 --- a/Lib/test/test_posix.py +++ b/Lib/test/test_posix.py @@ -784,7 +784,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) @@ -794,6 +794,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) @@ -807,6 +808,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 97d3e9ea15bf33..c644f881e460fe 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 794d6436b61ab0..f494aed42feae3 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 ac181effe49bb6..b705b7aee8136d 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 c94390589af3ed..18421fca9c21b6 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 193a0fc15d9bca..4ba37aed2dc9db 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 a364043d3d9dd8..5c084688dc24d5 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 f056e5ccb17f92..20f88d8c71e89a 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 316a04af1cdaa7..0493aae4fc67be 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 d135c68865812f..f1c6b2d97621ee 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 @@ -317,6 +318,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 00000000000000..6c76b7f4990e4f --- /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 6883ad0c12ccf1..d41e19e6527147 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -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; diff --git a/Tools/wasm/config.site-wasm32-wasi b/Tools/wasm/config.site-wasm32-wasi index a6fcbed48fa811..f151b7bc5ab0c9 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 pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy