@@ -1925,13 +1925,241 @@ def test_posix_spawnp(self):
19251925 assert_python_ok (* args , PATH = path )
19261926
19271927
1928+ @unittest .skipUnless (sys .platform == "darwin" , "test weak linking on macOS" )
1929+ class TestPosixWeaklinking (unittest .TestCase ):
1930+ # These test cases verify that weak linking support on macOS works
1931+ # as expected. These cases only test new behaviour introduced by weak linking,
1932+ # regular behaviour is tested by the normal test cases.
1933+ #
1934+ # See the section on Weak Linking in Mac/README.txt for more information.
1935+ def setUp (self ):
1936+ import sysconfig
1937+ import platform
1938+
1939+ config_vars = sysconfig .get_config_vars ()
1940+ self .available = { nm for nm in config_vars if nm .startswith ("HAVE_" ) and config_vars [nm ] }
1941+ self .mac_ver = tuple (int (part ) for part in platform .mac_ver ()[0 ].split ("." ))
1942+
1943+ def _verify_available (self , name ):
1944+ if name not in self .available :
1945+ raise unittest .SkipTest (f"{ name } not weak-linked" )
1946+
1947+ def test_pwritev (self ):
1948+ self ._verify_available ("HAVE_PWRITEV" )
1949+ if self .mac_ver >= (10 , 16 ):
1950+ self .assertTrue (hasattr (os , "pwritev" ), "os.pwritev is not available" )
1951+ self .assertTrue (hasattr (os , "preadv" ), "os.readv is not available" )
1952+
1953+ else :
1954+ self .assertFalse (hasattr (os , "pwritev" ), "os.pwritev is available" )
1955+ self .assertFalse (hasattr (os , "preadv" ), "os.readv is available" )
1956+
1957+ def test_stat (self ):
1958+ self ._verify_available ("HAVE_FSTATAT" )
1959+ if self .mac_ver >= (10 , 10 ):
1960+ self .assertIn ("HAVE_FSTATAT" , posix ._have_functions )
1961+
1962+ else :
1963+ self .assertNotIn ("HAVE_FSTATAT" , posix ._have_functions )
1964+
1965+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
1966+ os .stat ("file" , dir_fd = 0 )
1967+
1968+ def test_access (self ):
1969+ self ._verify_available ("HAVE_FACCESSAT" )
1970+ if self .mac_ver >= (10 , 10 ):
1971+ self .assertIn ("HAVE_FACCESSAT" , posix ._have_functions )
1972+
1973+ else :
1974+ self .assertNotIn ("HAVE_FACCESSAT" , posix ._have_functions )
1975+
1976+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
1977+ os .access ("file" , os .R_OK , dir_fd = 0 )
1978+
1979+ with self .assertRaisesRegex (NotImplementedError , "follow_symlinks unavailable" ):
1980+ os .access ("file" , os .R_OK , follow_symlinks = False )
1981+
1982+ with self .assertRaisesRegex (NotImplementedError , "effective_ids unavailable" ):
1983+ os .access ("file" , os .R_OK , effective_ids = True )
1984+
1985+ def test_chmod (self ):
1986+ self ._verify_available ("HAVE_FCHMODAT" )
1987+ if self .mac_ver >= (10 , 10 ):
1988+ self .assertIn ("HAVE_FCHMODAT" , posix ._have_functions )
1989+
1990+ else :
1991+ self .assertNotIn ("HAVE_FCHMODAT" , posix ._have_functions )
1992+ self .assertIn ("HAVE_LCHMOD" , posix ._have_functions )
1993+
1994+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
1995+ os .chmod ("file" , 0o644 , dir_fd = 0 )
1996+
1997+ def test_chown (self ):
1998+ self ._verify_available ("HAVE_FCHOWNAT" )
1999+ if self .mac_ver >= (10 , 10 ):
2000+ self .assertIn ("HAVE_FCHOWNAT" , posix ._have_functions )
2001+
2002+ else :
2003+ self .assertNotIn ("HAVE_FCHOWNAT" , posix ._have_functions )
2004+ self .assertIn ("HAVE_LCHOWN" , posix ._have_functions )
2005+
2006+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2007+ os .chown ("file" , 0 , 0 , dir_fd = 0 )
2008+
2009+ def test_link (self ):
2010+ self ._verify_available ("HAVE_LINKAT" )
2011+ if self .mac_ver >= (10 , 10 ):
2012+ self .assertIn ("HAVE_LINKAT" , posix ._have_functions )
2013+
2014+ else :
2015+ self .assertNotIn ("HAVE_LINKAT" , posix ._have_functions )
2016+
2017+ with self .assertRaisesRegex (NotImplementedError , "src_dir_fd unavailable" ):
2018+ os .link ("source" , "target" , src_dir_fd = 0 )
2019+
2020+ with self .assertRaisesRegex (NotImplementedError , "dst_dir_fd unavailable" ):
2021+ os .link ("source" , "target" , dst_dir_fd = 0 )
2022+
2023+ with self .assertRaisesRegex (NotImplementedError , "src_dir_fd unavailable" ):
2024+ os .link ("source" , "target" , src_dir_fd = 0 , dst_dir_fd = 0 )
2025+
2026+ # issue 41355: !HAVE_LINKAT code path ignores the follow_symlinks flag
2027+ with os_helper .temp_dir () as base_path :
2028+ link_path = os .path .join (base_path , "link" )
2029+ target_path = os .path .join (base_path , "target" )
2030+ source_path = os .path .join (base_path , "source" )
2031+
2032+ with open (source_path , "w" ) as fp :
2033+ fp .write ("data" )
2034+
2035+ os .symlink ("target" , link_path )
2036+
2037+ # Calling os.link should fail in the link(2) call, and
2038+ # should not reject *follow_symlinks* (to match the
2039+ # behaviour you'd get when building on a platform without
2040+ # linkat)
2041+ with self .assertRaises (FileExistsError ):
2042+ os .link (source_path , link_path , follow_symlinks = True )
2043+
2044+ with self .assertRaises (FileExistsError ):
2045+ os .link (source_path , link_path , follow_symlinks = False )
2046+
2047+
2048+ def test_listdir_scandir (self ):
2049+ self ._verify_available ("HAVE_FDOPENDIR" )
2050+ if self .mac_ver >= (10 , 10 ):
2051+ self .assertIn ("HAVE_FDOPENDIR" , posix ._have_functions )
2052+
2053+ else :
2054+ self .assertNotIn ("HAVE_FDOPENDIR" , posix ._have_functions )
2055+
2056+ with self .assertRaisesRegex (TypeError , "listdir: path should be string, bytes, os.PathLike or None, not int" ):
2057+ os .listdir (0 )
2058+
2059+ with self .assertRaisesRegex (TypeError , "scandir: path should be string, bytes, os.PathLike or None, not int" ):
2060+ os .scandir (0 )
2061+
2062+ def test_mkdir (self ):
2063+ self ._verify_available ("HAVE_MKDIRAT" )
2064+ if self .mac_ver >= (10 , 10 ):
2065+ self .assertIn ("HAVE_MKDIRAT" , posix ._have_functions )
2066+
2067+ else :
2068+ self .assertNotIn ("HAVE_MKDIRAT" , posix ._have_functions )
2069+
2070+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2071+ os .mkdir ("dir" , dir_fd = 0 )
2072+
2073+ def test_rename_replace (self ):
2074+ self ._verify_available ("HAVE_RENAMEAT" )
2075+ if self .mac_ver >= (10 , 10 ):
2076+ self .assertIn ("HAVE_RENAMEAT" , posix ._have_functions )
2077+
2078+ else :
2079+ self .assertNotIn ("HAVE_RENAMEAT" , posix ._have_functions )
2080+
2081+ with self .assertRaisesRegex (NotImplementedError , "src_dir_fd and dst_dir_fd unavailable" ):
2082+ os .rename ("a" , "b" , src_dir_fd = 0 )
2083+
2084+ with self .assertRaisesRegex (NotImplementedError , "src_dir_fd and dst_dir_fd unavailable" ):
2085+ os .rename ("a" , "b" , dst_dir_fd = 0 )
2086+
2087+ with self .assertRaisesRegex (NotImplementedError , "src_dir_fd and dst_dir_fd unavailable" ):
2088+ os .replace ("a" , "b" , src_dir_fd = 0 )
2089+
2090+ with self .assertRaisesRegex (NotImplementedError , "src_dir_fd and dst_dir_fd unavailable" ):
2091+ os .replace ("a" , "b" , dst_dir_fd = 0 )
2092+
2093+ def test_unlink_rmdir (self ):
2094+ self ._verify_available ("HAVE_UNLINKAT" )
2095+ if self .mac_ver >= (10 , 10 ):
2096+ self .assertIn ("HAVE_UNLINKAT" , posix ._have_functions )
2097+
2098+ else :
2099+ self .assertNotIn ("HAVE_UNLINKAT" , posix ._have_functions )
2100+
2101+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2102+ os .unlink ("path" , dir_fd = 0 )
2103+
2104+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2105+ os .rmdir ("path" , dir_fd = 0 )
2106+
2107+ def test_open (self ):
2108+ self ._verify_available ("HAVE_OPENAT" )
2109+ if self .mac_ver >= (10 , 10 ):
2110+ self .assertIn ("HAVE_OPENAT" , posix ._have_functions )
2111+
2112+ else :
2113+ self .assertNotIn ("HAVE_OPENAT" , posix ._have_functions )
2114+
2115+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2116+ os .open ("path" , os .O_RDONLY , dir_fd = 0 )
2117+
2118+ def test_readlink (self ):
2119+ self ._verify_available ("HAVE_READLINKAT" )
2120+ if self .mac_ver >= (10 , 10 ):
2121+ self .assertIn ("HAVE_READLINKAT" , posix ._have_functions )
2122+
2123+ else :
2124+ self .assertNotIn ("HAVE_READLINKAT" , posix ._have_functions )
2125+
2126+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2127+ os .readlink ("path" , dir_fd = 0 )
2128+
2129+ def test_symlink (self ):
2130+ self ._verify_available ("HAVE_SYMLINKAT" )
2131+ if self .mac_ver >= (10 , 10 ):
2132+ self .assertIn ("HAVE_SYMLINKAT" , posix ._have_functions )
2133+
2134+ else :
2135+ self .assertNotIn ("HAVE_SYMLINKAT" , posix ._have_functions )
2136+
2137+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2138+ os .symlink ("a" , "b" , dir_fd = 0 )
2139+
2140+ def test_utime (self ):
2141+ self ._verify_available ("HAVE_FUTIMENS" )
2142+ self ._verify_available ("HAVE_UTIMENSAT" )
2143+ if self .mac_ver >= (10 , 13 ):
2144+ self .assertIn ("HAVE_FUTIMENS" , posix ._have_functions )
2145+ self .assertIn ("HAVE_UTIMENSAT" , posix ._have_functions )
2146+
2147+ else :
2148+ self .assertNotIn ("HAVE_FUTIMENS" , posix ._have_functions )
2149+ self .assertNotIn ("HAVE_UTIMENSAT" , posix ._have_functions )
2150+
2151+ with self .assertRaisesRegex (NotImplementedError , "dir_fd unavailable" ):
2152+ os .utime ("path" , dir_fd = 0 )
2153+
2154+
19282155def test_main ():
19292156 try :
19302157 support .run_unittest (
19312158 PosixTester ,
19322159 PosixGroupsTester ,
19332160 TestPosixSpawn ,
19342161 TestPosixSpawnP ,
2162+ TestPosixWeaklinking
19352163 )
19362164 finally :
19372165 support .reap_children ()
0 commit comments