From ae3d790e123a9753076b07ad73c9f482e41884a2 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 28 Aug 2018 10:16:08 +0200 Subject: [PATCH] bpo-34530: Fix distutils find_executable() distutils.spawn.find_executable() has been reimplemented using shutil.which() to fix two issues: fallback on os.defpath if the PATH environment variable is not set, and respect the PATHEXT environment variable on Windows. --- Lib/distutils/spawn.py | 22 +++--------------- Lib/distutils/tests/test_spawn.py | 23 ++++++++++++++++--- .../2018-08-28-14-48-44.bpo-34530.ZZjx45.rst | 4 ++++ 3 files changed, 27 insertions(+), 22 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-08-28-14-48-44.bpo-34530.ZZjx45.rst diff --git a/Lib/distutils/spawn.py b/Lib/distutils/spawn.py index 5dd415a283d1de5..eefdcfa81885e77 100644 --- a/Lib/distutils/spawn.py +++ b/Lib/distutils/spawn.py @@ -6,8 +6,9 @@ executable name. """ -import sys import os +import shutil +import sys from distutils.errors import DistutilsPlatformError, DistutilsExecError from distutils.debug import DEBUG @@ -172,21 +173,4 @@ def find_executable(executable, path=None): A string listing directories separated by 'os.pathsep'; defaults to os.environ['PATH']. Returns the complete filename or None if not found. """ - if path is None: - path = os.environ['PATH'] - - paths = path.split(os.pathsep) - base, ext = os.path.splitext(executable) - - if (sys.platform == 'win32') and (ext != '.exe'): - executable = executable + '.exe' - - if not os.path.isfile(executable): - for p in paths: - f = os.path.join(p, executable) - if os.path.isfile(f): - # the file exists, we have a shot at spawn working - return f - return None - else: - return executable + return shutil.which(executable, path=path) diff --git a/Lib/distutils/tests/test_spawn.py b/Lib/distutils/tests/test_spawn.py index 5edc24a3a10491c..e47f58c10feefc4 100644 --- a/Lib/distutils/tests/test_spawn.py +++ b/Lib/distutils/tests/test_spawn.py @@ -1,9 +1,11 @@ """Tests for distutils.spawn.""" -import unittest -import sys import os -from test.support import run_unittest, unix_shell +import stat +import sys +import unittest +from test.support import run_unittest, unix_shell, temp_dir +from distutils.spawn import find_executable from distutils.spawn import _nt_quote_args from distutils.spawn import spawn from distutils.errors import DistutilsExecError @@ -51,6 +53,21 @@ def test_spawn(self): os.chmod(exe, 0o777) spawn([exe]) # should work without any error + def test_find_executable(self): + with temp_dir() as tmp_dir: + # Give the temporary program an ".exe" suffix for all. + # It's needed on Windows and not harmful on other platforms. + program = "testprogram" + ".exe" + + filename = os.path.join(tmp_dir, program) + with open(filename, "wb"): + pass + os.chmod(filename, stat.S_IXUSR) + + rv = find_executable(program, path=tmp_dir) + self.assertEqual(rv, filename) + + def test_suite(): return unittest.makeSuite(SpawnTestCase) diff --git a/Misc/NEWS.d/next/Library/2018-08-28-14-48-44.bpo-34530.ZZjx45.rst b/Misc/NEWS.d/next/Library/2018-08-28-14-48-44.bpo-34530.ZZjx45.rst new file mode 100644 index 000000000000000..436a8d03edb5612 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-08-28-14-48-44.bpo-34530.ZZjx45.rst @@ -0,0 +1,4 @@ +distutils.spawn.find_executable() has been reimplemented using +shutil.which() to fix two issues: fallback on os.defpath if the PATH +environment variable is not set, and respect the PATHEXT environment +variable on Windows.