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