Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
gh-127001: Fix conflict with extensionless files in shutil.which() on…
… Windows

Name without a PATHEXT extension is only searched if the mode does not
include X_OK.
  • Loading branch information
serhiy-storchaka committed Nov 19, 2024
commit b2ec0f6e87bcaf403542ef561434b03c865117a2
10 changes: 5 additions & 5 deletions Lib/shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -1555,16 +1555,16 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
if use_bytes:
pathext = [os.fsencode(ext) for ext in pathext]

files = ([cmd] + [cmd + ext for ext in pathext])
files = [cmd + ext for ext in pathext]

# gh-109590. If we are looking for an executable, we need to look
# for a PATHEXT match. The first cmd is the direct match
# (e.g. python.exe instead of python)
# Check that direct match first if and only if the extension is in PATHEXT
# Otherwise check it last
suffix = os.path.splitext(files[0])[1].upper()
if mode & os.X_OK and not any(suffix == ext.upper() for ext in pathext):
files.append(files.pop(0))
normcmd = cmd.upper()
if not (mode & os.X_OK) or any(normcmd.endswith(ext.upper()) for ext in pathext):
files.insert(0, cmd)
else:
# On other platforms you don't have things like PATHEXT to tell you
# what file suffixes are executable, so just pass on cmd as-is.
Expand All @@ -1573,7 +1573,7 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
seen = set()
for dir in path:
normdir = os.path.normcase(dir)
if not normdir in seen:
if normdir not in seen:
seen.add(normdir)
for thefile in files:
name = os.path.join(dir, thefile)
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_shutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -2592,8 +2592,8 @@ def test_pathext_preferred_for_execute(self):
open(exe, 'w').close()
os.chmod(exe, 0o755)

# default behavior allows a direct match if nothing in PATHEXT matches
self.assertEqual(shutil.which(self.to_text_type("test.exe")), exe)
self.assertIsNone(shutil.which(self.to_text_type("test.exe")))
self.assertEqual(shutil.which(self.to_text_type("test.exe"), mode=os.F_OK), exe)

dot_test = os.path.join(self.temp_dir, self.to_text_type("test.exe.test"))
open(dot_test, 'w').close()
Expand Down