Issue #16993: shutil.which() now preserves the case of the path and extension
on Windows.
This commit is contained in:
commit
5b987c2e68
|
@ -348,7 +348,7 @@ Directory and files operations
|
||||||
directories. For example, on Windows::
|
directories. For example, on Windows::
|
||||||
|
|
||||||
>>> shutil.which("python")
|
>>> shutil.which("python")
|
||||||
'c:\\python33\\python.exe'
|
'C:\\Python33\\python.exe'
|
||||||
|
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
|
@ -1092,10 +1092,12 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
|
||||||
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
|
pathext = os.environ.get("PATHEXT", "").split(os.pathsep)
|
||||||
# See if the given file matches any of the expected path extensions.
|
# See if the given file matches any of the expected path extensions.
|
||||||
# This will allow us to short circuit when given "python.exe".
|
# This will allow us to short circuit when given "python.exe".
|
||||||
matches = [cmd for ext in pathext if cmd.lower().endswith(ext.lower())]
|
|
||||||
# If it does match, only test that one, otherwise we have to try
|
# If it does match, only test that one, otherwise we have to try
|
||||||
# others.
|
# others.
|
||||||
files = [cmd] if matches else [cmd + ext.lower() for ext in pathext]
|
if any(cmd.lower().endswith(ext.lower()) for ext in pathext):
|
||||||
|
files = [cmd]
|
||||||
|
else:
|
||||||
|
files = [cmd + ext for ext in pathext]
|
||||||
else:
|
else:
|
||||||
# On other platforms you don't have things like PATHEXT to tell you
|
# 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.
|
# what file suffixes are executable, so just pass on cmd as-is.
|
||||||
|
@ -1103,9 +1105,9 @@ def which(cmd, mode=os.F_OK | os.X_OK, path=None):
|
||||||
|
|
||||||
seen = set()
|
seen = set()
|
||||||
for dir in path:
|
for dir in path:
|
||||||
dir = os.path.normcase(dir)
|
normdir = os.path.normcase(dir)
|
||||||
if not dir in seen:
|
if not normdir in seen:
|
||||||
seen.add(dir)
|
seen.add(normdir)
|
||||||
for thefile in files:
|
for thefile in files:
|
||||||
name = os.path.join(dir, thefile)
|
name = os.path.join(dir, thefile)
|
||||||
if _access_check(name, mode):
|
if _access_check(name, mode):
|
||||||
|
|
|
@ -1280,12 +1280,13 @@ class TestShutil(unittest.TestCase):
|
||||||
class TestWhich(unittest.TestCase):
|
class TestWhich(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.temp_dir = tempfile.mkdtemp()
|
self.temp_dir = tempfile.mkdtemp(prefix="Tmp")
|
||||||
self.addCleanup(shutil.rmtree, self.temp_dir, True)
|
self.addCleanup(shutil.rmtree, self.temp_dir, True)
|
||||||
# Give the temp_file an ".exe" suffix for all.
|
# Give the temp_file an ".exe" suffix for all.
|
||||||
# It's needed on Windows and not harmful on other platforms.
|
# It's needed on Windows and not harmful on other platforms.
|
||||||
self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
|
self.temp_file = tempfile.NamedTemporaryFile(dir=self.temp_dir,
|
||||||
suffix=".exe")
|
prefix="Tmp",
|
||||||
|
suffix=".Exe")
|
||||||
os.chmod(self.temp_file.name, stat.S_IXUSR)
|
os.chmod(self.temp_file.name, stat.S_IXUSR)
|
||||||
self.addCleanup(self.temp_file.close)
|
self.addCleanup(self.temp_file.close)
|
||||||
self.dir, self.file = os.path.split(self.temp_file.name)
|
self.dir, self.file = os.path.split(self.temp_file.name)
|
||||||
|
@ -1328,7 +1329,7 @@ class TestWhich(unittest.TestCase):
|
||||||
# Ask for the file without the ".exe" extension, then ensure that
|
# Ask for the file without the ".exe" extension, then ensure that
|
||||||
# it gets found properly with the extension.
|
# it gets found properly with the extension.
|
||||||
rv = shutil.which(self.temp_file.name[:-4], path=self.dir)
|
rv = shutil.which(self.temp_file.name[:-4], path=self.dir)
|
||||||
self.assertEqual(self.temp_file.name, rv)
|
self.assertEqual(rv, self.temp_file.name[:-4] + ".exe")
|
||||||
|
|
||||||
|
|
||||||
class TestMove(unittest.TestCase):
|
class TestMove(unittest.TestCase):
|
||||||
|
|
|
@ -220,6 +220,9 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #16993: shutil.which() now preserves the case of the path and extension
|
||||||
|
on Windows.
|
||||||
|
|
||||||
- Issue #16992: On Windows in signal.set_wakeup_fd, validate the file
|
- Issue #16992: On Windows in signal.set_wakeup_fd, validate the file
|
||||||
descriptor argument.
|
descriptor argument.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue