From cdb6b17b834ba2919ff559301154ac5b2f931752 Mon Sep 17 00:00:00 2001 From: Tzu-ping Chung Date: Sun, 3 Jun 2018 15:42:17 +0800 Subject: [PATCH] Respect PATHEXT when looking for Win executable ... as idiomatic in Windows programming. --- pipenv/core.py | 6 ++--- pipenv/utils.py | 22 +++++++-------- tests/unit/test_utils_windows_executable.py | 30 +++++++++++++++++++++ 3 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 tests/unit/test_utils_windows_executable.py diff --git a/pipenv/core.py b/pipenv/core.py index 1c28b10227..47650f3e80 100644 --- a/pipenv/core.py +++ b/pipenv/core.py @@ -126,10 +126,10 @@ def which(command, location=None, allow_global=False): if not allow_global: if os.name == 'nt': p = find_windows_executable( - os.path.join(location, 'Scripts'), command + os.path.join(location, 'Scripts'), command, ) else: - p = os.sep.join([location] + ['bin/{0}'.format(command)]) + p = os.path.join(location, 'bin', command) else: if command == 'python': p = sys.executable @@ -1508,7 +1508,7 @@ def pip_install( def pip_download(package_name): - cache_dir = Path(PIPENV_CACHE_DIR) + cache_dir = Path(PIPENV_CACHE_DIR) pip_config = { 'PIP_CACHE_DIR': fs_str(cache_dir.as_posix()), 'PIP_WHEEL_DIR': fs_str(cache_dir.joinpath('wheels').as_posix()), diff --git a/pipenv/utils.py b/pipenv/utils.py index 27ce9d301d..7aff3d3d09 100644 --- a/pipenv/utils.py +++ b/pipenv/utils.py @@ -835,20 +835,18 @@ def get_windows_path(*args): def find_windows_executable(bin_path, exe_name): """Given an executable name, search the given location for an executable""" requested_path = get_windows_path(bin_path, exe_name) - if os.path.exists(requested_path): + if os.path.isfile(requested_path): return requested_path - # Ensure we aren't adding two layers of file extensions - exe_name = os.path.splitext(exe_name)[0] - files = [ - '{0}.{1}'.format(exe_name, ext) for ext in ['', 'py', 'exe', 'bat'] - ] - exec_paths = [get_windows_path(bin_path, f) for f in files] - exec_files = [ - filename for filename in exec_paths if os.path.isfile(filename) - ] - if exec_files: - return exec_files[0] + try: + pathext = os.environ['PATHEXT'] + except KeyError: + pass + else: + for ext in pathext.split(os.pathsep): + path = get_windows_path(bin_path, exe_name + ext.strip().lower()) + if os.path.isfile(path): + return path return find_executable(exe_name) diff --git a/tests/unit/test_utils_windows_executable.py b/tests/unit/test_utils_windows_executable.py new file mode 100644 index 0000000000..e6c4439c1b --- /dev/null +++ b/tests/unit/test_utils_windows_executable.py @@ -0,0 +1,30 @@ +import os + +import mock +import pytest + +from pipenv import utils + + +# This module is run only on Windows. +pytestmark = pytest.mark.skipif( + os.name != 'nt', + reason="only relevant on windows", +) + + +@mock.patch('os.path.isfile') +@mock.patch('pipenv.utils.find_executable') +def test_find_windows_executable(mocked_find_executable, mocked_isfile): + mocked_isfile.return_value = False + mocked_find_executable.return_value = None + found = utils.find_windows_executable('fake/path', 'python') + assert found is None + + assert mocked_isfile.call_count > 1 + + calls = [mock.call('fake/path/python')] + [ + mock.call('fake/path/python{0}'.format(ext.lower())) + for ext in os.environ['PATHEXT'].split(';') + ] + assert mocked_isfile.mock_calls == calls