summaryrefslogtreecommitdiff
path: root/tests/functional/test_completion.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/functional/test_completion.py')
-rw-r--r--tests/functional/test_completion.py373
1 files changed, 232 insertions, 141 deletions
diff --git a/tests/functional/test_completion.py b/tests/functional/test_completion.py
index 8a7464982..b02cd4fa3 100644
--- a/tests/functional/test_completion.py
+++ b/tests/functional/test_completion.py
@@ -1,20 +1,36 @@
import os
import sys
+from pathlib import Path
+from typing import TYPE_CHECKING, Tuple, Union
import pytest
-from tests.lib.path import Path
+from tests.conftest import ScriptFactory
+from tests.lib import PipTestEnvironment, TestData, TestPipResult
+
+if TYPE_CHECKING:
+ from typing import Protocol
+else:
+ # TODO: Protocol was introduced in Python 3.8. Remove this branch when
+ # dropping support for Python 3.7.
+ Protocol = object
+
COMPLETION_FOR_SUPPORTED_SHELLS_TESTS = (
- ('bash', """\
+ (
+ "bash",
+ """\
_pip_completion()
{
COMPREPLY=( $( COMP_WORDS="${COMP_WORDS[*]}" \\
COMP_CWORD=$COMP_CWORD \\
PIP_AUTO_COMPLETE=1 $1 2>/dev/null ) )
}
-complete -o default -F _pip_completion pip"""),
- ('fish', """\
+complete -o default -F _pip_completion pip""",
+ ),
+ (
+ "fish",
+ """\
function __fish_complete_pip
set -lx COMP_WORDS (commandline -o) ""
set -lx COMP_CWORD ( \\
@@ -23,8 +39,11 @@ function __fish_complete_pip
set -lx PIP_AUTO_COMPLETE 1
string split \\ -- (eval $COMP_WORDS[1])
end
-complete -fa "(__fish_complete_pip)" -c pip"""),
- ('zsh', """\
+complete -fa "(__fish_complete_pip)" -c pip""",
+ ),
+ (
+ "zsh",
+ """\
function _pip_completion {
local words cword
read -Ac words
@@ -33,56 +52,101 @@ function _pip_completion {
COMP_CWORD=$(( cword-1 )) \\
PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null ))
}
-compctl -K _pip_completion pip"""),
+compctl -K _pip_completion pip""",
+ ),
+ (
+ "powershell",
+ """\
+if ((Test-Path Function:\\TabExpansion) -and -not `
+ (Test-Path Function:\\_pip_completeBackup)) {
+ Rename-Item Function:\\TabExpansion _pip_completeBackup
+}
+function TabExpansion($line, $lastWord) {
+ $lastBlock = [regex]::Split($line, '[|;]')[-1].TrimStart()
+ if ($lastBlock.StartsWith("pip ")) {
+ $Env:COMP_WORDS=$lastBlock
+ $Env:COMP_CWORD=$lastBlock.Split().Length - 1
+ $Env:PIP_AUTO_COMPLETE=1
+ (& pip).Split()
+ Remove-Item Env:COMP_WORDS
+ Remove-Item Env:COMP_CWORD
+ Remove-Item Env:PIP_AUTO_COMPLETE
+ }
+ elseif (Test-Path Function:\\_pip_completeBackup) {
+ # Fall back on existing tab expansion
+ _pip_completeBackup $line $lastWord
+ }
+}""",
+ ),
)
@pytest.fixture(scope="session")
def script_with_launchers(
- tmpdir_factory, script_factory, common_wheels, pip_src
-):
- tmpdir = Path(str(tmpdir_factory.mktemp("script_with_launchers")))
+ tmpdir_factory: pytest.TempPathFactory,
+ script_factory: ScriptFactory,
+ common_wheels: Path,
+ pip_src: Path,
+) -> PipTestEnvironment:
+ tmpdir = tmpdir_factory.mktemp("script_with_launchers")
script = script_factory(tmpdir.joinpath("workspace"))
# Re-install pip so we get the launchers.
- script.pip_install_local('-f', common_wheels, pip_src)
+ script.pip_install_local("-f", common_wheels, pip_src)
return script
@pytest.mark.parametrize(
- 'shell, completion',
+ "shell, completion",
COMPLETION_FOR_SUPPORTED_SHELLS_TESTS,
ids=[t[0] for t in COMPLETION_FOR_SUPPORTED_SHELLS_TESTS],
)
def test_completion_for_supported_shells(
- script_with_launchers, shell, completion
-):
+ script_with_launchers: PipTestEnvironment, shell: str, completion: str
+) -> None:
"""
Test getting completion for bash shell
"""
- result = script_with_launchers.pip(
- 'completion', '--' + shell, use_module=False
- )
- assert completion in result.stdout, str(result.stdout)
+ result = script_with_launchers.pip("completion", "--" + shell, use_module=False)
+ actual = str(result.stdout)
+ if script_with_launchers.zipapp:
+ # The zipapp reports its name as "pip.pyz", but the expected
+ # output assumes "pip"
+ actual = actual.replace("pip.pyz", "pip")
+ assert completion in actual, actual
@pytest.fixture(scope="session")
-def autocomplete_script(tmpdir_factory, script_factory):
- tmpdir = Path(str(tmpdir_factory.mktemp("autocomplete_script")))
+def autocomplete_script(
+ tmpdir_factory: pytest.TempPathFactory, script_factory: ScriptFactory
+) -> PipTestEnvironment:
+ tmpdir = tmpdir_factory.mktemp("autocomplete_script")
return script_factory(tmpdir.joinpath("workspace"))
-@pytest.fixture
-def autocomplete(autocomplete_script, monkeypatch):
- monkeypatch.setattr(autocomplete_script, 'environ', os.environ.copy())
- autocomplete_script.environ['PIP_AUTO_COMPLETE'] = '1'
+class DoAutocomplete(Protocol):
+ def __call__(
+ self, words: str, cword: str, cwd: Union[Path, str, None] = None
+ ) -> Tuple[TestPipResult, PipTestEnvironment]:
+ ...
+
- def do_autocomplete(words, cword, cwd=None):
- autocomplete_script.environ['COMP_WORDS'] = words
- autocomplete_script.environ['COMP_CWORD'] = cword
+@pytest.fixture
+def autocomplete(
+ autocomplete_script: PipTestEnvironment, monkeypatch: pytest.MonkeyPatch
+) -> DoAutocomplete:
+ monkeypatch.setattr(autocomplete_script, "environ", os.environ.copy())
+ autocomplete_script.environ["PIP_AUTO_COMPLETE"] = "1"
+
+ def do_autocomplete(
+ words: str, cword: str, cwd: Union[Path, str, None] = None
+ ) -> Tuple[TestPipResult, PipTestEnvironment]:
+ autocomplete_script.environ["COMP_WORDS"] = words
+ autocomplete_script.environ["COMP_CWORD"] = cword
result = autocomplete_script.run(
- 'python', '-c',
- 'from pip._internal.cli.autocompletion import autocomplete;'
- 'autocomplete()',
+ "python",
+ "-c",
+ "from pip._internal.cli.autocompletion import autocomplete;"
+ "autocomplete()",
expect_error=True,
cwd=cwd,
)
@@ -92,225 +156,252 @@ def autocomplete(autocomplete_script, monkeypatch):
return do_autocomplete
-def test_completion_for_unknown_shell(autocomplete_script):
+def test_completion_for_unknown_shell(autocomplete_script: PipTestEnvironment) -> None:
"""
Test getting completion for an unknown shell
"""
- error_msg = 'no such option: --myfooshell'
- result = autocomplete_script.pip(
- 'completion', '--myfooshell', expect_error=True
- )
- assert error_msg in result.stderr, 'tests for an unknown shell failed'
+ error_msg = "no such option: --myfooshell"
+ result = autocomplete_script.pip("completion", "--myfooshell", expect_error=True)
+ assert error_msg in result.stderr, "tests for an unknown shell failed"
-def test_completion_alone(autocomplete_script):
+def test_completion_alone(autocomplete_script: PipTestEnvironment) -> None:
"""
Test getting completion for none shell, just pip completion
"""
- result = autocomplete_script.pip('completion', allow_stderr_error=True)
- assert 'ERROR: You must pass --bash or --fish or --zsh' in result.stderr, \
- 'completion alone failed -- ' + result.stderr
+ result = autocomplete_script.pip("completion", allow_stderr_error=True)
+ assert (
+ "ERROR: You must pass --bash or --fish or --powershell or --zsh"
+ in result.stderr
+ ), ("completion alone failed -- " + result.stderr)
-def test_completion_for_un_snippet(autocomplete):
+def test_completion_for_un_snippet(autocomplete: DoAutocomplete) -> None:
"""
Test getting completion for ``un`` should return uninstall
"""
- res, env = autocomplete('pip un', '1')
- assert res.stdout.strip().split() == ['uninstall'], res.stdout
+ res, env = autocomplete("pip un", "1")
+ assert res.stdout.strip().split() == ["uninstall"], res.stdout
-def test_completion_for_default_parameters(autocomplete):
+def test_completion_for_default_parameters(autocomplete: DoAutocomplete) -> None:
"""
Test getting completion for ``--`` should contain --help
"""
- res, env = autocomplete('pip --', '1')
- assert '--help' in res.stdout,\
- "autocomplete function could not complete ``--``"
+ res, env = autocomplete("pip --", "1")
+ assert "--help" in res.stdout, "autocomplete function could not complete ``--``"
-def test_completion_option_for_command(autocomplete):
+def test_completion_option_for_command(autocomplete: DoAutocomplete) -> None:
"""
Test getting completion for ``--`` in command (e.g. ``pip search --``)
"""
- res, env = autocomplete('pip search --', '2')
- assert '--help' in res.stdout,\
- "autocomplete function could not complete ``--``"
+ res, env = autocomplete("pip search --", "2")
+ assert "--help" in res.stdout, "autocomplete function could not complete ``--``"
-def test_completion_short_option(autocomplete):
+def test_completion_short_option(autocomplete: DoAutocomplete) -> None:
"""
Test getting completion for short options after ``-`` (eg. pip -)
"""
- res, env = autocomplete('pip -', '1')
+ res, env = autocomplete("pip -", "1")
- assert '-h' in res.stdout.split(),\
- "autocomplete function could not complete short options after ``-``"
+ assert (
+ "-h" in res.stdout.split()
+ ), "autocomplete function could not complete short options after ``-``"
-def test_completion_short_option_for_command(autocomplete):
+def test_completion_short_option_for_command(autocomplete: DoAutocomplete) -> None:
"""
Test getting completion for short options after ``-`` in command
(eg. pip search -)
"""
- res, env = autocomplete('pip search -', '2')
+ res, env = autocomplete("pip search -", "2")
- assert '-h' in res.stdout.split(),\
- "autocomplete function could not complete short options after ``-``"
+ assert (
+ "-h" in res.stdout.split()
+ ), "autocomplete function could not complete short options after ``-``"
-def test_completion_files_after_option(autocomplete, data):
+def test_completion_files_after_option(
+ autocomplete: DoAutocomplete, data: TestData
+) -> None:
"""
Test getting completion for <file> or <dir> after options in command
(e.g. ``pip install -r``)
"""
res, env = autocomplete(
- words=('pip install -r r'),
- cword='3',
+ words=("pip install -r r"),
+ cword="3",
cwd=data.completion_paths,
)
- assert 'requirements.txt' in res.stdout, (
- "autocomplete function could not complete <file> "
- "after options in command"
- )
- assert os.path.join('resources', '') in res.stdout, (
- "autocomplete function could not complete <dir> "
- "after options in command"
- )
- assert not any(out in res.stdout for out in
- (os.path.join('REPLAY', ''), 'README.txt')), (
+ assert (
+ "requirements.txt" in res.stdout
+ ), "autocomplete function could not complete <file> after options in command"
+ assert (
+ os.path.join("resources", "") in res.stdout
+ ), "autocomplete function could not complete <dir> after options in command"
+ assert not any(
+ out in res.stdout for out in (os.path.join("REPLAY", ""), "README.txt")
+ ), (
"autocomplete function completed <file> or <dir> that "
"should not be completed"
)
- if sys.platform != 'win32':
+ if sys.platform != "win32":
return
- assert 'readme.txt' in res.stdout, (
- "autocomplete function could not complete <file> "
- "after options in command"
- )
- assert os.path.join('replay', '') in res.stdout, (
- "autocomplete function could not complete <dir> "
- "after options in command"
- )
+ assert (
+ "readme.txt" in res.stdout
+ ), "autocomplete function could not complete <file> after options in command"
+ assert (
+ os.path.join("replay", "") in res.stdout
+ ), "autocomplete function could not complete <dir> after options in command"
-def test_completion_not_files_after_option(autocomplete, data):
+def test_completion_not_files_after_option(
+ autocomplete: DoAutocomplete, data: TestData
+) -> None:
"""
Test not getting completion files after options which not applicable
- (e.g. ``pip install``)
+ (e.g. ``pip wheel``)
"""
res, env = autocomplete(
- words=('pip install r'),
- cword='2',
+ words=("pip wheel r"),
+ cword="2",
cwd=data.completion_paths,
)
- assert not any(out in res.stdout for out in
- ('requirements.txt', 'readme.txt',)), (
- "autocomplete function completed <file> when "
- "it should not complete"
- )
- assert not any(os.path.join(out, '') in res.stdout
- for out in ('replay', 'resources')), (
- "autocomplete function completed <dir> when "
- "it should not complete"
+ assert not any(
+ out in res.stdout
+ for out in (
+ "requirements.txt",
+ "readme.txt",
+ )
+ ), "autocomplete function completed <file> when it should not complete"
+ assert not any(
+ os.path.join(out, "") in res.stdout for out in ("replay", "resources")
+ ), "autocomplete function completed <dir> when it should not complete"
+
+
+def test_pip_install_complete_files(
+ autocomplete: DoAutocomplete, data: TestData
+) -> None:
+ """``pip install`` autocompletes wheel and sdist files."""
+ res, env = autocomplete(
+ words=("pip install r"),
+ cword="2",
+ cwd=data.completion_paths,
)
+ assert all(
+ out in res.stdout
+ for out in (
+ "requirements.txt",
+ "resources",
+ )
+ ), "autocomplete function could not complete <path>"
@pytest.mark.parametrize("cl_opts", ["-U", "--user", "-h"])
def test_completion_not_files_after_nonexpecting_option(
- autocomplete, data, cl_opts
-):
+ autocomplete: DoAutocomplete, data: TestData, cl_opts: str
+) -> None:
"""
Test not getting completion files after options which not applicable
(e.g. ``pip install``)
"""
res, env = autocomplete(
- words=(f'pip install {cl_opts} r'),
- cword='2',
+ words=(f"pip install {cl_opts} r"),
+ cword="2",
cwd=data.completion_paths,
)
- assert not any(out in res.stdout for out in
- ('requirements.txt', 'readme.txt',)), (
- "autocomplete function completed <file> when "
- "it should not complete"
- )
- assert not any(os.path.join(out, '') in res.stdout
- for out in ('replay', 'resources')), (
- "autocomplete function completed <dir> when "
- "it should not complete"
- )
+ assert not any(
+ out in res.stdout
+ for out in (
+ "requirements.txt",
+ "readme.txt",
+ )
+ ), "autocomplete function completed <file> when it should not complete"
+ assert not any(
+ os.path.join(out, "") in res.stdout for out in ("replay", "resources")
+ ), "autocomplete function completed <dir> when it should not complete"
-def test_completion_directories_after_option(autocomplete, data):
+def test_completion_directories_after_option(
+ autocomplete: DoAutocomplete, data: TestData
+) -> None:
"""
Test getting completion <dir> after options in command
(e.g. ``pip --cache-dir``)
"""
res, env = autocomplete(
- words=('pip --cache-dir r'),
- cword='2',
+ words=("pip --cache-dir r"),
+ cword="2",
cwd=data.completion_paths,
)
- assert os.path.join('resources', '') in res.stdout, (
- "autocomplete function could not complete <dir> after options"
- )
- assert not any(out in res.stdout for out in (
- 'requirements.txt', 'README.txt', os.path.join('REPLAY', ''))), (
- "autocomplete function completed <dir> when "
- "it should not complete"
- )
- if sys.platform == 'win32':
- assert os.path.join('replay', '') in res.stdout, (
- "autocomplete function could not complete <dir> after options"
- )
-
-
-def test_completion_subdirectories_after_option(autocomplete, data):
+ assert (
+ os.path.join("resources", "") in res.stdout
+ ), "autocomplete function could not complete <dir> after options"
+ assert not any(
+ out in res.stdout
+ for out in ("requirements.txt", "README.txt", os.path.join("REPLAY", ""))
+ ), "autocomplete function completed <dir> when it should not complete"
+ if sys.platform == "win32":
+ assert (
+ os.path.join("replay", "") in res.stdout
+ ), "autocomplete function could not complete <dir> after options"
+
+
+def test_completion_subdirectories_after_option(
+ autocomplete: DoAutocomplete, data: TestData
+) -> None:
"""
Test getting completion <dir> after options in command
given path of a directory
"""
res, env = autocomplete(
- words=('pip --cache-dir ' + os.path.join('resources', '')),
- cword='2',
+ words=("pip --cache-dir " + os.path.join("resources", "")),
+ cword="2",
cwd=data.completion_paths,
)
- assert os.path.join('resources',
- os.path.join('images', '')) in res.stdout, (
+ assert os.path.join("resources", os.path.join("images", "")) in res.stdout, (
"autocomplete function could not complete <dir> "
"given path of a directory after options"
)
-def test_completion_path_after_option(autocomplete, data):
+def test_completion_path_after_option(
+ autocomplete: DoAutocomplete, data: TestData
+) -> None:
"""
Test getting completion <path> after options in command
given absolute path
"""
res, env = autocomplete(
- words=('pip install -e ' + os.path.join(data.completion_paths, 'R')),
- cword='3',
+ words=("pip install -e " + os.path.join(data.completion_paths, "R")),
+ cword="3",
)
- assert all(os.path.normcase(os.path.join(data.completion_paths, out))
- in res.stdout for out in (
- 'README.txt', os.path.join('REPLAY', ''))), (
+ assert all(
+ os.path.normcase(os.path.join(data.completion_paths, out)) in res.stdout
+ for out in ("README.txt", os.path.join("REPLAY", ""))
+ ), (
"autocomplete function could not complete <path> "
"after options in command given absolute path"
)
-@pytest.mark.parametrize('flag', ['--bash', '--zsh', '--fish'])
+@pytest.mark.parametrize("flag", ["--bash", "--zsh", "--fish", "--powershell"])
def test_completion_uses_same_executable_name(
- autocomplete_script, flag, deprecated_python
-):
- executable_name = 'pip{}'.format(sys.version_info[0])
+ autocomplete_script: PipTestEnvironment, flag: str, deprecated_python: bool
+) -> None:
+ executable_name = "pip{}".format(sys.version_info[0])
# Deprecated python versions produce an extra deprecation warning
result = autocomplete_script.run(
- executable_name, 'completion', flag, expect_stderr=deprecated_python,
+ executable_name,
+ "completion",
+ flag,
+ expect_stderr=deprecated_python,
)
assert executable_name in result.stdout