Path: blob/master/venv/Lib/site-packages/pip/_internal/cli/autocompletion.py
811 views
"""Logic that powers autocompletion installed by ``pip completion``.1"""23import optparse4import os5import sys6from itertools import chain78from pip._internal.cli.main_parser import create_main_parser9from pip._internal.commands import commands_dict, create_command10from pip._internal.utils.misc import get_installed_distributions11from pip._internal.utils.typing import MYPY_CHECK_RUNNING1213if MYPY_CHECK_RUNNING:14from typing import Any, Iterable, List, Optional151617def autocomplete():18# type: () -> None19"""Entry Point for completion of main and subcommand options.20"""21# Don't complete if user hasn't sourced bash_completion file.22if 'PIP_AUTO_COMPLETE' not in os.environ:23return24cwords = os.environ['COMP_WORDS'].split()[1:]25cword = int(os.environ['COMP_CWORD'])26try:27current = cwords[cword - 1]28except IndexError:29current = ''3031parser = create_main_parser()32subcommands = list(commands_dict)33options = []3435# subcommand36subcommand_name = None # type: Optional[str]37for word in cwords:38if word in subcommands:39subcommand_name = word40break41# subcommand options42if subcommand_name is not None:43# special case: 'help' subcommand has no options44if subcommand_name == 'help':45sys.exit(1)46# special case: list locally installed dists for show and uninstall47should_list_installed = (48subcommand_name in ['show', 'uninstall'] and49not current.startswith('-')50)51if should_list_installed:52installed = []53lc = current.lower()54for dist in get_installed_distributions(local_only=True):55if dist.key.startswith(lc) and dist.key not in cwords[1:]:56installed.append(dist.key)57# if there are no dists installed, fall back to option completion58if installed:59for dist in installed:60print(dist)61sys.exit(1)6263subcommand = create_command(subcommand_name)6465for opt in subcommand.parser.option_list_all:66if opt.help != optparse.SUPPRESS_HELP:67for opt_str in opt._long_opts + opt._short_opts:68options.append((opt_str, opt.nargs))6970# filter out previously specified options from available options71prev_opts = [x.split('=')[0] for x in cwords[1:cword - 1]]72options = [(x, v) for (x, v) in options if x not in prev_opts]73# filter options by current input74options = [(k, v) for k, v in options if k.startswith(current)]75# get completion type given cwords and available subcommand options76completion_type = get_path_completion_type(77cwords, cword, subcommand.parser.option_list_all,78)79# get completion files and directories if ``completion_type`` is80# ``<file>``, ``<dir>`` or ``<path>``81if completion_type:82paths = auto_complete_paths(current, completion_type)83options = [(path, 0) for path in paths]84for option in options:85opt_label = option[0]86# append '=' to options which require args87if option[1] and option[0][:2] == "--":88opt_label += '='89print(opt_label)90else:91# show main parser options only when necessary9293opts = [i.option_list for i in parser.option_groups]94opts.append(parser.option_list)95flattened_opts = chain.from_iterable(opts)96if current.startswith('-'):97for opt in flattened_opts:98if opt.help != optparse.SUPPRESS_HELP:99subcommands += opt._long_opts + opt._short_opts100else:101# get completion type given cwords and all available options102completion_type = get_path_completion_type(cwords, cword,103flattened_opts)104if completion_type:105subcommands = list(auto_complete_paths(current,106completion_type))107108print(' '.join([x for x in subcommands if x.startswith(current)]))109sys.exit(1)110111112def get_path_completion_type(cwords, cword, opts):113# type: (List[str], int, Iterable[Any]) -> Optional[str]114"""Get the type of path completion (``file``, ``dir``, ``path`` or None)115116:param cwords: same as the environmental variable ``COMP_WORDS``117:param cword: same as the environmental variable ``COMP_CWORD``118:param opts: The available options to check119:return: path completion type (``file``, ``dir``, ``path`` or None)120"""121if cword < 2 or not cwords[cword - 2].startswith('-'):122return None123for opt in opts:124if opt.help == optparse.SUPPRESS_HELP:125continue126for o in str(opt).split('/'):127if cwords[cword - 2].split('=')[0] == o:128if not opt.metavar or any(129x in ('path', 'file', 'dir')130for x in opt.metavar.split('/')):131return opt.metavar132return None133134135def auto_complete_paths(current, completion_type):136# type: (str, str) -> Iterable[str]137"""If ``completion_type`` is ``file`` or ``path``, list all regular files138and directories starting with ``current``; otherwise only list directories139starting with ``current``.140141:param current: The word to be completed142:param completion_type: path completion type(`file`, `path` or `dir`)i143:return: A generator of regular files and/or directories144"""145directory, filename = os.path.split(current)146current_path = os.path.abspath(directory)147# Don't complete paths if they can't be accessed148if not os.access(current_path, os.R_OK):149return150filename = os.path.normcase(filename)151# list all files that start with ``filename``152file_list = (x for x in os.listdir(current_path)153if os.path.normcase(x).startswith(filename))154for f in file_list:155opt = os.path.join(current_path, f)156comp_file = os.path.normcase(os.path.join(directory, f))157# complete regular files when there is not ``<dir>`` after option158# complete directories when there is ``<file>``, ``<path>`` or159# ``<dir>``after option160if completion_type != 'dir' and os.path.isfile(opt):161yield comp_file162elif os.path.isdir(opt):163yield os.path.join(comp_file, '')164165166