@@ -1925,13 +1925,241 @@ def test_posix_spawnp(self):
1925
1925
assert_python_ok (* args , PATH = path )
1926
1926
1927
1927
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
+
1928
2155
def test_main ():
1929
2156
try :
1930
2157
support .run_unittest (
1931
2158
PosixTester ,
1932
2159
PosixGroupsTester ,
1933
2160
TestPosixSpawn ,
1934
2161
TestPosixSpawnP ,
2162
+ TestPosixWeaklinking
1935
2163
)
1936
2164
finally :
1937
2165
support .reap_children ()
0 commit comments