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