# Copyright 2012-2023, Andrey Kislyuk and argcomplete contributors. Licensed under the terms of the # `Apache License, Version 2.0 `_. Distribution of the LICENSE and NOTICE # files with source copies of this package and derivative works is **REQUIRED** as specified by the Apache License. # See https://github.com/kislyuk/argcomplete for more info. import argparse import os import sys from collections.abc import Mapping from typing import Callable, Dict, List, Optional, Sequence, TextIO, Union from . import io as _io from .completers import BaseCompleter, ChoicesCompleter, FilesCompleter, SuppressCompleter from .io import debug, mute_stderr from .lexers import split_line from .packages._argparse import IntrospectiveArgumentParser, action_is_greedy, action_is_open, action_is_satisfied safe_actions = { argparse._StoreAction, argparse._StoreConstAction, argparse._StoreTrueAction, argparse._StoreFalseAction, argparse._AppendAction, argparse._AppendConstAction, argparse._CountAction, } def default_validator(completion, prefix): return completion.startswith(prefix) class CompletionFinder(object): """ Inherit from this class if you wish to override any of the stages below. Otherwise, use ``argcomplete.autocomplete()`` directly (it's a convenience instance of this class). It has the same signature as :meth:`CompletionFinder.__call__()`. """ def __init__( self, argument_parser=None, always_complete_options=True, exclude=None, validator=None, print_suppressed=False, default_completer=FilesCompleter(), append_space=None, ): self._parser = argument_parser self.always_complete_options = always_complete_options self.exclude = exclude if validator is None: validator = default_validator self.validator = validator self.print_suppressed = print_suppressed self.completing = False self._display_completions: Dict[str, str] = {} self.default_completer = default_completer if append_space is None: append_space = os.environ.get("_ARGCOMPLETE_SUPPRESS_SPACE") != "1" self.append_space = append_space def __call__( self, argument_parser: argparse.ArgumentParser, always_complete_options: Union[bool, str] = True, exit_method: Callable = os._exit, output_stream: Optional[TextIO] = None, exclude: Optional[Sequence[str]] = None, validator: Optional[Callable] = None, print_suppressed: bool = False, append_space: Optional[bool] = None, default_completer: BaseCompleter = FilesCompleter(), ) -> None: """ :param argument_parser: The argument parser to autocomplete on :param always_complete_options: Controls the autocompletion of option strings if an option string opening character (normally ``-``) has not been entered. If ``True`` (default), both short (``-x``) and long (``--x``) option strings will be suggested. If ``False``, no option strings will be suggested. If ``long``, long options and short options with no long variant will be suggested. If ``short``, short options and long options with no short variant will be suggested. :param exit_method: Method used to stop the program after printing completions. Defaults to :meth:`os._exit`. If you want to perform a normal exit that calls exit handlers, use :meth:`sys.exit`. :param exclude: List of strings representing options to be omitted from autocompletion :param validator: Function to filter all completions through before returning (called with two string arguments, completion and prefix; return value is evaluated as a boolean) :param print_suppressed: Whether or not to autocomplete options that have the ``help=argparse.SUPPRESS`` keyword argument set. :param append_space: Whether to append a space to unique matches. The default is ``True``. .. note:: If you are not subclassing CompletionFinder to override its behaviors, use :meth:`argcomplete.autocomplete()` directly. It has the same signature as this method. Produces tab completions for ``argument_parser``. See module docs for more info. Argcomplete only executes actions if their class is known not to have side effects. Custom action classes can be added to argcomplete.safe_actions, if their values are wanted in the ``parsed_args`` completer argument, or their execution is otherwise desirable. """ self.__init__( # type: ignore argument_parser, always_complete_options=always_complete_options, exclude=exclude, validator=validator, print_suppressed=print_suppressed, append_space=append_space, default_completer=default_completer, ) if "_ARGCOMPLETE" not in os.environ: # not an argument completion invocation return self._init_debug_stream() if output_stream is None: filename = os.environ.get("_ARGCOMPLETE_STDOUT_FILENAME") if filename is not None: debug("Using output file {}".format(filename)) output_stream = open(filename, "w") if output_stream is None: try: output_stream = os.fdopen(8, "w") except Exception: debug("Unable to open fd 8 for writing, quitting") exit_method(1) assert output_stream is not None ifs = os.environ.get("_ARGCOMPLETE_IFS", "\013") if len(ifs) != 1: debug("Invalid value for IFS, quitting [{v}]".format(v=ifs)) exit_method(1) dfs = os.environ.get("_ARGCOMPLETE_DFS") if dfs and len(dfs) != 1: debug("Invalid value for DFS, quitting [{v}]".format(v=dfs)) exit_method(1) comp_line = os.environ["COMP_LINE"] comp_point = int(os.environ["COMP_POINT"]) cword_prequote, cword_prefix, cword_suffix, comp_words, last_wordbreak_pos = split_line(comp_line, comp_point) # _ARGCOMPLETE is set by the shell script to tell us where comp_words # should start, based on what we're completing. # 1: