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