@@ -2067,6 +2067,14 @@ def setUp(self):
2067
2067
self .curdir = os .curdir
2068
2068
self .ext = ".EXE"
2069
2069
2070
+ def to_text_type (self , s ):
2071
+ '''
2072
+ In this class we're testing with str, so convert s to a str
2073
+ '''
2074
+ if isinstance (s , bytes ):
2075
+ return s .decode ()
2076
+ return s
2077
+
2070
2078
def test_basic (self ):
2071
2079
# Given an EXE in a directory, it should be returned.
2072
2080
rv = shutil .which (self .file , path = self .dir )
@@ -2254,9 +2262,9 @@ def test_empty_path_no_PATH(self):
2254
2262
2255
2263
@unittest .skipUnless (sys .platform == "win32" , 'test specific to Windows' )
2256
2264
def test_pathext (self ):
2257
- ext = ".xyz"
2265
+ ext = self . to_text_type ( ".xyz" )
2258
2266
temp_filexyz = tempfile .NamedTemporaryFile (dir = self .temp_dir ,
2259
- prefix = "Tmp2" , suffix = ext )
2267
+ prefix = self . to_text_type ( "Tmp2" ) , suffix = ext )
2260
2268
os .chmod (temp_filexyz .name , stat .S_IXUSR )
2261
2269
self .addCleanup (temp_filexyz .close )
2262
2270
@@ -2265,38 +2273,39 @@ def test_pathext(self):
2265
2273
program = os .path .splitext (program )[0 ]
2266
2274
2267
2275
with os_helper .EnvironmentVarGuard () as env :
2268
- env ['PATHEXT' ] = ext
2276
+ env ['PATHEXT' ] = ext if isinstance ( ext , str ) else ext . decode ()
2269
2277
rv = shutil .which (program , path = self .temp_dir )
2270
2278
self .assertEqual (rv , temp_filexyz .name )
2271
2279
2272
2280
# Issue 40592: See https://bugs.python.org/issue40592
2273
2281
@unittest .skipUnless (sys .platform == "win32" , 'test specific to Windows' )
2274
2282
def test_pathext_with_empty_str (self ):
2275
- ext = ".xyz"
2283
+ ext = self . to_text_type ( ".xyz" )
2276
2284
temp_filexyz = tempfile .NamedTemporaryFile (dir = self .temp_dir ,
2277
- prefix = "Tmp2" , suffix = ext )
2285
+ prefix = self . to_text_type ( "Tmp2" ) , suffix = ext )
2278
2286
self .addCleanup (temp_filexyz .close )
2279
2287
2280
2288
# strip path and extension
2281
2289
program = os .path .basename (temp_filexyz .name )
2282
2290
program = os .path .splitext (program )[0 ]
2283
2291
2284
2292
with os_helper .EnvironmentVarGuard () as env :
2285
- env ['PATHEXT' ] = f"{ ext } ;" # note the ;
2293
+ env ['PATHEXT' ] = f"{ ext if isinstance ( ext , str ) else ext . decode () } ;" # note the ;
2286
2294
rv = shutil .which (program , path = self .temp_dir )
2287
2295
self .assertEqual (rv , temp_filexyz .name )
2288
2296
2289
2297
# See GH-75586
2290
2298
@unittest .skipUnless (sys .platform == "win32" , 'test specific to Windows' )
2291
2299
def test_pathext_applied_on_files_in_path (self ):
2292
2300
with os_helper .EnvironmentVarGuard () as env :
2293
- env ["PATH" ] = self .temp_dir
2301
+ env ["PATH" ] = self .temp_dir if isinstance ( self . temp_dir , str ) else self . temp_dir . decode ()
2294
2302
env ["PATHEXT" ] = ".test"
2295
2303
2296
- test_path = pathlib .Path (self .temp_dir ) / "test_program.test"
2297
- test_path .touch (mode = 0o755 )
2304
+ test_path = os .path .join (self .temp_dir , self .to_text_type ("test_program.test" ))
2305
+ open (test_path , 'w' ).close ()
2306
+ os .chmod (test_path , 0o755 )
2298
2307
2299
- self .assertEqual (shutil .which ("test_program" ), str ( test_path ) )
2308
+ self .assertEqual (shutil .which (self . to_text_type ( "test_program" )), test_path )
2300
2309
2301
2310
# See GH-75586
2302
2311
@unittest .skipUnless (sys .platform == "win32" , 'test specific to Windows' )
@@ -2312,16 +2321,69 @@ def test_win_path_needs_curdir(self):
2312
2321
self .assertFalse (shutil ._win_path_needs_curdir ('dontcare' , os .X_OK ))
2313
2322
need_curdir_mock .assert_called_once_with ('dontcare' )
2314
2323
2324
+ # See GH-109590
2325
+ @unittest .skipUnless (sys .platform == "win32" , 'test specific to Windows' )
2326
+ def test_pathext_preferred_for_execute (self ):
2327
+ with os_helper .EnvironmentVarGuard () as env :
2328
+ env ["PATH" ] = self .temp_dir if isinstance (self .temp_dir , str ) else self .temp_dir .decode ()
2329
+ env ["PATHEXT" ] = ".test"
2330
+
2331
+ exe = os .path .join (self .temp_dir , self .to_text_type ("test.exe" ))
2332
+ open (exe , 'w' ).close ()
2333
+ os .chmod (exe , 0o755 )
2334
+
2335
+ # default behavior allows a direct match if nothing in PATHEXT matches
2336
+ self .assertEqual (shutil .which (self .to_text_type ("test.exe" )), exe )
2337
+
2338
+ dot_test = os .path .join (self .temp_dir , self .to_text_type ("test.exe.test" ))
2339
+ open (dot_test , 'w' ).close ()
2340
+ os .chmod (dot_test , 0o755 )
2341
+
2342
+ # now we have a PATHEXT match, so it take precedence
2343
+ self .assertEqual (shutil .which (self .to_text_type ("test.exe" )), dot_test )
2344
+
2345
+ # but if we don't use os.X_OK we don't change the order based off PATHEXT
2346
+ # and therefore get the direct match.
2347
+ self .assertEqual (shutil .which (self .to_text_type ("test.exe" ), mode = os .F_OK ), exe )
2348
+
2349
+ # See GH-109590
2350
+ @unittest .skipUnless (sys .platform == "win32" , 'test specific to Windows' )
2351
+ def test_pathext_given_extension_preferred (self ):
2352
+ with os_helper .EnvironmentVarGuard () as env :
2353
+ env ["PATH" ] = self .temp_dir if isinstance (self .temp_dir , str ) else self .temp_dir .decode ()
2354
+ env ["PATHEXT" ] = ".exe2;.exe"
2355
+
2356
+ exe = os .path .join (self .temp_dir , self .to_text_type ("test.exe" ))
2357
+ open (exe , 'w' ).close ()
2358
+ os .chmod (exe , 0o755 )
2359
+
2360
+ exe2 = os .path .join (self .temp_dir , self .to_text_type ("test.exe2" ))
2361
+ open (exe2 , 'w' ).close ()
2362
+ os .chmod (exe2 , 0o755 )
2363
+
2364
+ # even though .exe2 is preferred in PATHEXT, we matched directly to test.exe
2365
+ self .assertEqual (shutil .which (self .to_text_type ("test.exe" )), exe )
2366
+ self .assertEqual (shutil .which (self .to_text_type ("test" )), exe2 )
2367
+
2315
2368
2316
2369
class TestWhichBytes (TestWhich ):
2317
2370
def setUp (self ):
2318
2371
TestWhich .setUp (self )
2319
2372
self .dir = os .fsencode (self .dir )
2320
2373
self .file = os .fsencode (self .file )
2321
2374
self .temp_file .name = os .fsencode (self .temp_file .name )
2375
+ self .temp_dir = os .fsencode (self .temp_dir )
2322
2376
self .curdir = os .fsencode (self .curdir )
2323
2377
self .ext = os .fsencode (self .ext )
2324
2378
2379
+ def to_text_type (self , s ):
2380
+ '''
2381
+ In this class we're testing with bytes, so convert s to a bytes
2382
+ '''
2383
+ if isinstance (s , str ):
2384
+ return s .encode ()
2385
+ return s
2386
+
2325
2387
2326
2388
class TestMove (BaseTest , unittest .TestCase ):
2327
2389
0 commit comments