From c7e46bf425c1073e02b0c5f6419478a2316bede1 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sun, 1 Oct 2017 13:25:41 -0500 Subject: [PATCH 1/6] Add a failing test for shell completion --- tests/functional/test_completion.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index f6b34b211ad..166fc5f8c59 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -1,4 +1,7 @@ import os +import sys + +import pytest def test_completion_for_bash(script): @@ -114,3 +117,13 @@ def test_completion_option_for_command(script): res, env = setup_completion(script, 'pip search --', '2') assert '--help' in res.stdout,\ "autocomplete function could not complete ``--``" + + +@pytest.mark.parametrize('flag', ['--bash', '--zsh', '--fish']) +def test_completion_uses_same_executable_name(script, flag): + expect_stderr = sys.version_info[:2] == (3, 3) + executable_name = 'pip{}'.format(sys.version_info[0]) + result = script.run( + executable_name, 'completion', flag, expect_stderr=expect_stderr + ) + assert executable_name in result.stdout From d019590dccac9b82e0f47e3532f7173122bc5227 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sun, 1 Oct 2017 14:07:09 -0500 Subject: [PATCH 2/6] Add some tests for get_prog --- tests/unit/test_utils.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 9ab2e81c0b1..4f8aab9a7a9 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -23,8 +23,8 @@ from pip._internal.utils.glibc import check_glibc_version from pip._internal.utils.hashes import Hashes, MissingHashes from pip._internal.utils.misc import ( - egg_link_path, ensure_dir, get_installed_distributions, normalize_path, - rmtree, untar_file, unzip_file + egg_link_path, ensure_dir, get_installed_distributions, get_prog, + normalize_path, rmtree, untar_file, unzip_file ) from pip._internal.utils.packaging import check_dist_requires_python from pip._internal.utils.temp_dir import TempDirectory @@ -592,3 +592,22 @@ def test_check_requires(self, metadata, should_raise): check_dist_requires_python(fake_dist) else: check_dist_requires_python(fake_dist) + + +class TestGetProg(object): + + @pytest.mark.parametrize( + ("argv", "executable", "expected"), + [ + ('/usr/bin/pip', '', 'pip'), + ('-c', '/usr/bin/python', '/usr/bin/python -m pip'), + ('__main__.py', '/usr/bin/python', '/usr/bin/python -m pip'), + ] + ) + def test_get_prog(self, monkeypatch, argv, executable, expected): + monkeypatch.setattr('pip._internal.utils.misc.sys.argv', [argv]) + monkeypatch.setattr( + 'pip._internal.utils.misc.sys.executable', + executable + ) + assert get_prog() == expected From 45e5458078c75290450442cf48b5ba4de9b45ac1 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sun, 1 Oct 2017 14:07:44 -0500 Subject: [PATCH 3/6] Add failing test for get_prog --- tests/unit/test_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unit/test_utils.py b/tests/unit/test_utils.py index 4f8aab9a7a9..acfa6193ae3 100644 --- a/tests/unit/test_utils.py +++ b/tests/unit/test_utils.py @@ -602,6 +602,7 @@ class TestGetProg(object): ('/usr/bin/pip', '', 'pip'), ('-c', '/usr/bin/python', '/usr/bin/python -m pip'), ('__main__.py', '/usr/bin/python', '/usr/bin/python -m pip'), + ('/usr/bin/pip3', '', 'pip3'), ] ) def test_get_prog(self, monkeypatch, argv, executable, expected): From 511e6bbc304f3c55aa0c0352a39d02a0defc5c94 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sun, 1 Oct 2017 14:08:30 -0500 Subject: [PATCH 4/6] Make get_prog return the actual executable name used --- src/pip/_internal/utils/misc.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pip/_internal/utils/misc.py b/src/pip/_internal/utils/misc.py index afd033f88b1..09c7646bff9 100644 --- a/src/pip/_internal/utils/misc.py +++ b/src/pip/_internal/utils/misc.py @@ -91,8 +91,11 @@ def ensure_dir(path): def get_prog(): try: - if os.path.basename(sys.argv[0]) in ('__main__.py', '-c'): + prog = os.path.basename(sys.argv[0]) + if prog in ('__main__.py', '-c'): return "%s -m pip" % sys.executable + else: + return prog except (AttributeError, TypeError, IndexError): pass return 'pip' From 6de819b9d3fcee98edc75dbfc4e5c82fe362aa9d Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sun, 1 Oct 2017 14:09:18 -0500 Subject: [PATCH 5/6] Use get_prog in completion scripts for executable name --- src/pip/_internal/commands/completion.py | 17 +++++++++++------ tests/functional/test_completion.py | 4 +++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/pip/_internal/commands/completion.py b/src/pip/_internal/commands/completion.py index 40cd32e1dce..c4b387364a5 100644 --- a/src/pip/_internal/commands/completion.py +++ b/src/pip/_internal/commands/completion.py @@ -4,6 +4,7 @@ import textwrap from pip._internal.basecommand import Command +from pip._internal.utils.misc import get_prog BASE_COMPLETION = """ # pip %(shell)s completion start%(script)s# pip %(shell)s completion end @@ -17,7 +18,7 @@ COMP_CWORD=$COMP_CWORD \\ PIP_AUTO_COMPLETE=1 $1 ) ) } - complete -o default -F _pip_completion pip + complete -o default -F _pip_completion %(prog)s """, 'zsh': """ function _pip_completion { @@ -28,17 +29,19 @@ COMP_CWORD=$(( cword-1 )) \\ PIP_AUTO_COMPLETE=1 $words[1] ) ) } - compctl -K _pip_completion pip + compctl -K _pip_completion %(prog)s """, 'fish': """ function __fish_complete_pip set -lx COMP_WORDS (commandline -o) "" - set -lx COMP_CWORD {cword} + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) set -lx PIP_AUTO_COMPLETE 1 string split \\ -- (eval $COMP_WORDS[1]) end - complete -fa "(__fish_complete_pip)" -c pip - """.format(cword="(math (contains -i -- (commandline -t) $COMP_WORDS)-1)") + complete -fa "(__fish_complete_pip)" -c %(prog)s + """, } @@ -80,7 +83,9 @@ def run(self, options, args): shell_options = ['--' + shell for shell in sorted(shells)] if options.shell in shells: script = textwrap.dedent( - COMPLETION_SCRIPTS.get(options.shell, '') + COMPLETION_SCRIPTS.get(options.shell, '') % { + 'prog': get_prog(), + } ) print(BASE_COMPLETION % {'script': script, 'shell': options.shell}) else: diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py index 166fc5f8c59..c5483bf0301 100644 --- a/tests/functional/test_completion.py +++ b/tests/functional/test_completion.py @@ -47,7 +47,9 @@ def test_completion_for_fish(script): fish_completion = """\ function __fish_complete_pip set -lx COMP_WORDS (commandline -o) "" - set -lx COMP_CWORD (math (contains -i -- (commandline -t) $COMP_WORDS)-1) + set -lx COMP_CWORD ( \\ + math (contains -i -- (commandline -t) $COMP_WORDS)-1 \\ + ) set -lx PIP_AUTO_COMPLETE 1 string split \\ -- (eval $COMP_WORDS[1]) end From 4a94372abf14f65b1bb40c3335e407de49eb8379 Mon Sep 17 00:00:00 2001 From: Dustin Ingram Date: Sun, 1 Oct 2017 15:04:25 -0500 Subject: [PATCH 6/6] Add a newsfile --- news/3997.bugfix | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 news/3997.bugfix diff --git a/news/3997.bugfix b/news/3997.bugfix new file mode 100644 index 00000000000..78e9895f8c8 --- /dev/null +++ b/news/3997.bugfix @@ -0,0 +1,2 @@ +Shell completion scripts now use correct executable names (e.g., ``pip3`` +instead of ``pip``)