pip/tests/functional/test_completion.py

317 lines
9.8 KiB
Python

import os
import sys
import pytest
from tests.lib.path import Path
COMPLETION_FOR_SUPPORTED_SHELLS_TESTS = (
('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', """\
function __fish_complete_pip
set -lx COMP_WORDS (commandline -o) ""
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"""),
('zsh', """\
function _pip_completion {
local words cword
read -Ac words
read -cn cword
reply=( $( COMP_WORDS="$words[*]" \\
COMP_CWORD=$(( cword-1 )) \\
PIP_AUTO_COMPLETE=1 $words[1] 2>/dev/null ))
}
compctl -K _pip_completion pip"""),
)
@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")))
script = script_factory(tmpdir.joinpath("workspace"))
# Re-install pip so we get the launchers.
script.pip_install_local('-f', common_wheels, pip_src)
return script
@pytest.mark.parametrize(
'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
):
"""
Test getting completion for bash shell
"""
result = script_with_launchers.pip(
'completion', '--' + shell, use_module=False
)
assert completion in result.stdout, str(result.stdout)
@pytest.fixture(scope="session")
def autocomplete_script(tmpdir_factory, script_factory):
tmpdir = Path(str(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'
def do_autocomplete(words, cword, cwd=None):
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()',
expect_error=True,
cwd=cwd,
)
return result, autocomplete_script
return do_autocomplete
def test_completion_for_unknown_shell(autocomplete_script):
"""
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'
def test_completion_alone(autocomplete_script):
"""
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
def test_completion_for_un_snippet(autocomplete):
"""
Test getting completion for ``un`` should return uninstall
"""
res, env = autocomplete('pip un', '1')
assert res.stdout.strip().split() == ['uninstall'], res.stdout
def test_completion_for_default_parameters(autocomplete):
"""
Test getting completion for ``--`` should contain --help
"""
res, env = autocomplete('pip --', '1')
assert '--help' in res.stdout,\
"autocomplete function could not complete ``--``"
def test_completion_option_for_command(autocomplete):
"""
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 ``--``"
def test_completion_short_option(autocomplete):
"""
Test getting completion for short options after ``-`` (eg. pip -)
"""
res, env = autocomplete('pip -', '1')
assert '-h' in res.stdout.split(),\
"autocomplete function could not complete short options after ``-``"
def test_completion_short_option_for_command(autocomplete):
"""
Test getting completion for short options after ``-`` in command
(eg. pip search -)
"""
res, env = autocomplete('pip search -', '2')
assert '-h' in res.stdout.split(),\
"autocomplete function could not complete short options after ``-``"
def test_completion_files_after_option(autocomplete, data):
"""
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',
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')), (
"autocomplete function completed <file> or <dir> that "
"should not be completed"
)
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"
)
def test_completion_not_files_after_option(autocomplete, data):
"""
Test not getting completion files after options which not applicable
(e.g. ``pip install``)
"""
res, env = autocomplete(
words=('pip install 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"
)
@pytest.mark.parametrize("cl_opts", ["-U", "--user", "-h"])
def test_completion_not_files_after_nonexpecting_option(
autocomplete, data, cl_opts
):
"""
Test not getting completion files after options which not applicable
(e.g. ``pip install``)
"""
res, env = autocomplete(
words=('pip install {cl_opts} r'.format(**locals())),
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"
)
def test_completion_directories_after_option(autocomplete, data):
"""
Test getting completion <dir> after options in command
(e.g. ``pip --cache-dir``)
"""
res, env = autocomplete(
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):
"""
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',
cwd=data.completion_paths,
)
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):
"""
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',
)
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'])
def test_completion_uses_same_executable_name(
autocomplete_script, flag, deprecated_python
):
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,
)
assert executable_name in result.stdout