Skip to content

Commit

Permalink
pythongh-127001: Fix conflict with extensionless files in shutil.whic…
Browse files Browse the repository at this point in the history
…h() 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
1 parent 88dc84b commit b2ec0f6
Show file tree
Hide file tree
Showing 2 changed files with 7 additions and 7 deletions.
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

0 comments on commit b2ec0f6

Please sign in to comment.