Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-90473: Make chmod a dummy on WASI, skip chmod tests #93534

Merged
merged 1 commit into from
Jun 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Lib/test/support/os_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def setUp(self):
env['COLUMNS'] = '80'


@os_helper.skip_unless_working_chmod
ambv marked this conversation as resolved.
Show resolved Hide resolved
class TempDirMixin(object):

def setUp(self):
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_dbm_dumb.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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')
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_netrc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
5 changes: 3 additions & 2 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,),
Expand All @@ -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"):
Expand Down
3 changes: 3 additions & 0 deletions Lib/test/test_pathlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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()
Expand All @@ -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()
Expand Down
4 changes: 3 additions & 1 deletion Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down
3 changes: 1 addition & 2 deletions Lib/test/test_posixpath.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_py_compile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_pydoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_stat.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down Expand Up @@ -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")
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_tempfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_uu.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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))
Expand All @@ -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))
Expand All @@ -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:
Expand All @@ -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
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_zipapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
@@ -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`.
4 changes: 4 additions & 0 deletions Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 5 additions & 0 deletions Tools/wasm/config.site-wasm32-wasi
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down