1515import time
1616import os
1717import platform
18- import pwd
1918import stat
2019import tempfile
2120import unittest
2221import warnings
2322import textwrap
2423from contextlib import contextmanager
2524
25+ try :
26+ import pwd
27+ except ImportError :
28+ pwd = None
29+
2630_DUMMY_SYMLINK = os .path .join (tempfile .gettempdir (),
2731 os_helper .TESTFN + '-dummy-symlink' )
2832
29- requires_32b = unittest .skipUnless (sys .maxsize < 2 ** 32 ,
30- 'test is only meaningful on 32-bit builds' )
33+ requires_32b = unittest .skipUnless (
34+ # Emscripten/WASI have 32 bits pointers, but support 64 bits syscall args.
35+ sys .maxsize < 2 ** 32 and not (support .is_emscripten or support .is_wasi ),
36+ 'test is only meaningful on 32-bit builds'
37+ )
3138
3239def _supports_sched ():
3340 if not hasattr (posix , 'sched_getscheduler' ):
@@ -46,19 +53,13 @@ class PosixTester(unittest.TestCase):
4653
4754 def setUp (self ):
4855 # create empty file
56+ self .addCleanup (os_helper .unlink , os_helper .TESTFN )
4957 with open (os_helper .TESTFN , "wb" ):
5058 pass
51- self .teardown_files = [ os_helper .TESTFN ]
52- self ._warnings_manager = warnings_helper .check_warnings ()
53- self ._warnings_manager .__enter__ ()
59+ self .enterContext (warnings_helper .check_warnings ())
5460 warnings .filterwarnings ('ignore' , '.* potential security risk .*' ,
5561 RuntimeWarning )
5662
57- def tearDown (self ):
58- for teardown_file in self .teardown_files :
59- os_helper .unlink (teardown_file )
60- self ._warnings_manager .__exit__ (None , None , None )
61-
6263 def testNoArgFunctions (self ):
6364 # test posix functions which take no arguments and have
6465 # no side-effects which we need to cleanup (e.g., fork, wait, abort)
@@ -71,8 +72,9 @@ def testNoArgFunctions(self):
7172 for name in NO_ARG_FUNCTIONS :
7273 posix_func = getattr (posix , name , None )
7374 if posix_func is not None :
74- posix_func ()
75- self .assertRaises (TypeError , posix_func , 1 )
75+ with self .subTest (name ):
76+ posix_func ()
77+ self .assertRaises (TypeError , posix_func , 1 )
7678
7779 @unittest .skipUnless (hasattr (posix , 'getresuid' ),
7880 'test needs posix.getresuid()' )
@@ -126,6 +128,7 @@ def test_setresgid_exception(self):
126128
127129 @unittest .skipUnless (hasattr (posix , 'initgroups' ),
128130 "test needs os.initgroups()" )
131+ @unittest .skipUnless (hasattr (pwd , 'getpwuid' ), "test needs pwd.getpwuid()" )
129132 def test_initgroups (self ):
130133 # It takes a string and an integer; check that it raises a TypeError
131134 # for other argument lists.
@@ -184,7 +187,7 @@ def test_truncate(self):
184187 posix .truncate (os_helper .TESTFN , 0 )
185188
186189 @unittest .skipUnless (getattr (os , 'execve' , None ) in os .supports_fd , "test needs execve() to support the fd parameter" )
187- @unittest . skipUnless ( hasattr ( os , 'fork' ), "test needs os.fork()" )
190+ @support . requires_fork ( )
188191 def test_fexecve (self ):
189192 fp = os .open (sys .executable , os .O_RDONLY )
190193 try :
@@ -199,7 +202,7 @@ def test_fexecve(self):
199202
200203
201204 @unittest .skipUnless (hasattr (posix , 'waitid' ), "test needs posix.waitid()" )
202- @unittest . skipUnless ( hasattr ( os , 'fork' ), "test needs os.fork()" )
205+ @support . requires_fork ( )
203206 def test_waitid (self ):
204207 pid = os .fork ()
205208 if pid == 0 :
@@ -209,7 +212,7 @@ def test_waitid(self):
209212 res = posix .waitid (posix .P_PID , pid , posix .WEXITED )
210213 self .assertEqual (pid , res .si_pid )
211214
212- @unittest . skipUnless ( hasattr ( os , 'fork' ), "test needs os.fork()" )
215+ @support . requires_fork ( )
213216 def test_register_at_fork (self ):
214217 with self .assertRaises (TypeError , msg = "Positional args not allowed" ):
215218 os .register_at_fork (lambda : None )
@@ -544,6 +547,7 @@ def test_readv_overflow_32bits(self):
544547
545548 @unittest .skipUnless (hasattr (posix , 'dup' ),
546549 'test needs posix.dup()' )
550+ @unittest .skipIf (support .is_wasi , "WASI does not have dup()" )
547551 def test_dup (self ):
548552 fp = open (os_helper .TESTFN )
549553 try :
@@ -561,6 +565,7 @@ def test_confstr(self):
561565
562566 @unittest .skipUnless (hasattr (posix , 'dup2' ),
563567 'test needs posix.dup2()' )
568+ @unittest .skipIf (support .is_wasi , "WASI does not have dup2()" )
564569 def test_dup2 (self ):
565570 fp1 = open (os_helper .TESTFN )
566571 fp2 = open (os_helper .TESTFN )
@@ -572,6 +577,7 @@ def test_dup2(self):
572577
573578 @unittest .skipUnless (hasattr (os , 'O_CLOEXEC' ), "needs os.O_CLOEXEC" )
574579 @support .requires_linux_version (2 , 6 , 23 )
580+ @support .requires_subprocess ()
575581 def test_oscloexec (self ):
576582 fd = os .open (os_helper .TESTFN , os .O_RDONLY | os .O_CLOEXEC )
577583 self .addCleanup (os .close , fd )
@@ -733,7 +739,11 @@ def check_stat(uid, gid):
733739 is_root = (uid in (0 , 1 ))
734740 else :
735741 is_root = (uid == 0 )
736- if is_root :
742+ if support .is_emscripten :
743+ # Emscripten getuid() / geteuid() always return 0 (root), but
744+ # cannot chown uid/gid to random value.
745+ pass
746+ elif is_root :
737747 # Try an amusingly large uid/gid to make sure we handle
738748 # large unsigned values. (chown lets you use any
739749 # uid/gid you like, even if they aren't defined.)
@@ -778,7 +788,8 @@ def check_stat(uid, gid):
778788 self .assertRaises (TypeError , chown_func , first_param , uid , t (gid ))
779789 check_stat (uid , gid )
780790
781- @unittest .skipUnless (hasattr (posix , 'chown' ), "test needs os.chown()" )
791+ @os_helper .skip_unless_working_chmod
792+ @unittest .skipIf (support .is_emscripten , "getgid() is a stub" )
782793 def test_chown (self ):
783794 # raise an OSError if the file does not exist
784795 os .unlink (os_helper .TESTFN )
@@ -788,7 +799,9 @@ def test_chown(self):
788799 os_helper .create_empty_file (os_helper .TESTFN )
789800 self ._test_all_chown_common (posix .chown , os_helper .TESTFN , posix .stat )
790801
802+ @os_helper .skip_unless_working_chmod
791803 @unittest .skipUnless (hasattr (posix , 'fchown' ), "test needs os.fchown()" )
804+ @unittest .skipIf (support .is_emscripten , "getgid() is a stub" )
792805 def test_fchown (self ):
793806 os .unlink (os_helper .TESTFN )
794807
@@ -801,6 +814,7 @@ def test_fchown(self):
801814 finally :
802815 test_file .close ()
803816
817+ @os_helper .skip_unless_working_chmod
804818 @unittest .skipUnless (hasattr (posix , 'lchown' ), "test needs os.lchown()" )
805819 def test_lchown (self ):
806820 os .unlink (os_helper .TESTFN )
@@ -963,8 +977,8 @@ def test_lchflags_symlink(self):
963977
964978 self .assertTrue (hasattr (testfn_st , 'st_flags' ))
965979
980+ self .addCleanup (os_helper .unlink , _DUMMY_SYMLINK )
966981 os .symlink (os_helper .TESTFN , _DUMMY_SYMLINK )
967- self .teardown_files .append (_DUMMY_SYMLINK )
968982 dummy_symlink_st = os .lstat (_DUMMY_SYMLINK )
969983
970984 def chflags_nofollow (path , flags ):
@@ -1060,6 +1074,7 @@ def test_getgrouplist(self):
10601074
10611075 @unittest .skipUnless (hasattr (os , 'getegid' ), "test needs os.getegid()" )
10621076 @unittest .skipUnless (hasattr (os , 'popen' ), "test needs os.popen()" )
1077+ @support .requires_subprocess ()
10631078 def test_getgroups (self ):
10641079 with os .popen ('id -G 2>/dev/null' ) as idg :
10651080 groups = idg .read ().strip ()
@@ -1207,6 +1222,7 @@ def test_sched_setaffinity(self):
12071222 # bpo-47205: does not raise OSError on FreeBSD
12081223 self .assertRaises (OSError , posix .sched_setaffinity , - 1 , mask )
12091224
1225+ @unittest .skipIf (support .is_wasi , "No dynamic linking on WASI" )
12101226 def test_rtld_constants (self ):
12111227 # check presence of major RTLD_* constants
12121228 posix .RTLD_LAZY
@@ -1346,6 +1362,7 @@ def test_chmod_dir_fd(self):
13461362
13471363 @unittest .skipUnless (hasattr (os , 'chown' ) and (os .chown in os .supports_dir_fd ),
13481364 "test needs dir_fd support in os.chown()" )
1365+ @unittest .skipIf (support .is_emscripten , "getgid() is a stub" )
13491366 def test_chown_dir_fd (self ):
13501367 with self .prepare_file () as (dir_fd , name , fullname ):
13511368 posix .chown (name , os .getuid (), os .getgid (), dir_fd = dir_fd )
@@ -1401,7 +1418,14 @@ def test_utime_dir_fd(self):
14011418 # whoops! using both together not supported on this platform.
14021419 pass
14031420
1404- @unittest .skipUnless (os .link in os .supports_dir_fd , "test needs dir_fd support in os.link()" )
1421+ @unittest .skipIf (
1422+ support .is_wasi ,
1423+ "WASI: symlink following on path_link is not supported"
1424+ )
1425+ @unittest .skipUnless (
1426+ hasattr (os , "link" ) and os .link in os .supports_dir_fd ,
1427+ "test needs dir_fd support in os.link()"
1428+ )
14051429 def test_link_dir_fd (self ):
14061430 with self .prepare_file () as (dir_fd , name , fullname ), \
14071431 self .prepare () as (dir_fd2 , linkname , fulllinkname ):
@@ -1490,7 +1514,7 @@ def test_unlink_dir_fd(self):
14901514 raise
14911515
14921516 @unittest .skip ("TODO: RUSTPYTHON; no os.mkfifo" )
1493- # @unittest.skipUnless(os.mkfifo in os.supports_dir_fd, "test needs dir_fd support in os.mkfifo()")
1517+ # @unittest.skipUnless(hasattr(os, 'mkfifo') and os.mkfifo in os.supports_dir_fd, "test needs dir_fd support in os.mkfifo()")
14941518 def test_mkfifo_dir_fd (self ):
14951519 with self .prepare () as (dir_fd , name , fullname ):
14961520 try :
@@ -2089,6 +2113,28 @@ def test_mkdir(self):
20892113 with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
20902114 os .mkdir ("dir" , dir_fd = 0 )
20912115
2116+ def test_mkfifo (self ):
2117+ self ._verify_available ("HAVE_MKFIFOAT" )
2118+ if self .mac_ver >= (13 , 0 ):
2119+ self .assertIn ("HAVE_MKFIFOAT" , posix ._have_functions )
2120+
2121+ else :
2122+ self .assertNotIn ("HAVE_MKFIFOAT" , posix ._have_functions )
2123+
2124+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2125+ os .mkfifo ("path" , dir_fd = 0 )
2126+
2127+ def test_mknod (self ):
2128+ self ._verify_available ("HAVE_MKNODAT" )
2129+ if self .mac_ver >= (13 , 0 ):
2130+ self .assertIn ("HAVE_MKNODAT" , posix ._have_functions )
2131+
2132+ else :
2133+ self .assertNotIn ("HAVE_MKNODAT" , posix ._have_functions )
2134+
2135+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2136+ os .mknod ("path" , dir_fd = 0 )
2137+
20922138 def test_rename_replace (self ):
20932139 self ._verify_available ("HAVE_RENAMEAT" )
20942140 if self .mac_ver >= (10 , 10 ):
0 commit comments