From c555bfde01a270f70d953afe4f3d7d7e4fa4dee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Dequ=C3=A8nes=20=28Duck=29?= <Duck@DuckCorp.org> Date: Thu, 26 May 2022 13:01:48 +0900 Subject: [PATCH] New upstream version 2.0.0 --- .github/FUNDING.yml | 1 - .github/workflows/pythonpackage.yml | 27 - .gitignore | 30 - Changes.rst | 19 +- Makefile | 28 - PKG-INFO | 761 +++++++++--------- README.rst | 18 +- argcomplete.egg-info/PKG-INFO | 761 +++++++++--------- argcomplete.egg-info/SOURCES.txt | 11 - argcomplete.egg-info/requires.txt | 6 - argcomplete/__init__.py | 52 +- .../bash_completion.d/python-argcomplete | 2 +- argcomplete/compat.py | 25 - argcomplete/completers.py | 14 +- argcomplete/my_argparse.py | 4 +- argcomplete/shell_integration.py | 13 +- common.mk | 62 -- docs/conf.py | 33 - docs/examples/describe_github_user.py | 16 - docs/fish_help_string.png | Bin 14171 -> 0 bytes docs/index.rst | 11 - scripts/activate-global-python-argcomplete | 13 +- ...thon-argcomplete-check-easy-install-script | 4 +- scripts/register-python-argcomplete | 4 +- setup.cfg | 2 +- setup.py | 10 +- test/prog | 2 +- test/test.py | 69 +- tox.ini | 8 - 29 files changed, 904 insertions(+), 1102 deletions(-) delete mode 100644 .github/FUNDING.yml delete mode 100644 .github/workflows/pythonpackage.yml delete mode 100644 .gitignore delete mode 100644 Makefile delete mode 100644 argcomplete/compat.py delete mode 100644 common.mk delete mode 100644 docs/conf.py delete mode 100755 docs/examples/describe_github_user.py delete mode 100644 docs/fish_help_string.png delete mode 100644 docs/index.rst delete mode 100644 tox.ini diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 90d9e33..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: [kislyuk] diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml deleted file mode 100644 index 6c59173..0000000 --- a/.github/workflows/pythonpackage.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Python package - -on: [push, pull_request] - -jobs: - build: - runs-on: ${{matrix.os}} - strategy: - matrix: - os: [ubuntu-20.04, macos-10.15] - python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] - - steps: - - uses: actions/checkout@v1 - - name: Set up Python ${{matrix.python-version}} - uses: actions/setup-python@v1 - with: - python-version: ${{matrix.python-version}} - - name: Install - run: | - [[ $(uname) == Linux ]] && sudo apt-get install --yes rpm tcsh fish - [[ $(uname) == Darwin ]] && brew install bash rpm tcsh fish - python -m pip install --quiet --upgrade codecov - - name: Test - run: | - make test - - uses: codecov/codecov-action@v1 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 259db9d..0000000 --- a/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ -# Reminder: -# - A leading slash means the pattern is anchored at the root. -# - No leading slash means the pattern matches at any depth. - -# Python files -*.pyc -__pycache__/ -.tox/ -*.egg-info/ -/build/ -/dist/ -/.eggs/ -.coverage - -# IDE project files -/.pydevproject - -# vim python-mode plugin -/.ropeproject - -# IntelliJ IDEA / PyCharm project files -/.idea -/*.iml - -# JS/node/npm/web dev files -node_modules -npm-debug.log - -# OS X metadata files -.DS_Store diff --git a/Changes.rst b/Changes.rst index 01eeb68..317246d 100644 --- a/Changes.rst +++ b/Changes.rst @@ -1,3 +1,16 @@ +Changes for v2.0.0 (2022-01-03) +=============================== + +- Truncate input after cursor. Fixes #351 (#352) + +- Support of path completion in fish #327 (#359) + +- Drop support for Python 2.7 and 3.5 (#361) + +- Add support for Python 3.10 (#356) + +- Test, documentation, and release infrastructure improvements + Changes for v1.12.3 (2021-04-19) ================================ @@ -257,17 +270,17 @@ Version 0.8.6 (2015-04-11) Version 0.8.5 (2015-04-07) ========================== -- Fix issues related to using argcomplete in a REPL environement. +- Fix issues related to using argcomplete in a REPL environment. - New helper method for custom completion display. - Expand test suite; formatting cleanup. Version 0.8.4 (2014-12-11) ========================== -- Fix issue related to using argcomplete in a REPL environement. Thanks to @wapiflapi (pull request #91). +- Fix issue related to using argcomplete in a REPL environment. Thanks to @wapiflapi (pull request #91). Version 0.8.3 (2014-11-09) ========================== -- Fix multiple issues related to using argcomplete in a REPL environement. Thanks to @wapiflapi (pull request #90). +- Fix multiple issues related to using argcomplete in a REPL environment. Thanks to @wapiflapi (pull request #90). Version 0.8.2 (2014-11-03) ========================== diff --git a/Makefile b/Makefile deleted file mode 100644 index ee621f5..0000000 --- a/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -test_deps: - python -m pip install .[test] - -lint: test_deps - ./setup.py flake8 - for script in scripts/*; do if grep -q python $$script; then flake8 $$script; fi; done - -test: lint test_deps - coverage run --source=argcomplete --omit=argcomplete/my_shlex.py ./test/test.py -v - -init_docs: - cd docs; sphinx-quickstart - -docs: - sphinx-build docs docs/html - -install: clean - pip install wheel - python setup.py bdist_wheel - pip install --upgrade dist/*.whl - -clean: - -rm -rf build dist - -rm -rf *.egg-info - -.PHONY: test test_deps docs install clean lint lint_deps - -include common.mk diff --git a/PKG-INFO b/PKG-INFO index 05a65b1..e7ca3e4 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: argcomplete -Version: 1.12.3 +Version: 2.0.0 Summary: Bash tab completion for argparse Home-page: https://github.com/kislyuk/argcomplete Author: Andrey Kislyuk @@ -10,374 +10,6 @@ Project-URL: Documentation, https://kislyuk.github.io/argcomplete Project-URL: Source Code, https://github.com/kislyuk/argcomplete Project-URL: Issue Tracker, https://github.com/kislyuk/argcomplete/issues Project-URL: Change Log, https://github.com/kislyuk/argcomplete/blob/master/Changes.rst -Description: argcomplete - Bash tab completion for argparse - ============================================== - *Tab complete all the things!* - - Argcomplete provides easy, extensible command line tab completion of arguments for your Python script. - - It makes two assumptions: - - * You're using bash as your shell (limited support for zsh, fish, and tcsh is available) - * You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options - - Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can - dynamically suggest completions for your argument/option values (for example, if the user is browsing resources over - the network). - - Installation - ------------ - :: - - pip3 install argcomplete - activate-global-python-argcomplete - - See `Activating global completion`_ below for details about the second step (or if it reports an error). - - Refresh your bash environment (start a new shell or ``source /etc/profile``). - - Synopsis - -------- - Python code (e.g. ``my-awesome-script``): - - .. code-block:: python - - #!/usr/bin/env python - # PYTHON_ARGCOMPLETE_OK - import argcomplete, argparse - parser = argparse.ArgumentParser() - ... - argcomplete.autocomplete(parser) - args = parser.parse_args() - ... - - Shellcode (only necessary if global completion is not activated - see `Global completion`_ below), to be put in e.g. ``.bashrc``:: - - eval "$(register-python-argcomplete my-awesome-script)" - - argcomplete.autocomplete(*parser*) - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This method is the entry point to the module. It must be called **after** ArgumentParser construction is complete, but - **before** the ``ArgumentParser.parse_args()`` method is called. The method looks for an environment variable that the - completion hook shellcode sets, and if it's there, collects completions, prints them to the output stream (fd 8 by - default), and exits. Otherwise, it returns to the caller immediately. - - .. admonition:: Side effects - - Argcomplete gets completions by running your program. It intercepts the execution flow at the moment - ``argcomplete.autocomplete()`` is called. After sending completions, it exits using ``exit_method`` (``os._exit`` - by default). This means if your program has any side effects that happen before ``argcomplete`` is called, those - side effects will happen every time the user presses ``<TAB>`` (although anything your program prints to stdout or - stderr will be suppressed). For this reason it's best to construct the argument parser and call - ``argcomplete.autocomplete()`` as early as possible in your execution flow. - - .. admonition:: Performance - - If the program takes a long time to get to the point where ``argcomplete.autocomplete()`` is called, the tab completion - process will feel sluggish, and the user may lose confidence in it. So it's also important to minimize the startup time - of the program up to that point (for example, by deferring initialization or importing of large modules until after - parsing options). - - Specifying completers - --------------------- - You can specify custom completion functions for your options and arguments. Two styles are supported: callable and - readline-style. Callable completers are simpler. They are called with the following keyword arguments: - - * ``prefix``: The prefix text of the last word before the cursor on the command line. - For dynamic completers, this can be used to reduce the work required to generate possible completions. - * ``action``: The ``argparse.Action`` instance that this completer was called for. - * ``parser``: The ``argparse.ArgumentParser`` instance that the action was taken by. - * ``parsed_args``: The result of argument parsing so far (the ``argparse.Namespace`` args object normally returned by - ``ArgumentParser.parse_args()``). - - Completers should return their completions as a list of strings. An example completer for names of environment - variables might look like this: - - .. code-block:: python - - def EnvironCompleter(**kwargs): - return os.environ - - To specify a completer for an argument or option, set the ``completer`` attribute of its associated action. An easy - way to do this at definition time is: - - .. code-block:: python - - from argcomplete.completers import EnvironCompleter - - parser = argparse.ArgumentParser() - parser.add_argument("--env-var1").completer = EnvironCompleter - parser.add_argument("--env-var2").completer = EnvironCompleter - argcomplete.autocomplete(parser) - - If you specify the ``choices`` keyword for an argparse option or argument (and don't specify a completer), it will be - used for completions. - - A completer that is initialized with a set of all possible choices of values for its action might look like this: - - .. code-block:: python - - class ChoicesCompleter(object): - def __init__(self, choices): - self.choices = choices - - def __call__(self, **kwargs): - return self.choices - - The following two ways to specify a static set of choices are equivalent for completion purposes: - - .. code-block:: python - - from argcomplete.completers import ChoicesCompleter - - parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss')) - parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss')) - - Note that if you use the ``choices=<completions>`` option, argparse will show - all these choices in the ``--help`` output by default. To prevent this, set - ``metavar`` (like ``parser.add_argument("--protocol", metavar="PROTOCOL", - choices=('http', 'https', 'ssh', 'rsync', 'wss'))``). - - The following `script <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ uses - ``parsed_args`` and `Requests <http://python-requests.org/>`_ to query GitHub for publicly known members of an - organization and complete their names, then prints the member description: - - .. code-block:: python - - #!/usr/bin/env python - # PYTHON_ARGCOMPLETE_OK - import argcomplete, argparse, requests, pprint - - def github_org_members(prefix, parsed_args, **kwargs): - resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization) - return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix)) - - parser = argparse.ArgumentParser() - parser.add_argument("--organization", help="GitHub organization") - parser.add_argument("--member", help="GitHub member").completer = github_org_members - - argcomplete.autocomplete(parser) - args = parser.parse_args() - - pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json()) - - `Try it <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ like this:: - - ./describe_github_user.py --organization heroku --member <TAB> - - If you have a useful completer to add to the `completer library - <https://github.com/kislyuk/argcomplete/blob/master/argcomplete/completers.py>`_, send a pull request! - - Readline-style completers - ~~~~~~~~~~~~~~~~~~~~~~~~~ - The readline_ module defines a completer protocol in rlcompleter_. Readline-style completers are also supported by - argcomplete, so you can use the same completer object both in an interactive readline-powered shell and on the bash - command line. For example, you can use the readline-style completer provided by IPython_ to get introspective - completions like you would get in the IPython shell: - - .. _readline: http://docs.python.org/3/library/readline.html - .. _rlcompleter: http://docs.python.org/3/library/rlcompleter.html#completer-objects - .. _IPython: http://ipython.org/ - - .. code-block:: python - - import IPython - parser.add_argument("--python-name").completer = IPython.core.completer.Completer() - - ``argcomplete.CompletionFinder.rl_complete`` can also be used to plug in an argparse parser as a readline completer. - - Printing warnings in completers - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Normal stdout/stderr output is suspended when argcomplete runs. Sometimes, though, when the user presses ``<TAB>``, it's - appropriate to print information about why completions generation failed. To do this, use ``warn``: - - .. code-block:: python - - from argcomplete import warn - - def AwesomeWebServiceCompleter(prefix, **kwargs): - if login_failed: - warn("Please log in to Awesome Web Service to use autocompletion") - return completions - - Using a custom completion validator - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - By default, argcomplete validates your completions by checking if they start with the prefix given to the completer. You - can override this validation check by supplying the ``validator`` keyword to ``argcomplete.autocomplete()``: - - .. code-block:: python - - def my_validator(current_input, keyword_to_check_against): - # Pass through ALL options even if they don't all start with 'current_input' - return True - - argcomplete.autocomplete(parser, validator=my_validator) - - Global completion - ----------------- - In global completion mode, you don't have to register each argcomplete-capable executable separately. Instead, bash - will look for the string **PYTHON_ARGCOMPLETE_OK** in the first 1024 bytes of any executable that it's running - completion for, and if it's found, follow the rest of the argcomplete protocol as described above. - - Additionally, completion is activated for scripts run as ``python <script>`` and ``python -m <module>``. - This also works for alternate Python versions (e.g. ``python3`` and ``pypy``), as long as that version of Python has - argcomplete installed. - - .. admonition:: Bash version compatibility - - Global completion requires bash support for ``complete -D``, which was introduced in bash 4.2. On OS X or older Linux - systems, you will need to update bash to use this feature. Check the version of the running copy of bash with - ``echo $BASH_VERSION``. On OS X, install bash via `Homebrew <http://brew.sh/>`_ (``brew install bash``), add - ``/usr/local/bin/bash`` to ``/etc/shells``, and run ``chsh`` to change your shell. - - Global completion is not currently compatible with zsh. - - .. note:: If you use setuptools/distribute ``scripts`` or ``entry_points`` directives to package your module, - argcomplete will follow the wrapper scripts to their destination and look for ``PYTHON_ARGCOMPLETE_OK`` in the - destination code. - - If you choose not to use global completion, or ship a bash completion module that depends on argcomplete, you must - register your script explicitly using ``eval "$(register-python-argcomplete my-awesome-script)"``. Standard bash - completion registration roules apply: namely, the script name is passed directly to ``complete``, meaning it is only tab - completed when invoked exactly as it was registered. In the above example, ``my-awesome-script`` must be on the path, - and the user must be attempting to complete it by that name. The above line alone would **not** allow you to complete - ``./my-awesome-script``, or ``/path/to/my-awesome-script``. - - - Activating global completion - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The script ``activate-global-python-argcomplete`` will try to install the file - ``bash_completion.d/python-argcomplete`` (`see on GitHub`_) into an appropriate location on your system - (``/etc/bash_completion.d/`` or ``~/.bash_completion.d/``). If it - fails, but you know the correct location of your bash completion scripts directory, you can specify it with ``--dest``:: - - activate-global-python-argcomplete --dest=/path/to/bash_completion.d - - Otherwise, you can redirect its shellcode output into a file:: - - activate-global-python-argcomplete --dest=- > file - - The file's contents should then be sourced in e.g. ``~/.bashrc``. - - .. _`see on GitHub`: https://github.com/kislyuk/argcomplete/blob/master/argcomplete/bash_completion.d/python-argcomplete - - Zsh Support - ------------ - To activate completions for zsh you need to have ``bashcompinit`` enabled in zsh:: - - autoload -U bashcompinit - bashcompinit - - Afterwards you can enable completion for your scripts with ``register-python-argcomplete``:: - - eval "$(register-python-argcomplete my-awesome-script)" - - Tcsh Support - ------------ - To activate completions for tcsh use:: - - eval `register-python-argcomplete --shell tcsh my-awesome-script` - - The ``python-argcomplete-tcsh`` script provides completions for tcsh. - The following is an example of the tcsh completion syntax for - ``my-awesome-script`` emitted by ``register-python-argcomplete``:: - - complete my-awesome-script 'p@*@`python-argcomplete-tcsh my-awesome-script`@' - - Fish Support - ------------ - To activate completions for fish use:: - - register-python-argcomplete --shell fish my-awesome-script | source - - or create new completion file, e.g:: - - register-python-argcomplete --shell fish my-awesome-script > ~/.config/fish/completions/my-awesome-script.fish - - Completion Description For Fish - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - By default help string is added as completion description. - - .. image:: docs/fish_help_string.png - - You can disable this feature by removing ``_ARGCOMPLETE_DFS`` variable, e.g:: - - register-python-argcomplete --shell fish my-awesome-script | grep -v _ARGCOMPLETE_DFS | . - - Git Bash Support - ---------------- - Due to limitations of file descriptor inheritance on Windows, - Git Bash not supported out of the box. You can opt in to using - temporary files instead of file descriptors for for IPC - by setting the environment variable ``ARGCOMPLETE_USE_TEMPFILES``, - e.g. by adding ``export ARGCOMPLETE_USE_TEMPFILES=1`` to ``~/.bashrc``. - - For full support, consider using Bash with the - Windows Subsystem for Linux (WSL). - - External argcomplete script - --------------------------- - To register an argcomplete script for an arbitrary name, the ``--external-argcomplete-script`` argument of the ``register-python-argcomplete`` script can be used:: - - eval "$(register-python-argcomplete --external-argcomplete-script /path/to/script arbitrary-name)" - - This allows, for example, to use the auto completion functionality of argcomplete for an application not written in Python. - The command line interface of this program must be additionally implemented in a Python script with argparse and argcomplete and whenever the application is called the registered external argcomplete script is used for auto completion. - - This option can also be used in combination with the other supported shells. - - Python Support - -------------- - Argcomplete requires Python 2.7 or 3.5+. - - Common Problems - --------------- - If global completion is not completing your script, bash may have registered a - default completion function:: - - $ complete | grep my-awesome-script - complete -F _minimal my-awesome-script - - You can fix this by restarting your shell, or by running - ``complete -r my-awesome-script``. - - Debugging - --------- - Set the ``_ARC_DEBUG`` variable in your shell to enable verbose debug output every time argcomplete runs. This will - disrupt the command line composition state of your terminal, but make it possible to see the internal state of the - completer if it encounters problems. - - Acknowledgments - --------------- - Inspired and informed by the optcomplete_ module by Martin Blais. - - .. _optcomplete: http://pypi.python.org/pypi/optcomplete - - Links - ----- - * `Project home page (GitHub) <https://github.com/kislyuk/argcomplete>`_ - * `Documentation <https://kislyuk.github.io/argcomplete/>`_ - * `Package distribution (PyPI) <https://pypi.python.org/pypi/argcomplete>`_ - * `Change log <https://github.com/kislyuk/argcomplete/blob/master/Changes.rst>`_ - * `xontrib-argcomplete <https://github.com/anki-code/xontrib-argcomplete>`_ - support argcomplete in `xonsh <https://github.com/xonsh/xonsh>`_ shell - - Bugs - ~~~~ - Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/kislyuk/argcomplete/issues>`_. - - License - ------- - Licensed under the terms of the `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. - - .. image:: https://github.com/kislyuk/argcomplete/workflows/Python%20package/badge.svg - :target: https://github.com/kislyuk/argcomplete/actions - .. image:: https://codecov.io/github/kislyuk/argcomplete/coverage.svg?branch=master - :target: https://codecov.io/github/kislyuk/argcomplete?branch=master - .. image:: https://img.shields.io/pypi/v/argcomplete.svg - :target: https://pypi.python.org/pypi/argcomplete - .. image:: https://img.shields.io/pypi/l/argcomplete.svg - :target: https://pypi.python.org/pypi/argcomplete - Platform: MacOS X Platform: Posix Classifier: Environment :: Console @@ -386,13 +18,12 @@ Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Development Status :: 5 - Production/Stable @@ -400,4 +31,390 @@ Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Shells Classifier: Topic :: Terminals +Requires-Python: >=3.6 Provides-Extra: test +License-File: LICENSE.rst + +argcomplete - Bash tab completion for argparse +============================================== +*Tab complete all the things!* + +Argcomplete provides easy, extensible command line tab completion of arguments for your Python script. + +It makes two assumptions: + +* You're using bash as your shell (limited support for zsh, fish, and tcsh is available) +* You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options + +Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can +dynamically suggest completions for your argument/option values (for example, if the user is browsing resources over +the network). + +Installation +------------ +:: + + pip3 install argcomplete + activate-global-python-argcomplete + +See `Activating global completion`_ below for details about the second step (or if it reports an error). + +Refresh your bash environment (start a new shell or ``source /etc/profile``). + +Synopsis +-------- +Python code (e.g. ``my-awesome-script``): + +.. code-block:: python + + #!/usr/bin/env python + # PYTHON_ARGCOMPLETE_OK + import argcomplete, argparse + parser = argparse.ArgumentParser() + ... + argcomplete.autocomplete(parser) + args = parser.parse_args() + ... + +Shellcode (only necessary if global completion is not activated - see `Global completion`_ below), to be put in e.g. ``.bashrc``:: + + eval "$(register-python-argcomplete my-awesome-script)" + +argcomplete.autocomplete(*parser*) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method is the entry point to the module. It must be called **after** ArgumentParser construction is complete, but +**before** the ``ArgumentParser.parse_args()`` method is called. The method looks for an environment variable that the +completion hook shellcode sets, and if it's there, collects completions, prints them to the output stream (fd 8 by +default), and exits. Otherwise, it returns to the caller immediately. + +.. admonition:: Side effects + + Argcomplete gets completions by running your program. It intercepts the execution flow at the moment + ``argcomplete.autocomplete()`` is called. After sending completions, it exits using ``exit_method`` (``os._exit`` + by default). This means if your program has any side effects that happen before ``argcomplete`` is called, those + side effects will happen every time the user presses ``<TAB>`` (although anything your program prints to stdout or + stderr will be suppressed). For this reason it's best to construct the argument parser and call + ``argcomplete.autocomplete()`` as early as possible in your execution flow. + +.. admonition:: Performance + + If the program takes a long time to get to the point where ``argcomplete.autocomplete()`` is called, the tab completion + process will feel sluggish, and the user may lose confidence in it. So it's also important to minimize the startup time + of the program up to that point (for example, by deferring initialization or importing of large modules until after + parsing options). + +Specifying completers +--------------------- +You can specify custom completion functions for your options and arguments. Two styles are supported: callable and +readline-style. Callable completers are simpler. They are called with the following keyword arguments: + +* ``prefix``: The prefix text of the last word before the cursor on the command line. + For dynamic completers, this can be used to reduce the work required to generate possible completions. +* ``action``: The ``argparse.Action`` instance that this completer was called for. +* ``parser``: The ``argparse.ArgumentParser`` instance that the action was taken by. +* ``parsed_args``: The result of argument parsing so far (the ``argparse.Namespace`` args object normally returned by + ``ArgumentParser.parse_args()``). + +Completers should return their completions as a list of strings. An example completer for names of environment +variables might look like this: + +.. code-block:: python + + def EnvironCompleter(**kwargs): + return os.environ + +To specify a completer for an argument or option, set the ``completer`` attribute of its associated action. An easy +way to do this at definition time is: + +.. code-block:: python + + from argcomplete.completers import EnvironCompleter + + parser = argparse.ArgumentParser() + parser.add_argument("--env-var1").completer = EnvironCompleter + parser.add_argument("--env-var2").completer = EnvironCompleter + argcomplete.autocomplete(parser) + +If you specify the ``choices`` keyword for an argparse option or argument (and don't specify a completer), it will be +used for completions. + +A completer that is initialized with a set of all possible choices of values for its action might look like this: + +.. code-block:: python + + class ChoicesCompleter(object): + def __init__(self, choices): + self.choices = choices + + def __call__(self, **kwargs): + return self.choices + +The following two ways to specify a static set of choices are equivalent for completion purposes: + +.. code-block:: python + + from argcomplete.completers import ChoicesCompleter + + parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss')) + parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss')) + +Note that if you use the ``choices=<completions>`` option, argparse will show +all these choices in the ``--help`` output by default. To prevent this, set +``metavar`` (like ``parser.add_argument("--protocol", metavar="PROTOCOL", +choices=('http', 'https', 'ssh', 'rsync', 'wss'))``). + +The following `script <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ uses +``parsed_args`` and `Requests <http://python-requests.org/>`_ to query GitHub for publicly known members of an +organization and complete their names, then prints the member description: + +.. code-block:: python + + #!/usr/bin/env python + # PYTHON_ARGCOMPLETE_OK + import argcomplete, argparse, requests, pprint + + def github_org_members(prefix, parsed_args, **kwargs): + resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization) + return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix)) + + parser = argparse.ArgumentParser() + parser.add_argument("--organization", help="GitHub organization") + parser.add_argument("--member", help="GitHub member").completer = github_org_members + + argcomplete.autocomplete(parser) + args = parser.parse_args() + + pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json()) + +`Try it <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ like this:: + + ./describe_github_user.py --organization heroku --member <TAB> + +If you have a useful completer to add to the `completer library +<https://github.com/kislyuk/argcomplete/blob/master/argcomplete/completers.py>`_, send a pull request! + +Readline-style completers +~~~~~~~~~~~~~~~~~~~~~~~~~ +The readline_ module defines a completer protocol in rlcompleter_. Readline-style completers are also supported by +argcomplete, so you can use the same completer object both in an interactive readline-powered shell and on the bash +command line. For example, you can use the readline-style completer provided by IPython_ to get introspective +completions like you would get in the IPython shell: + +.. _readline: http://docs.python.org/3/library/readline.html +.. _rlcompleter: http://docs.python.org/3/library/rlcompleter.html#completer-objects +.. _IPython: http://ipython.org/ + +.. code-block:: python + + import IPython + parser.add_argument("--python-name").completer = IPython.core.completer.Completer() + +``argcomplete.CompletionFinder.rl_complete`` can also be used to plug in an argparse parser as a readline completer. + +Printing warnings in completers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Normal stdout/stderr output is suspended when argcomplete runs. Sometimes, though, when the user presses ``<TAB>``, it's +appropriate to print information about why completions generation failed. To do this, use ``warn``: + +.. code-block:: python + + from argcomplete import warn + + def AwesomeWebServiceCompleter(prefix, **kwargs): + if login_failed: + warn("Please log in to Awesome Web Service to use autocompletion") + return completions + +Using a custom completion validator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +By default, argcomplete validates your completions by checking if they start with the prefix given to the completer. You +can override this validation check by supplying the ``validator`` keyword to ``argcomplete.autocomplete()``: + +.. code-block:: python + + def my_validator(current_input, keyword_to_check_against): + # Pass through ALL options even if they don't all start with 'current_input' + return True + + argcomplete.autocomplete(parser, validator=my_validator) + +Global completion +----------------- +In global completion mode, you don't have to register each argcomplete-capable executable separately. Instead, bash +will look for the string **PYTHON_ARGCOMPLETE_OK** in the first 1024 bytes of any executable that it's running +completion for, and if it's found, follow the rest of the argcomplete protocol as described above. + +Additionally, completion is activated for scripts run as ``python <script>`` and ``python -m <module>``. +This also works for alternate Python versions (e.g. ``python3`` and ``pypy``), as long as that version of Python has +argcomplete installed. + +.. admonition:: Bash version compatibility + + Global completion requires bash support for ``complete -D``, which was introduced in bash 4.2. On OS X or older Linux + systems, you will need to update bash to use this feature. Check the version of the running copy of bash with + ``echo $BASH_VERSION``. On OS X, install bash via `Homebrew <http://brew.sh/>`_ (``brew install bash``), add + ``/usr/local/bin/bash`` to ``/etc/shells``, and run ``chsh`` to change your shell. + + Global completion is not currently compatible with zsh. + +.. note:: If you use setuptools/distribute ``scripts`` or ``entry_points`` directives to package your module, + argcomplete will follow the wrapper scripts to their destination and look for ``PYTHON_ARGCOMPLETE_OK`` in the + destination code. + +If you choose not to use global completion, or ship a bash completion module that depends on argcomplete, you must +register your script explicitly using ``eval "$(register-python-argcomplete my-awesome-script)"``. Standard bash +completion registration roules apply: namely, the script name is passed directly to ``complete``, meaning it is only tab +completed when invoked exactly as it was registered. In the above example, ``my-awesome-script`` must be on the path, +and the user must be attempting to complete it by that name. The above line alone would **not** allow you to complete +``./my-awesome-script``, or ``/path/to/my-awesome-script``. + + +Activating global completion +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The script ``activate-global-python-argcomplete`` will try to install the file +``bash_completion.d/python-argcomplete`` (`see on GitHub`_) into an appropriate location on your system +(``/etc/bash_completion.d/`` or ``~/.bash_completion.d/``). If it +fails, but you know the correct location of your bash completion scripts directory, you can specify it with ``--dest``:: + + activate-global-python-argcomplete --dest=/path/to/bash_completion.d + +Otherwise, you can redirect its shellcode output into a file:: + + activate-global-python-argcomplete --dest=- > file + +The file's contents should then be sourced in e.g. ``~/.bashrc``. + +.. _`see on GitHub`: https://github.com/kislyuk/argcomplete/blob/master/argcomplete/bash_completion.d/python-argcomplete + +Zsh Support +------------ +To activate completions for zsh you need to have ``bashcompinit`` enabled in zsh:: + + autoload -U bashcompinit + bashcompinit + +Afterwards you can enable completion for your scripts with ``register-python-argcomplete``:: + + eval "$(register-python-argcomplete my-awesome-script)" + +Tcsh Support +------------ +To activate completions for tcsh use:: + + eval `register-python-argcomplete --shell tcsh my-awesome-script` + +The ``python-argcomplete-tcsh`` script provides completions for tcsh. +The following is an example of the tcsh completion syntax for +``my-awesome-script`` emitted by ``register-python-argcomplete``:: + + complete my-awesome-script 'p@*@`python-argcomplete-tcsh my-awesome-script`@' + +Fish Support +------------ +To activate completions for fish use:: + + register-python-argcomplete --shell fish my-awesome-script | source + +or create new completion file, e.g:: + + register-python-argcomplete --shell fish my-awesome-script > ~/.config/fish/completions/my-awesome-script.fish + +Completion Description For Fish +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +By default help string is added as completion description. + +.. image:: docs/fish_help_string.png + +You can disable this feature by removing ``_ARGCOMPLETE_DFS`` variable, e.g:: + + register-python-argcomplete --shell fish my-awesome-script | grep -v _ARGCOMPLETE_DFS | source + +Absolute Path Completion +~~~~~~~~~~~~~~~~~~~~~~~~ +If script is not in path you still can register it's completion by specifying absolute path:: + + register-python-argcomplete --shell fish /home/awesome-user/my-awesome-script | source + +then you can complete it by using ``/home/awesome-user/my-awesome-script`` or ``./my-awesome-script``. + +Unfortunately ``~/my-awesome-script`` would not work, to fix it you can run:: + + register-python-argcomplete --shell fish my-awesome-script -e /home/awesome-user/my-awesome-script | source + +This would enable completion for any ``my-awesome-script`` in any location. + +Git Bash Support +---------------- +Due to limitations of file descriptor inheritance on Windows, +Git Bash not supported out of the box. You can opt in to using +temporary files instead of file descriptors for for IPC +by setting the environment variable ``ARGCOMPLETE_USE_TEMPFILES``, +e.g. by adding ``export ARGCOMPLETE_USE_TEMPFILES=1`` to ``~/.bashrc``. + +For full support, consider using Bash with the +Windows Subsystem for Linux (WSL). + +External argcomplete script +--------------------------- +To register an argcomplete script for an arbitrary name, the ``--external-argcomplete-script`` argument of the ``register-python-argcomplete`` script can be used:: + + eval "$(register-python-argcomplete --external-argcomplete-script /path/to/script arbitrary-name)" + +This allows, for example, to use the auto completion functionality of argcomplete for an application not written in Python. +The command line interface of this program must be additionally implemented in a Python script with argparse and argcomplete and whenever the application is called the registered external argcomplete script is used for auto completion. + +This option can also be used in combination with the other supported shells. + +Python Support +-------------- +Argcomplete requires Python 3.6+. + +Common Problems +--------------- +If global completion is not completing your script, bash may have registered a +default completion function:: + + $ complete | grep my-awesome-script + complete -F _minimal my-awesome-script + +You can fix this by restarting your shell, or by running +``complete -r my-awesome-script``. + +Debugging +--------- +Set the ``_ARC_DEBUG`` variable in your shell to enable verbose debug output every time argcomplete runs. This will +disrupt the command line composition state of your terminal, but make it possible to see the internal state of the +completer if it encounters problems. + +Acknowledgments +--------------- +Inspired and informed by the optcomplete_ module by Martin Blais. + +.. _optcomplete: http://pypi.python.org/pypi/optcomplete + +Links +----- +* `Project home page (GitHub) <https://github.com/kislyuk/argcomplete>`_ +* `Documentation <https://kislyuk.github.io/argcomplete/>`_ +* `Package distribution (PyPI) <https://pypi.python.org/pypi/argcomplete>`_ +* `Change log <https://github.com/kislyuk/argcomplete/blob/master/Changes.rst>`_ +* `xontrib-argcomplete <https://github.com/anki-code/xontrib-argcomplete>`_ - support argcomplete in `xonsh <https://github.com/xonsh/xonsh>`_ shell + +Bugs +~~~~ +Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/kislyuk/argcomplete/issues>`_. + +License +------- +Licensed under the terms of the `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. + +.. image:: https://github.com/kislyuk/argcomplete/workflows/Python%20package/badge.svg + :target: https://github.com/kislyuk/argcomplete/actions +.. image:: https://codecov.io/github/kislyuk/argcomplete/coverage.svg?branch=master + :target: https://codecov.io/github/kislyuk/argcomplete?branch=master +.. image:: https://img.shields.io/pypi/v/argcomplete.svg + :target: https://pypi.python.org/pypi/argcomplete +.. image:: https://img.shields.io/pypi/l/argcomplete.svg + :target: https://pypi.python.org/pypi/argcomplete + + diff --git a/README.rst b/README.rst index 86c8d44..dd19a29 100644 --- a/README.rst +++ b/README.rst @@ -290,7 +290,21 @@ By default help string is added as completion description. You can disable this feature by removing ``_ARGCOMPLETE_DFS`` variable, e.g:: - register-python-argcomplete --shell fish my-awesome-script | grep -v _ARGCOMPLETE_DFS | . + register-python-argcomplete --shell fish my-awesome-script | grep -v _ARGCOMPLETE_DFS | source + +Absolute Path Completion +~~~~~~~~~~~~~~~~~~~~~~~~ +If script is not in path you still can register it's completion by specifying absolute path:: + + register-python-argcomplete --shell fish /home/awesome-user/my-awesome-script | source + +then you can complete it by using ``/home/awesome-user/my-awesome-script`` or ``./my-awesome-script``. + +Unfortunately ``~/my-awesome-script`` would not work, to fix it you can run:: + + register-python-argcomplete --shell fish my-awesome-script -e /home/awesome-user/my-awesome-script | source + +This would enable completion for any ``my-awesome-script`` in any location. Git Bash Support ---------------- @@ -316,7 +330,7 @@ This option can also be used in combination with the other supported shells. Python Support -------------- -Argcomplete requires Python 2.7 or 3.5+. +Argcomplete requires Python 3.6+. Common Problems --------------- diff --git a/argcomplete.egg-info/PKG-INFO b/argcomplete.egg-info/PKG-INFO index 05a65b1..e7ca3e4 100644 --- a/argcomplete.egg-info/PKG-INFO +++ b/argcomplete.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: argcomplete -Version: 1.12.3 +Version: 2.0.0 Summary: Bash tab completion for argparse Home-page: https://github.com/kislyuk/argcomplete Author: Andrey Kislyuk @@ -10,374 +10,6 @@ Project-URL: Documentation, https://kislyuk.github.io/argcomplete Project-URL: Source Code, https://github.com/kislyuk/argcomplete Project-URL: Issue Tracker, https://github.com/kislyuk/argcomplete/issues Project-URL: Change Log, https://github.com/kislyuk/argcomplete/blob/master/Changes.rst -Description: argcomplete - Bash tab completion for argparse - ============================================== - *Tab complete all the things!* - - Argcomplete provides easy, extensible command line tab completion of arguments for your Python script. - - It makes two assumptions: - - * You're using bash as your shell (limited support for zsh, fish, and tcsh is available) - * You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options - - Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can - dynamically suggest completions for your argument/option values (for example, if the user is browsing resources over - the network). - - Installation - ------------ - :: - - pip3 install argcomplete - activate-global-python-argcomplete - - See `Activating global completion`_ below for details about the second step (or if it reports an error). - - Refresh your bash environment (start a new shell or ``source /etc/profile``). - - Synopsis - -------- - Python code (e.g. ``my-awesome-script``): - - .. code-block:: python - - #!/usr/bin/env python - # PYTHON_ARGCOMPLETE_OK - import argcomplete, argparse - parser = argparse.ArgumentParser() - ... - argcomplete.autocomplete(parser) - args = parser.parse_args() - ... - - Shellcode (only necessary if global completion is not activated - see `Global completion`_ below), to be put in e.g. ``.bashrc``:: - - eval "$(register-python-argcomplete my-awesome-script)" - - argcomplete.autocomplete(*parser*) - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - This method is the entry point to the module. It must be called **after** ArgumentParser construction is complete, but - **before** the ``ArgumentParser.parse_args()`` method is called. The method looks for an environment variable that the - completion hook shellcode sets, and if it's there, collects completions, prints them to the output stream (fd 8 by - default), and exits. Otherwise, it returns to the caller immediately. - - .. admonition:: Side effects - - Argcomplete gets completions by running your program. It intercepts the execution flow at the moment - ``argcomplete.autocomplete()`` is called. After sending completions, it exits using ``exit_method`` (``os._exit`` - by default). This means if your program has any side effects that happen before ``argcomplete`` is called, those - side effects will happen every time the user presses ``<TAB>`` (although anything your program prints to stdout or - stderr will be suppressed). For this reason it's best to construct the argument parser and call - ``argcomplete.autocomplete()`` as early as possible in your execution flow. - - .. admonition:: Performance - - If the program takes a long time to get to the point where ``argcomplete.autocomplete()`` is called, the tab completion - process will feel sluggish, and the user may lose confidence in it. So it's also important to minimize the startup time - of the program up to that point (for example, by deferring initialization or importing of large modules until after - parsing options). - - Specifying completers - --------------------- - You can specify custom completion functions for your options and arguments. Two styles are supported: callable and - readline-style. Callable completers are simpler. They are called with the following keyword arguments: - - * ``prefix``: The prefix text of the last word before the cursor on the command line. - For dynamic completers, this can be used to reduce the work required to generate possible completions. - * ``action``: The ``argparse.Action`` instance that this completer was called for. - * ``parser``: The ``argparse.ArgumentParser`` instance that the action was taken by. - * ``parsed_args``: The result of argument parsing so far (the ``argparse.Namespace`` args object normally returned by - ``ArgumentParser.parse_args()``). - - Completers should return their completions as a list of strings. An example completer for names of environment - variables might look like this: - - .. code-block:: python - - def EnvironCompleter(**kwargs): - return os.environ - - To specify a completer for an argument or option, set the ``completer`` attribute of its associated action. An easy - way to do this at definition time is: - - .. code-block:: python - - from argcomplete.completers import EnvironCompleter - - parser = argparse.ArgumentParser() - parser.add_argument("--env-var1").completer = EnvironCompleter - parser.add_argument("--env-var2").completer = EnvironCompleter - argcomplete.autocomplete(parser) - - If you specify the ``choices`` keyword for an argparse option or argument (and don't specify a completer), it will be - used for completions. - - A completer that is initialized with a set of all possible choices of values for its action might look like this: - - .. code-block:: python - - class ChoicesCompleter(object): - def __init__(self, choices): - self.choices = choices - - def __call__(self, **kwargs): - return self.choices - - The following two ways to specify a static set of choices are equivalent for completion purposes: - - .. code-block:: python - - from argcomplete.completers import ChoicesCompleter - - parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss')) - parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss')) - - Note that if you use the ``choices=<completions>`` option, argparse will show - all these choices in the ``--help`` output by default. To prevent this, set - ``metavar`` (like ``parser.add_argument("--protocol", metavar="PROTOCOL", - choices=('http', 'https', 'ssh', 'rsync', 'wss'))``). - - The following `script <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ uses - ``parsed_args`` and `Requests <http://python-requests.org/>`_ to query GitHub for publicly known members of an - organization and complete their names, then prints the member description: - - .. code-block:: python - - #!/usr/bin/env python - # PYTHON_ARGCOMPLETE_OK - import argcomplete, argparse, requests, pprint - - def github_org_members(prefix, parsed_args, **kwargs): - resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization) - return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix)) - - parser = argparse.ArgumentParser() - parser.add_argument("--organization", help="GitHub organization") - parser.add_argument("--member", help="GitHub member").completer = github_org_members - - argcomplete.autocomplete(parser) - args = parser.parse_args() - - pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json()) - - `Try it <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ like this:: - - ./describe_github_user.py --organization heroku --member <TAB> - - If you have a useful completer to add to the `completer library - <https://github.com/kislyuk/argcomplete/blob/master/argcomplete/completers.py>`_, send a pull request! - - Readline-style completers - ~~~~~~~~~~~~~~~~~~~~~~~~~ - The readline_ module defines a completer protocol in rlcompleter_. Readline-style completers are also supported by - argcomplete, so you can use the same completer object both in an interactive readline-powered shell and on the bash - command line. For example, you can use the readline-style completer provided by IPython_ to get introspective - completions like you would get in the IPython shell: - - .. _readline: http://docs.python.org/3/library/readline.html - .. _rlcompleter: http://docs.python.org/3/library/rlcompleter.html#completer-objects - .. _IPython: http://ipython.org/ - - .. code-block:: python - - import IPython - parser.add_argument("--python-name").completer = IPython.core.completer.Completer() - - ``argcomplete.CompletionFinder.rl_complete`` can also be used to plug in an argparse parser as a readline completer. - - Printing warnings in completers - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Normal stdout/stderr output is suspended when argcomplete runs. Sometimes, though, when the user presses ``<TAB>``, it's - appropriate to print information about why completions generation failed. To do this, use ``warn``: - - .. code-block:: python - - from argcomplete import warn - - def AwesomeWebServiceCompleter(prefix, **kwargs): - if login_failed: - warn("Please log in to Awesome Web Service to use autocompletion") - return completions - - Using a custom completion validator - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - By default, argcomplete validates your completions by checking if they start with the prefix given to the completer. You - can override this validation check by supplying the ``validator`` keyword to ``argcomplete.autocomplete()``: - - .. code-block:: python - - def my_validator(current_input, keyword_to_check_against): - # Pass through ALL options even if they don't all start with 'current_input' - return True - - argcomplete.autocomplete(parser, validator=my_validator) - - Global completion - ----------------- - In global completion mode, you don't have to register each argcomplete-capable executable separately. Instead, bash - will look for the string **PYTHON_ARGCOMPLETE_OK** in the first 1024 bytes of any executable that it's running - completion for, and if it's found, follow the rest of the argcomplete protocol as described above. - - Additionally, completion is activated for scripts run as ``python <script>`` and ``python -m <module>``. - This also works for alternate Python versions (e.g. ``python3`` and ``pypy``), as long as that version of Python has - argcomplete installed. - - .. admonition:: Bash version compatibility - - Global completion requires bash support for ``complete -D``, which was introduced in bash 4.2. On OS X or older Linux - systems, you will need to update bash to use this feature. Check the version of the running copy of bash with - ``echo $BASH_VERSION``. On OS X, install bash via `Homebrew <http://brew.sh/>`_ (``brew install bash``), add - ``/usr/local/bin/bash`` to ``/etc/shells``, and run ``chsh`` to change your shell. - - Global completion is not currently compatible with zsh. - - .. note:: If you use setuptools/distribute ``scripts`` or ``entry_points`` directives to package your module, - argcomplete will follow the wrapper scripts to their destination and look for ``PYTHON_ARGCOMPLETE_OK`` in the - destination code. - - If you choose not to use global completion, or ship a bash completion module that depends on argcomplete, you must - register your script explicitly using ``eval "$(register-python-argcomplete my-awesome-script)"``. Standard bash - completion registration roules apply: namely, the script name is passed directly to ``complete``, meaning it is only tab - completed when invoked exactly as it was registered. In the above example, ``my-awesome-script`` must be on the path, - and the user must be attempting to complete it by that name. The above line alone would **not** allow you to complete - ``./my-awesome-script``, or ``/path/to/my-awesome-script``. - - - Activating global completion - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - The script ``activate-global-python-argcomplete`` will try to install the file - ``bash_completion.d/python-argcomplete`` (`see on GitHub`_) into an appropriate location on your system - (``/etc/bash_completion.d/`` or ``~/.bash_completion.d/``). If it - fails, but you know the correct location of your bash completion scripts directory, you can specify it with ``--dest``:: - - activate-global-python-argcomplete --dest=/path/to/bash_completion.d - - Otherwise, you can redirect its shellcode output into a file:: - - activate-global-python-argcomplete --dest=- > file - - The file's contents should then be sourced in e.g. ``~/.bashrc``. - - .. _`see on GitHub`: https://github.com/kislyuk/argcomplete/blob/master/argcomplete/bash_completion.d/python-argcomplete - - Zsh Support - ------------ - To activate completions for zsh you need to have ``bashcompinit`` enabled in zsh:: - - autoload -U bashcompinit - bashcompinit - - Afterwards you can enable completion for your scripts with ``register-python-argcomplete``:: - - eval "$(register-python-argcomplete my-awesome-script)" - - Tcsh Support - ------------ - To activate completions for tcsh use:: - - eval `register-python-argcomplete --shell tcsh my-awesome-script` - - The ``python-argcomplete-tcsh`` script provides completions for tcsh. - The following is an example of the tcsh completion syntax for - ``my-awesome-script`` emitted by ``register-python-argcomplete``:: - - complete my-awesome-script 'p@*@`python-argcomplete-tcsh my-awesome-script`@' - - Fish Support - ------------ - To activate completions for fish use:: - - register-python-argcomplete --shell fish my-awesome-script | source - - or create new completion file, e.g:: - - register-python-argcomplete --shell fish my-awesome-script > ~/.config/fish/completions/my-awesome-script.fish - - Completion Description For Fish - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - By default help string is added as completion description. - - .. image:: docs/fish_help_string.png - - You can disable this feature by removing ``_ARGCOMPLETE_DFS`` variable, e.g:: - - register-python-argcomplete --shell fish my-awesome-script | grep -v _ARGCOMPLETE_DFS | . - - Git Bash Support - ---------------- - Due to limitations of file descriptor inheritance on Windows, - Git Bash not supported out of the box. You can opt in to using - temporary files instead of file descriptors for for IPC - by setting the environment variable ``ARGCOMPLETE_USE_TEMPFILES``, - e.g. by adding ``export ARGCOMPLETE_USE_TEMPFILES=1`` to ``~/.bashrc``. - - For full support, consider using Bash with the - Windows Subsystem for Linux (WSL). - - External argcomplete script - --------------------------- - To register an argcomplete script for an arbitrary name, the ``--external-argcomplete-script`` argument of the ``register-python-argcomplete`` script can be used:: - - eval "$(register-python-argcomplete --external-argcomplete-script /path/to/script arbitrary-name)" - - This allows, for example, to use the auto completion functionality of argcomplete for an application not written in Python. - The command line interface of this program must be additionally implemented in a Python script with argparse and argcomplete and whenever the application is called the registered external argcomplete script is used for auto completion. - - This option can also be used in combination with the other supported shells. - - Python Support - -------------- - Argcomplete requires Python 2.7 or 3.5+. - - Common Problems - --------------- - If global completion is not completing your script, bash may have registered a - default completion function:: - - $ complete | grep my-awesome-script - complete -F _minimal my-awesome-script - - You can fix this by restarting your shell, or by running - ``complete -r my-awesome-script``. - - Debugging - --------- - Set the ``_ARC_DEBUG`` variable in your shell to enable verbose debug output every time argcomplete runs. This will - disrupt the command line composition state of your terminal, but make it possible to see the internal state of the - completer if it encounters problems. - - Acknowledgments - --------------- - Inspired and informed by the optcomplete_ module by Martin Blais. - - .. _optcomplete: http://pypi.python.org/pypi/optcomplete - - Links - ----- - * `Project home page (GitHub) <https://github.com/kislyuk/argcomplete>`_ - * `Documentation <https://kislyuk.github.io/argcomplete/>`_ - * `Package distribution (PyPI) <https://pypi.python.org/pypi/argcomplete>`_ - * `Change log <https://github.com/kislyuk/argcomplete/blob/master/Changes.rst>`_ - * `xontrib-argcomplete <https://github.com/anki-code/xontrib-argcomplete>`_ - support argcomplete in `xonsh <https://github.com/xonsh/xonsh>`_ shell - - Bugs - ~~~~ - Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/kislyuk/argcomplete/issues>`_. - - License - ------- - Licensed under the terms of the `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. - - .. image:: https://github.com/kislyuk/argcomplete/workflows/Python%20package/badge.svg - :target: https://github.com/kislyuk/argcomplete/actions - .. image:: https://codecov.io/github/kislyuk/argcomplete/coverage.svg?branch=master - :target: https://codecov.io/github/kislyuk/argcomplete?branch=master - .. image:: https://img.shields.io/pypi/v/argcomplete.svg - :target: https://pypi.python.org/pypi/argcomplete - .. image:: https://img.shields.io/pypi/l/argcomplete.svg - :target: https://pypi.python.org/pypi/argcomplete - Platform: MacOS X Platform: Posix Classifier: Environment :: Console @@ -386,13 +18,12 @@ Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: POSIX Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Development Status :: 5 - Production/Stable @@ -400,4 +31,390 @@ Classifier: Topic :: Software Development Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: System :: Shells Classifier: Topic :: Terminals +Requires-Python: >=3.6 Provides-Extra: test +License-File: LICENSE.rst + +argcomplete - Bash tab completion for argparse +============================================== +*Tab complete all the things!* + +Argcomplete provides easy, extensible command line tab completion of arguments for your Python script. + +It makes two assumptions: + +* You're using bash as your shell (limited support for zsh, fish, and tcsh is available) +* You're using `argparse <http://docs.python.org/3/library/argparse.html>`_ to manage your command line arguments/options + +Argcomplete is particularly useful if your program has lots of options or subparsers, and if your program can +dynamically suggest completions for your argument/option values (for example, if the user is browsing resources over +the network). + +Installation +------------ +:: + + pip3 install argcomplete + activate-global-python-argcomplete + +See `Activating global completion`_ below for details about the second step (or if it reports an error). + +Refresh your bash environment (start a new shell or ``source /etc/profile``). + +Synopsis +-------- +Python code (e.g. ``my-awesome-script``): + +.. code-block:: python + + #!/usr/bin/env python + # PYTHON_ARGCOMPLETE_OK + import argcomplete, argparse + parser = argparse.ArgumentParser() + ... + argcomplete.autocomplete(parser) + args = parser.parse_args() + ... + +Shellcode (only necessary if global completion is not activated - see `Global completion`_ below), to be put in e.g. ``.bashrc``:: + + eval "$(register-python-argcomplete my-awesome-script)" + +argcomplete.autocomplete(*parser*) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +This method is the entry point to the module. It must be called **after** ArgumentParser construction is complete, but +**before** the ``ArgumentParser.parse_args()`` method is called. The method looks for an environment variable that the +completion hook shellcode sets, and if it's there, collects completions, prints them to the output stream (fd 8 by +default), and exits. Otherwise, it returns to the caller immediately. + +.. admonition:: Side effects + + Argcomplete gets completions by running your program. It intercepts the execution flow at the moment + ``argcomplete.autocomplete()`` is called. After sending completions, it exits using ``exit_method`` (``os._exit`` + by default). This means if your program has any side effects that happen before ``argcomplete`` is called, those + side effects will happen every time the user presses ``<TAB>`` (although anything your program prints to stdout or + stderr will be suppressed). For this reason it's best to construct the argument parser and call + ``argcomplete.autocomplete()`` as early as possible in your execution flow. + +.. admonition:: Performance + + If the program takes a long time to get to the point where ``argcomplete.autocomplete()`` is called, the tab completion + process will feel sluggish, and the user may lose confidence in it. So it's also important to minimize the startup time + of the program up to that point (for example, by deferring initialization or importing of large modules until after + parsing options). + +Specifying completers +--------------------- +You can specify custom completion functions for your options and arguments. Two styles are supported: callable and +readline-style. Callable completers are simpler. They are called with the following keyword arguments: + +* ``prefix``: The prefix text of the last word before the cursor on the command line. + For dynamic completers, this can be used to reduce the work required to generate possible completions. +* ``action``: The ``argparse.Action`` instance that this completer was called for. +* ``parser``: The ``argparse.ArgumentParser`` instance that the action was taken by. +* ``parsed_args``: The result of argument parsing so far (the ``argparse.Namespace`` args object normally returned by + ``ArgumentParser.parse_args()``). + +Completers should return their completions as a list of strings. An example completer for names of environment +variables might look like this: + +.. code-block:: python + + def EnvironCompleter(**kwargs): + return os.environ + +To specify a completer for an argument or option, set the ``completer`` attribute of its associated action. An easy +way to do this at definition time is: + +.. code-block:: python + + from argcomplete.completers import EnvironCompleter + + parser = argparse.ArgumentParser() + parser.add_argument("--env-var1").completer = EnvironCompleter + parser.add_argument("--env-var2").completer = EnvironCompleter + argcomplete.autocomplete(parser) + +If you specify the ``choices`` keyword for an argparse option or argument (and don't specify a completer), it will be +used for completions. + +A completer that is initialized with a set of all possible choices of values for its action might look like this: + +.. code-block:: python + + class ChoicesCompleter(object): + def __init__(self, choices): + self.choices = choices + + def __call__(self, **kwargs): + return self.choices + +The following two ways to specify a static set of choices are equivalent for completion purposes: + +.. code-block:: python + + from argcomplete.completers import ChoicesCompleter + + parser.add_argument("--protocol", choices=('http', 'https', 'ssh', 'rsync', 'wss')) + parser.add_argument("--proto").completer=ChoicesCompleter(('http', 'https', 'ssh', 'rsync', 'wss')) + +Note that if you use the ``choices=<completions>`` option, argparse will show +all these choices in the ``--help`` output by default. To prevent this, set +``metavar`` (like ``parser.add_argument("--protocol", metavar="PROTOCOL", +choices=('http', 'https', 'ssh', 'rsync', 'wss'))``). + +The following `script <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ uses +``parsed_args`` and `Requests <http://python-requests.org/>`_ to query GitHub for publicly known members of an +organization and complete their names, then prints the member description: + +.. code-block:: python + + #!/usr/bin/env python + # PYTHON_ARGCOMPLETE_OK + import argcomplete, argparse, requests, pprint + + def github_org_members(prefix, parsed_args, **kwargs): + resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization) + return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix)) + + parser = argparse.ArgumentParser() + parser.add_argument("--organization", help="GitHub organization") + parser.add_argument("--member", help="GitHub member").completer = github_org_members + + argcomplete.autocomplete(parser) + args = parser.parse_args() + + pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json()) + +`Try it <https://raw.github.com/kislyuk/argcomplete/master/docs/examples/describe_github_user.py>`_ like this:: + + ./describe_github_user.py --organization heroku --member <TAB> + +If you have a useful completer to add to the `completer library +<https://github.com/kislyuk/argcomplete/blob/master/argcomplete/completers.py>`_, send a pull request! + +Readline-style completers +~~~~~~~~~~~~~~~~~~~~~~~~~ +The readline_ module defines a completer protocol in rlcompleter_. Readline-style completers are also supported by +argcomplete, so you can use the same completer object both in an interactive readline-powered shell and on the bash +command line. For example, you can use the readline-style completer provided by IPython_ to get introspective +completions like you would get in the IPython shell: + +.. _readline: http://docs.python.org/3/library/readline.html +.. _rlcompleter: http://docs.python.org/3/library/rlcompleter.html#completer-objects +.. _IPython: http://ipython.org/ + +.. code-block:: python + + import IPython + parser.add_argument("--python-name").completer = IPython.core.completer.Completer() + +``argcomplete.CompletionFinder.rl_complete`` can also be used to plug in an argparse parser as a readline completer. + +Printing warnings in completers +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Normal stdout/stderr output is suspended when argcomplete runs. Sometimes, though, when the user presses ``<TAB>``, it's +appropriate to print information about why completions generation failed. To do this, use ``warn``: + +.. code-block:: python + + from argcomplete import warn + + def AwesomeWebServiceCompleter(prefix, **kwargs): + if login_failed: + warn("Please log in to Awesome Web Service to use autocompletion") + return completions + +Using a custom completion validator +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +By default, argcomplete validates your completions by checking if they start with the prefix given to the completer. You +can override this validation check by supplying the ``validator`` keyword to ``argcomplete.autocomplete()``: + +.. code-block:: python + + def my_validator(current_input, keyword_to_check_against): + # Pass through ALL options even if they don't all start with 'current_input' + return True + + argcomplete.autocomplete(parser, validator=my_validator) + +Global completion +----------------- +In global completion mode, you don't have to register each argcomplete-capable executable separately. Instead, bash +will look for the string **PYTHON_ARGCOMPLETE_OK** in the first 1024 bytes of any executable that it's running +completion for, and if it's found, follow the rest of the argcomplete protocol as described above. + +Additionally, completion is activated for scripts run as ``python <script>`` and ``python -m <module>``. +This also works for alternate Python versions (e.g. ``python3`` and ``pypy``), as long as that version of Python has +argcomplete installed. + +.. admonition:: Bash version compatibility + + Global completion requires bash support for ``complete -D``, which was introduced in bash 4.2. On OS X or older Linux + systems, you will need to update bash to use this feature. Check the version of the running copy of bash with + ``echo $BASH_VERSION``. On OS X, install bash via `Homebrew <http://brew.sh/>`_ (``brew install bash``), add + ``/usr/local/bin/bash`` to ``/etc/shells``, and run ``chsh`` to change your shell. + + Global completion is not currently compatible with zsh. + +.. note:: If you use setuptools/distribute ``scripts`` or ``entry_points`` directives to package your module, + argcomplete will follow the wrapper scripts to their destination and look for ``PYTHON_ARGCOMPLETE_OK`` in the + destination code. + +If you choose not to use global completion, or ship a bash completion module that depends on argcomplete, you must +register your script explicitly using ``eval "$(register-python-argcomplete my-awesome-script)"``. Standard bash +completion registration roules apply: namely, the script name is passed directly to ``complete``, meaning it is only tab +completed when invoked exactly as it was registered. In the above example, ``my-awesome-script`` must be on the path, +and the user must be attempting to complete it by that name. The above line alone would **not** allow you to complete +``./my-awesome-script``, or ``/path/to/my-awesome-script``. + + +Activating global completion +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The script ``activate-global-python-argcomplete`` will try to install the file +``bash_completion.d/python-argcomplete`` (`see on GitHub`_) into an appropriate location on your system +(``/etc/bash_completion.d/`` or ``~/.bash_completion.d/``). If it +fails, but you know the correct location of your bash completion scripts directory, you can specify it with ``--dest``:: + + activate-global-python-argcomplete --dest=/path/to/bash_completion.d + +Otherwise, you can redirect its shellcode output into a file:: + + activate-global-python-argcomplete --dest=- > file + +The file's contents should then be sourced in e.g. ``~/.bashrc``. + +.. _`see on GitHub`: https://github.com/kislyuk/argcomplete/blob/master/argcomplete/bash_completion.d/python-argcomplete + +Zsh Support +------------ +To activate completions for zsh you need to have ``bashcompinit`` enabled in zsh:: + + autoload -U bashcompinit + bashcompinit + +Afterwards you can enable completion for your scripts with ``register-python-argcomplete``:: + + eval "$(register-python-argcomplete my-awesome-script)" + +Tcsh Support +------------ +To activate completions for tcsh use:: + + eval `register-python-argcomplete --shell tcsh my-awesome-script` + +The ``python-argcomplete-tcsh`` script provides completions for tcsh. +The following is an example of the tcsh completion syntax for +``my-awesome-script`` emitted by ``register-python-argcomplete``:: + + complete my-awesome-script 'p@*@`python-argcomplete-tcsh my-awesome-script`@' + +Fish Support +------------ +To activate completions for fish use:: + + register-python-argcomplete --shell fish my-awesome-script | source + +or create new completion file, e.g:: + + register-python-argcomplete --shell fish my-awesome-script > ~/.config/fish/completions/my-awesome-script.fish + +Completion Description For Fish +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +By default help string is added as completion description. + +.. image:: docs/fish_help_string.png + +You can disable this feature by removing ``_ARGCOMPLETE_DFS`` variable, e.g:: + + register-python-argcomplete --shell fish my-awesome-script | grep -v _ARGCOMPLETE_DFS | source + +Absolute Path Completion +~~~~~~~~~~~~~~~~~~~~~~~~ +If script is not in path you still can register it's completion by specifying absolute path:: + + register-python-argcomplete --shell fish /home/awesome-user/my-awesome-script | source + +then you can complete it by using ``/home/awesome-user/my-awesome-script`` or ``./my-awesome-script``. + +Unfortunately ``~/my-awesome-script`` would not work, to fix it you can run:: + + register-python-argcomplete --shell fish my-awesome-script -e /home/awesome-user/my-awesome-script | source + +This would enable completion for any ``my-awesome-script`` in any location. + +Git Bash Support +---------------- +Due to limitations of file descriptor inheritance on Windows, +Git Bash not supported out of the box. You can opt in to using +temporary files instead of file descriptors for for IPC +by setting the environment variable ``ARGCOMPLETE_USE_TEMPFILES``, +e.g. by adding ``export ARGCOMPLETE_USE_TEMPFILES=1`` to ``~/.bashrc``. + +For full support, consider using Bash with the +Windows Subsystem for Linux (WSL). + +External argcomplete script +--------------------------- +To register an argcomplete script for an arbitrary name, the ``--external-argcomplete-script`` argument of the ``register-python-argcomplete`` script can be used:: + + eval "$(register-python-argcomplete --external-argcomplete-script /path/to/script arbitrary-name)" + +This allows, for example, to use the auto completion functionality of argcomplete for an application not written in Python. +The command line interface of this program must be additionally implemented in a Python script with argparse and argcomplete and whenever the application is called the registered external argcomplete script is used for auto completion. + +This option can also be used in combination with the other supported shells. + +Python Support +-------------- +Argcomplete requires Python 3.6+. + +Common Problems +--------------- +If global completion is not completing your script, bash may have registered a +default completion function:: + + $ complete | grep my-awesome-script + complete -F _minimal my-awesome-script + +You can fix this by restarting your shell, or by running +``complete -r my-awesome-script``. + +Debugging +--------- +Set the ``_ARC_DEBUG`` variable in your shell to enable verbose debug output every time argcomplete runs. This will +disrupt the command line composition state of your terminal, but make it possible to see the internal state of the +completer if it encounters problems. + +Acknowledgments +--------------- +Inspired and informed by the optcomplete_ module by Martin Blais. + +.. _optcomplete: http://pypi.python.org/pypi/optcomplete + +Links +----- +* `Project home page (GitHub) <https://github.com/kislyuk/argcomplete>`_ +* `Documentation <https://kislyuk.github.io/argcomplete/>`_ +* `Package distribution (PyPI) <https://pypi.python.org/pypi/argcomplete>`_ +* `Change log <https://github.com/kislyuk/argcomplete/blob/master/Changes.rst>`_ +* `xontrib-argcomplete <https://github.com/anki-code/xontrib-argcomplete>`_ - support argcomplete in `xonsh <https://github.com/xonsh/xonsh>`_ shell + +Bugs +~~~~ +Please report bugs, issues, feature requests, etc. on `GitHub <https://github.com/kislyuk/argcomplete/issues>`_. + +License +------- +Licensed under the terms of the `Apache License, Version 2.0 <http://www.apache.org/licenses/LICENSE-2.0>`_. + +.. image:: https://github.com/kislyuk/argcomplete/workflows/Python%20package/badge.svg + :target: https://github.com/kislyuk/argcomplete/actions +.. image:: https://codecov.io/github/kislyuk/argcomplete/coverage.svg?branch=master + :target: https://codecov.io/github/kislyuk/argcomplete?branch=master +.. image:: https://img.shields.io/pypi/v/argcomplete.svg + :target: https://pypi.python.org/pypi/argcomplete +.. image:: https://img.shields.io/pypi/l/argcomplete.svg + :target: https://pypi.python.org/pypi/argcomplete + + diff --git a/argcomplete.egg-info/SOURCES.txt b/argcomplete.egg-info/SOURCES.txt index caf2abe..e2ac453 100644 --- a/argcomplete.egg-info/SOURCES.txt +++ b/argcomplete.egg-info/SOURCES.txt @@ -1,20 +1,13 @@ -.gitignore Authors.rst Changes.rst LICENSE.rst MANIFEST.in -Makefile README.rst -common.mk setup.cfg setup.py -tox.ini -.github/FUNDING.yml -.github/workflows/pythonpackage.yml argcomplete/__init__.py argcomplete/_check_console_script.py argcomplete/_check_module.py -argcomplete/compat.py argcomplete/completers.py argcomplete/my_argparse.py argcomplete/my_shlex.py @@ -26,10 +19,6 @@ argcomplete.egg-info/not-zip-safe argcomplete.egg-info/requires.txt argcomplete.egg-info/top_level.txt argcomplete/bash_completion.d/python-argcomplete -docs/conf.py -docs/fish_help_string.png -docs/index.rst -docs/examples/describe_github_user.py scripts/activate-global-python-argcomplete scripts/python-argcomplete-check-easy-install-script scripts/python-argcomplete-tcsh diff --git a/argcomplete.egg-info/requires.txt b/argcomplete.egg-info/requires.txt index 833bfac..04a9239 100644 --- a/argcomplete.egg-info/requires.txt +++ b/argcomplete.egg-info/requires.txt @@ -1,10 +1,4 @@ -[:python_version == "2.7"] -importlib-metadata<5,>=0.23 - -[:python_version == "3.5"] -importlib-metadata<5,>=0.23 - [:python_version == "3.6"] importlib-metadata<5,>=0.23 diff --git a/argcomplete/__init__.py b/argcomplete/__init__.py index c2c8390..0b7925b 100644 --- a/argcomplete/__init__.py +++ b/argcomplete/__init__.py @@ -1,14 +1,11 @@ -# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors. +# Copyright 2012-2021, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. -from __future__ import absolute_import, division, print_function, unicode_literals - import os, sys, argparse, contextlib from . import completers, my_shlex as shlex -from .compat import USING_PYTHON2, str, sys_encoding, ensure_str, ensure_bytes from .completers import FilesCompleter, SuppressCompleter from .my_argparse import IntrospectiveArgumentParser, action_is_satisfied, action_is_open, action_is_greedy -from .shell_integration import shellcode +from .shell_integration import shellcode # noqa _DEBUG = "_ARC_DEBUG" in os.environ @@ -16,13 +13,9 @@ debug_stream = sys.stderr def debug(*args): if _DEBUG: - if USING_PYTHON2: - # debug_stream has to be binary mode in Python 2. - # Attempting to write unicode directly uses the default ascii conversion. - # Convert any unicode to bytes, leaving non-string input alone. - args = [ensure_bytes(x) if isinstance(x, str) else x for x in args] print(file=debug_stream, *args) + BASH_FILE_COMPLETION_FALLBACK = 79 BASH_DIR_COMPLETION_FALLBACK = 80 @@ -59,6 +52,7 @@ class ArgcompleteException(Exception): def split_line(line, point=None): if point is None: point = len(line) + line = line[:point] lexer = shlex.shlex(line, posix=True) lexer.whitespace_split = True lexer.wordbreaks = os.environ.get("_ARGCOMPLETE_COMP_WORDBREAKS", "") @@ -180,7 +174,7 @@ class CompletionFinder(object): global debug_stream try: debug_stream = os.fdopen(9, "w") - except: + except Exception: debug_stream = sys.stderr debug() @@ -188,12 +182,12 @@ class CompletionFinder(object): filename = os.environ.get("_ARGCOMPLETE_STDOUT_FILENAME") if filename is not None: debug("Using output file {}".format(filename)) - output_stream = open(filename, "wb") + output_stream = open(filename, "w") if output_stream is None: try: - output_stream = os.fdopen(8, "wb") - except: + output_stream = os.fdopen(8, "w") + except Exception: debug("Unable to open fd 8 for writing, quitting") exit_method(1) @@ -214,7 +208,6 @@ class CompletionFinder(object): comp_line = os.environ["COMP_LINE"] comp_point = int(os.environ["COMP_POINT"]) - comp_line = ensure_str(comp_line) 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 @@ -245,7 +238,7 @@ class CompletionFinder(object): completions = [dfs.join((key, display_completions.get(key) or "")) for key in completions] debug("\nReturning completions:", completions) - output_stream.write(ifs.join(completions).encode(sys_encoding)) + output_stream.write(ifs.join(completions)) output_stream.flush() debug_stream.flush() exit_method(0) @@ -256,10 +249,6 @@ class CompletionFinder(object): parsed_args = argparse.Namespace() self.completing = True - if USING_PYTHON2: - # Python 2 argparse only properly works with byte strings. - comp_words = [ensure_bytes(word) for word in comp_words] - try: debug("invoking parser with", comp_words[1:]) with mute_stderr(): @@ -298,8 +287,7 @@ class CompletionFinder(object): return classname = "MonkeyPatchedIntrospectiveArgumentParser" - if USING_PYTHON2: - classname = bytes(classname) + parser.__class__ = type(classname, (IntrospectiveArgumentParser, parser.__class__), {}) for action in parser._actions: @@ -357,9 +345,9 @@ class CompletionFinder(object): def _include_options(self, action, cword_prefix): if len(cword_prefix) > 0 or self.always_complete_options is True: - return [ensure_str(opt) for opt in action.option_strings if ensure_str(opt).startswith(cword_prefix)] - long_opts = [ensure_str(opt) for opt in action.option_strings if len(opt) > 2] - short_opts = [ensure_str(opt) for opt in action.option_strings if len(opt) <= 2] + return [opt for opt in action.option_strings if opt.startswith(cword_prefix)] + long_opts = [opt for opt in action.option_strings if len(opt) > 2] + short_opts = [opt for opt in action.option_strings if len(opt) <= 2] if self.always_complete_options == "long": return long_opts if long_opts else short_opts elif self.always_complete_options == "short": @@ -368,8 +356,8 @@ class CompletionFinder(object): def _get_option_completions(self, parser, cword_prefix): self._display_completions.update( - [[tuple(ensure_str(x) for x in action.option_strings - if ensure_str(x).startswith(cword_prefix)), action.help] + [[tuple(x for x in action.option_strings + if x.startswith(cword_prefix)), action.help] for action in parser._actions if action.option_strings]) @@ -536,11 +524,6 @@ class CompletionFinder(object): This method is exposed for overriding in subclasses; there is no need to use it directly. """ - # On Python 2, we have to make sure all completions are unicode objects before we continue and output them. - # Otherwise, because python disobeys the system locale encoding and uses ascii as the default encoding, it will - # try to implicitly decode string objects using ascii, and fail. - completions = [ensure_str(c) for c in completions] - # De-duplicate completions and remove excluded ones if self.exclude is None: self.exclude = set() @@ -598,7 +581,7 @@ class CompletionFinder(object): def rl_complete(self, text, state): """ Alternate entry point for using the argcomplete completer in a readline-based REPL. See also - `rlcompleter <https://docs.python.org/2/library/rlcompleter.html#completer-objects>`_. + `rlcompleter <https://docs.python.org/3/library/rlcompleter.html#completer-objects>`_. Usage: .. code-block:: python @@ -611,8 +594,6 @@ class CompletionFinder(object): readline.set_completer(completer.rl_complete) readline.parse_and_bind("tab: complete") result = input("prompt> ") - - (Use ``raw_input`` instead of ``input`` on Python 2, or use `eight <https://github.com/kislyuk/eight>`_). """ if state == 0: cword_prequote, cword_prefix, cword_suffix, comp_words, first_colon_pos = split_line(text) @@ -673,6 +654,7 @@ class ExclusiveCompletionFinder(CompletionFinder): return False + autocomplete = CompletionFinder() autocomplete.__doc__ = """ Use this to access argcomplete. See :meth:`argcomplete.CompletionFinder.__call__()`. """ diff --git a/argcomplete/bash_completion.d/python-argcomplete b/argcomplete/bash_completion.d/python-argcomplete index c776992..86e7815 100644 --- a/argcomplete/bash_completion.d/python-argcomplete +++ b/argcomplete/bash_completion.d/python-argcomplete @@ -1,4 +1,4 @@ -# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors. +# Copyright 2012-2021, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. # Copy of __expand_tilde_by_ref from bash-completion diff --git a/argcomplete/compat.py b/argcomplete/compat.py deleted file mode 100644 index 36b1a6d..0000000 --- a/argcomplete/compat.py +++ /dev/null @@ -1,25 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals - -import locale -import sys - -sys_encoding = locale.getpreferredencoding() - -USING_PYTHON2 = True if sys.version_info < (3, 0) else False - -if USING_PYTHON2: - str = unicode # noqa -else: - str = str - - -def ensure_bytes(x, encoding=sys_encoding): - if not isinstance(x, bytes): - x = x.encode(encoding) - return x - - -def ensure_str(x, encoding=sys_encoding): - if not isinstance(x, str): - x = x.decode(encoding) - return x diff --git a/argcomplete/completers.py b/argcomplete/completers.py index 3752005..b53878e 100644 --- a/argcomplete/completers.py +++ b/argcomplete/completers.py @@ -1,15 +1,14 @@ -# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors. +# Copyright 2012-2021, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. -from __future__ import absolute_import, division, print_function, unicode_literals - import os import subprocess -from .compat import str, sys_encoding def _call(*args, **kwargs): + # TODO: replace "universal_newlines" with "text" once 3.6 support is dropped + kwargs["universal_newlines"] = True try: - return subprocess.check_output(*args, **kwargs).decode(sys_encoding).splitlines() + return subprocess.check_output(*args, **kwargs).splitlines() except subprocess.CalledProcessError: return [] @@ -18,8 +17,6 @@ class ChoicesCompleter(object): self.choices = choices def _convert(self, choice): - if isinstance(choice, bytes): - choice = choice.decode(sys_encoding) if not isinstance(choice, str): choice = str(choice) return choice @@ -27,6 +24,7 @@ class ChoicesCompleter(object): def __call__(self, **kwargs): return (self._convert(c) for c in self.choices) + EnvironCompleter = ChoicesCompleter(os.environ) class FilesCompleter(object): @@ -77,7 +75,7 @@ class _FilteredFilesCompleter(object): target_dir = os.path.dirname(prefix) try: names = os.listdir(target_dir or ".") - except: + except Exception: return # empty iterator incomplete_part = os.path.basename(prefix) # Iterate on target_dir entries and filter on given predicate diff --git a/argcomplete/my_argparse.py b/argcomplete/my_argparse.py index 19bd24c..c7603f5 100644 --- a/argcomplete/my_argparse.py +++ b/argcomplete/my_argparse.py @@ -1,4 +1,4 @@ -# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors. +# Copyright 2012-2021, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. from argparse import ArgumentParser, ArgumentError, SUPPRESS, _SubParsersAction @@ -134,7 +134,7 @@ class IntrospectiveArgumentParser(ArgumentParser): or isinstance(action, _SubParsersAction): try: action(self, namespace, argument_values, option_string) - except: + except BaseException: # Begin added by argcomplete # When a subparser action is taken and fails due to incomplete arguments, it does not merge the # contents of its parsed namespace into the parent namespace. Do that here to allow completers to diff --git a/argcomplete/shell_integration.py b/argcomplete/shell_integration.py index f068ff9..f6f23bc 100644 --- a/argcomplete/shell_integration.py +++ b/argcomplete/shell_integration.py @@ -58,7 +58,7 @@ complete "%(executable)s" 'p@*@`python-argcomplete-tcsh "%(argcomplete_script)s" ''' fishcode = r''' -function __fish_%(executable)s_complete +function __fish_%(function_name)s_complete set -x _ARGCOMPLETE 1 set -x _ARGCOMPLETE_DFS \t set -x _ARGCOMPLETE_IFS \n @@ -73,7 +73,7 @@ function __fish_%(executable)s_complete %(argcomplete_script)s 8>&1 9>&2 1>/dev/null 2>&1 end end -complete -c %(executable)s -f -a '(__fish_%(executable)s_complete)' +complete %(completion_arg)s %(executable)s -f -a '(__fish_%(function_name)s_complete)' ''' shell_codes = {'bash': bashcode, 'tcsh': tcshcode, 'fish': fishcode} @@ -109,6 +109,15 @@ def shellcode(executables, use_defaults=True, shell='bash', complete_arguments=N function_suffix = '' code = bashcode % dict(complete_opts=complete_options, executables=executables_list, argcomplete_script=script, function_suffix=function_suffix) + elif shell == 'fish': + code = "" + for executable in executables: + script = argcomplete_script or executable + completion_arg = '--path' if '/' in executable else '--command' # use path for absolute paths + function_name = executable.replace("/", "_") # / not allowed in function name + + code += fishcode % dict(executable=executable, argcomplete_script=script, + completion_arg=completion_arg, function_name=function_name) else: code = "" for executable in executables: diff --git a/common.mk b/common.mk deleted file mode 100644 index d2d8dab..0000000 --- a/common.mk +++ /dev/null @@ -1,62 +0,0 @@ -SHELL=/bin/bash -eo pipefail - -release-major: - $(eval export TAG=$(shell git describe --tags --match 'v*.*.*' | perl -ne '/^v(\d+)\.(\d+)\.(\d+)/; print "v@{[$$1+1]}.0.0"')) - $(MAKE) release - -release-minor: - $(eval export TAG=$(shell git describe --tags --match 'v*.*.*' | perl -ne '/^v(\d+)\.(\d+)\.(\d+)/; print "v$$1.@{[$$2+1]}.0"')) - $(MAKE) release - -release-patch: - $(eval export TAG=$(shell git describe --tags --match 'v*.*.*' | perl -ne '/^v(\d+)\.(\d+)\.(\d+)/; print "v$$1.$$2.@{[$$3+1]}"')) - $(MAKE) release - -release: - @if ! git diff --cached --exit-code; then echo "Commit staged files before proceeding"; exit 1; fi - @if [[ -z $$TAG ]]; then echo "Use release-{major,minor,patch}"; exit 1; fi - @if ! type -P pandoc; then echo "Please install pandoc"; exit 1; fi - @if ! type -P sponge; then echo "Please install moreutils"; exit 1; fi - @if ! type -P http; then echo "Please install httpie"; exit 1; fi - @if ! type -P twine; then echo "Please install twine"; exit 1; fi - $(eval REMOTE=$(shell git remote get-url origin | perl -ne '/([^\/\:]+\/.+?)(\.git)?$$/; print $$1')) - $(eval GIT_USER=$(shell git config --get user.email)) - $(eval GH_AUTH=$(shell if grep -q '@github.com' ~/.git-credentials; then echo $$(grep '@github.com' ~/.git-credentials | python3 -c 'import sys, urllib.parse as p; print(p.urlparse(sys.stdin.read()).netloc.split("@")[0])'); else echo $(GIT_USER); fi)) - $(eval RELEASES_API=https://api.github.com/repos/${REMOTE}/releases) - $(eval UPLOADS_API=https://uploads.github.com/repos/${REMOTE}/releases) - git pull -# git clean -x --force $$(python setup.py --name) -# sed -i -e "s/version=\([\'\"]\)[0-9]*\.[0-9]*\.[0-9]*/version=\1$${TAG:1}/" setup.py -# git add setup.py -# TAG_MSG=$$(mktemp); \ - echo "# Changes for ${TAG} ($$(date +%Y-%m-%d))" > $$TAG_MSG; \ - git log --pretty=format:%s $$(git describe --abbrev=0)..HEAD >> $$TAG_MSG; \ - $${EDITOR:-emacs} $$TAG_MSG; \ - if [[ -f Changes.md ]]; then cat $$TAG_MSG <(echo) Changes.md | sponge Changes.md; git add Changes.md; fi; \ - if [[ -f Changes.rst ]]; then cat <(pandoc --from markdown --to rst $$TAG_MSG) <(echo) Changes.rst | sponge Changes.rst; git add Changes.rst; fi; \ - git commit -m ${TAG}; \ - git tag --sign --annotate --file $$TAG_MSG ${TAG} -# git push --follow-tags -# http --check-status --auth ${GH_AUTH} ${RELEASES_API} tag_name=${TAG} name=${TAG} \ - body="$$(git tag --list ${TAG} -n99 | perl -pe 's/^\S+\s*// if $$. == 1' | sed 's/^\s\s\s\s//')" -# $(MAKE) install - http --check-status --auth ${GH_AUTH} POST ${UPLOADS_API}/$$(http --auth ${GH_AUTH} ${RELEASES_API}/latest | jq .id)/assets \ - name==$$(basename dist/*.whl) label=="Python Wheel" < dist/*.whl - $(MAKE) release-pypi - $(MAKE) release-docs - -release-pypi: - python setup.py sdist bdist_wheel - twine upload dist/*.tar.gz dist/*.whl --sign --verbose - -release-docs: - $(MAKE) docs - -git branch -D gh-pages - git checkout -B gh-pages-stage - touch docs/html/.nojekyll - git add --force docs/html - git commit -m "Docs for ${TAG}" - git push --force origin $$(git subtree split --prefix docs/html --branch gh-pages):refs/heads/gh-pages - git checkout - - -.PHONY: release diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 8595cf5..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,33 +0,0 @@ -import os - -project = "argcomplete" -copyright = "Andrey Kislyuk and argcomplete contributors" -author = "Andrey Kislyuk" -version = "" -release = "" -language = None -master_doc = "index" -extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode"] -source_suffix = [".rst", ".md"] -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] -pygments_style = "sphinx" - -if "readthedocs.org" in os.getcwd().split("/"): - with open("index.rst", "w") as fh: - fh.write("Documentation for this project has moved to https://kislyuk.github.io/" + project) -else: - import guzzle_sphinx_theme - html_theme_path = guzzle_sphinx_theme.html_theme_path() - html_theme = "guzzle_sphinx_theme" - html_theme_options = { - "project_nav_name": project, - "projectlink": "https://github.com/kislyuk/" + project, - } - html_sidebars = { - "**": [ - "logo-text.html", - # "globaltoc.html", - "localtoc.html", - "searchbox.html" - ] - } diff --git a/docs/examples/describe_github_user.py b/docs/examples/describe_github_user.py deleted file mode 100755 index 7e2db03..0000000 --- a/docs/examples/describe_github_user.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python -# PYTHON_ARGCOMPLETE_OK -import argcomplete, argparse, requests, pprint - -def github_org_members(prefix, parsed_args, **kwargs): - resource = "https://api.github.com/orgs/{org}/members".format(org=parsed_args.organization) - return (member['login'] for member in requests.get(resource).json() if member['login'].startswith(prefix)) - -parser = argparse.ArgumentParser() -parser.add_argument("--organization", help="GitHub organization") -parser.add_argument("--member", help="GitHub member").completer = github_org_members - -argcomplete.autocomplete(parser) -args = parser.parse_args() - -pprint.pprint(requests.get("https://api.github.com/users/{m}".format(m=args.member)).json()) diff --git a/docs/fish_help_string.png b/docs/fish_help_string.png deleted file mode 100644 index 98c59d3fedaf12f74b8c54e92a853ec47294d497..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14171 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 diff --git a/docs/index.rst b/docs/index.rst deleted file mode 100644 index 64992bf..0000000 --- a/docs/index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. include:: ../README.rst - -API documentation -================= - -.. autofunction:: argcomplete.autocomplete - -.. automodule:: argcomplete - :members: - :special-members: - :exclude-members: __weakref__ diff --git a/scripts/activate-global-python-argcomplete b/scripts/activate-global-python-argcomplete index 02d10e7..9361ebb 100755 --- a/scripts/activate-global-python-argcomplete +++ b/scripts/activate-global-python-argcomplete @@ -1,7 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK -# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors. +# Copyright 2012-2021, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. ''' @@ -49,9 +49,10 @@ def replaceCompleteCall(line): else: return line + if args.dest == '-': - for l in open(activator): - sys.stdout.write(replaceCompleteCall(l)) + for line in open(activator): + sys.stdout.write(replaceCompleteCall(line)) else: dest = os.path.join(args.dest, "python-argcomplete") @@ -65,9 +66,9 @@ else: try: shutil.copy(activator, dest) if args.complete_arguments or not args.use_defaults: - for l in fileinput.input(dest, inplace=True): + for line in fileinput.input(dest, inplace=True): # fileinput with inplace=True redirects stdout to the edited file - sys.stdout.write(replaceCompleteCall(l)) + sys.stdout.write(replaceCompleteCall(line)) except Exception as e: err = str(e) if args.dest == dest_opt.default: diff --git a/scripts/python-argcomplete-check-easy-install-script b/scripts/python-argcomplete-check-easy-install-script index f25b279..a036e73 100755 --- a/scripts/python-argcomplete-check-easy-install-script +++ b/scripts/python-argcomplete-check-easy-install-script @@ -1,6 +1,6 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 -# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors. +# Copyright 2012-2021, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. ''' diff --git a/scripts/register-python-argcomplete b/scripts/register-python-argcomplete index 78ba99f..624b3fc 100755 --- a/scripts/register-python-argcomplete +++ b/scripts/register-python-argcomplete @@ -1,7 +1,7 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK -# Copyright 2012-2019, Andrey Kislyuk and argcomplete contributors. +# Copyright 2012-2021, Andrey Kislyuk and argcomplete contributors. # Licensed under the Apache License. See https://github.com/kislyuk/argcomplete for more info. ''' diff --git a/setup.cfg b/setup.cfg index 61dc802..08909a2 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,7 +10,7 @@ universal = 1 [flake8] max-line-length = 120 -ignore = E301, E302, E305, E401, F401, E722, E741 +ignore = E302, E401 exclude = my_shlex.py [egg_info] diff --git a/setup.py b/setup.py index 4e5da72..cea6834 100755 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ importlib_backport_requires = ["importlib-metadata >= 0.23, < 5"] setup( name='argcomplete', - version='1.12.3', + version='2.0.0', url='https://github.com/kislyuk/argcomplete', project_urls={ "Documentation": "https://kislyuk.github.io/argcomplete", @@ -22,12 +22,11 @@ setup( author_email='kislyuk@gmail.com', description='Bash tab completion for argparse', long_description=open('README.rst').read(), + python_requires='>=3.6', install_requires=install_requires, tests_require=tests_require, extras_require={ "test": tests_require, - ':python_version == "2.7"': importlib_backport_requires, - ':python_version == "3.5"': importlib_backport_requires, ':python_version == "3.6"': importlib_backport_requires, ':python_version == "3.7"': importlib_backport_requires }, @@ -45,13 +44,12 @@ setup( 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX', 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Development Status :: 5 - Production/Stable', diff --git a/test/prog b/test/prog index 076b10b..ab41dda 100755 --- a/test/prog +++ b/test/prog @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK """Test script used by test.TestBash.""" from __future__ import print_function diff --git a/test/test.py b/test/test.py index 02fd6e1..5c48611 100755 --- a/test/test.py +++ b/test/test.py @@ -6,13 +6,7 @@ from __future__ import absolute_import, division, print_function, unicode_litera import os, sys, shutil, argparse, subprocess, unittest, contextlib import pexpect, pexpect.replwrap from tempfile import TemporaryFile, NamedTemporaryFile, mkdtemp - -try: - # Python 2 - from cStringIO import StringIO -except ImportError: - # Python 3 - from io import StringIO +from io import StringIO TEST_DIR = os.path.abspath(os.path.dirname(__file__)) # noqa BASE_DIR = os.path.dirname(TEST_DIR) # noqa @@ -30,7 +24,6 @@ from argcomplete import ( warn ) from argcomplete.completers import FilesCompleter, DirectoriesCompleter, SuppressCompleter -from argcomplete.compat import USING_PYTHON2, str, sys_encoding, ensure_str, ensure_bytes IFS = "\013" COMP_WORDBREAKS = " \t\n\"'><=;|&(:" @@ -78,19 +71,17 @@ class TestArgcomplete(unittest.TestCase): os.environ = self._os_environ def run_completer(self, parser, command, point=None, completer=autocomplete, **kwargs): - command = ensure_str(command) - if point is None: point = str(len(command)) - with TemporaryFile() as t: - os.environ["COMP_LINE"] = ensure_bytes(command) if USING_PYTHON2 else command + with TemporaryFile(mode="w+") as t: + os.environ["COMP_LINE"] = command os.environ["COMP_POINT"] = point with self.assertRaises(SystemExit) as cm: completer(parser, output_stream=t, exit_method=sys.exit, **kwargs) if cm.exception.code != 0: raise Exception("Unexpected exit code %d" % cm.exception.code) t.seek(0) - return t.read().decode(sys_encoding).split(IFS) + return t.read().split(IFS) def test_basic_completion(self): p = ArgumentParser() @@ -112,7 +103,7 @@ class TestArgcomplete(unittest.TestCase): def test_choices(self): def make_parser(): parser = ArgumentParser() - parser.add_argument("--ship", choices=["submarine", b"speedboat"]) + parser.add_argument("--ship", choices=["submarine", "speedboat"]) return parser expected_outputs = ( @@ -396,14 +387,11 @@ class TestArgcomplete(unittest.TestCase): def test_non_ascii(self): def make_parser(): - _str = ensure_bytes if USING_PYTHON2 else str parser = ArgumentParser() - # Python 2 argparse only works with byte strings or ascii unicode strings. - # Python 3 argparse only works with unicode strings. - parser.add_argument(_str("--книга"), choices=[ - _str("Трудно быть богом"), - _str("Парень из преиÑподней"), - _str("Понедельник начинаетÑÑ Ð² Ñубботу"), + parser.add_argument("--книга", choices=[ + "Трудно быть богом", + "Парень из преиÑподней", + "Понедельник начинаетÑÑ Ð² Ñубботу", ]) return parser @@ -517,7 +505,6 @@ class TestArgcomplete(unittest.TestCase): self.assertEqual("ttt", disp.get("--oh", "")) self.assertEqual("ccc", disp.get("--ch", "")) - @unittest.skipIf(USING_PYTHON2, "Subparser aliases aren't supported on Python 2") def test_display_completions_with_aliases(self): parser = ArgumentParser() parser.add_subparsers().add_parser("a", aliases=["b", "c"], help="abc help") @@ -1047,7 +1034,7 @@ class TestCheckModule(unittest.TestCase): def test_not_package(self): self._mkfile('module.py') - with self.assertRaisesRegexp(Exception, 'module is not a package'): + with self.assertRaisesRegex(Exception, 'module is not a package'): _check_module.find('module.bad') self.assertNotIn('module', sys.modules) @@ -1252,11 +1239,11 @@ class TestBashGlobal(TestBash): def test_python_completion(self): self.sh.run_command('cd ' + TEST_DIR) - self.assertEqual(self.sh.run_command('python ./prog basic f\t'), 'foo\r\n') + self.assertEqual(self.sh.run_command('python3 ./prog basic f\t'), 'foo\r\n') def test_python_filename_completion(self): self.sh.run_command('cd ' + TEST_DIR) - self.assertEqual(self.sh.run_command('python ./pro\tbasic f\t'), 'foo\r\n') + self.assertEqual(self.sh.run_command('python3 ./pro\tbasic f\t'), 'foo\r\n') def test_python_not_executable(self): """Test completing a script that cannot be run directly.""" @@ -1268,7 +1255,7 @@ class TestBashGlobal(TestBash): # Ensure prog is no longer able to be run as "./prog". self.assertIn('<<126>>', self.sh.run_command('./prog; echo "<<$?>>"')) # Ensure completion still functions when run via python. - self.assertEqual(self.sh.run_command('python ./prog basic f\t'), 'foo\r\n') + self.assertEqual(self.sh.run_command('python3 ./prog basic f\t'), 'foo\r\n') def test_python_module(self): """Test completing a module run with python -m.""" @@ -1278,7 +1265,7 @@ class TestBashGlobal(TestBash): open('package/__init__.py', 'w').close() shutil.copy(prog, 'package/prog.py') self.sh.run_command('cd ' + os.getcwd()) - self.assertEqual(self.sh.run_command('python -m package.prog basic f\t'), 'foo\r\n') + self.assertEqual(self.sh.run_command('python3 -m package.prog basic f\t'), 'foo\r\n') def _test_console_script(self, package=False, wheel=False): with TempDir(prefix='test_dir_py', dir='.'): @@ -1378,11 +1365,11 @@ class TestFish(_TestSh, unittest.TestCase): sh.run_command('set -x PYTHONPATH {0}'.format(BASE_DIR)) # 'dummy' argument unused; checks multi-command registration works # by passing 'prog' as the second argument. - output = sh.run_command('register-python-argcomplete --shell fish dummy prog | .') + output = sh.run_command('register-python-argcomplete --shell fish dummy prog | source') self.assertEqual(output, '') # Register a dummy completion with an external argcomplete script # to ensure this doesn't overwrite our previous registration. - output = sh.run_command('register-python-argcomplete --shell fish dummy --external-argcomplete-script dummy | .') + output = sh.run_command('register-python-argcomplete --shell fish dummy --external-argcomplete-script dummy | source') self.assertEqual(output, '') self.sh = sh @@ -1394,6 +1381,30 @@ class TestFish(_TestSh, unittest.TestCase): self.sh.run_command('') +@unittest.skipIf(FISH_VERSION_TUPLE < (3, 3), "Path completion is fixed in fish 3.3") +class TestFishPathCompletion(unittest.TestCase): + def setUp(self): + sh = Shell('fish') + path = ' '.join([os.path.join(BASE_DIR, 'scripts'), '$PATH']) + sh.run_command('set -x PATH {0}'.format(path)) + sh.run_command('set -x PYTHONPATH {0}'.format(BASE_DIR)) + self.assertEqual(sh.run_command('register-python-argcomplete --shell fish {0}/prog | source'.format(TEST_DIR)), '') + + self.sh = sh + + def test_path_completion(self): + self.assertEqual(self.sh.run_command('{}/prog basic f\t'.format(TEST_DIR)), 'foo\r\n') + self.sh.run_command('cd {}'.format(TEST_DIR)) + self.assertEqual(self.sh.run_command('./prog basic f\t'), 'foo\r\n') + + def tearDown(self): + # The shell wrapper is fragile; exactly which exception is raised + # differs depending on environment. + with self.assertRaises((pexpect.EOF, OSError)): + self.sh.run_command('exit') + self.sh.run_command('') + + class Shell(object): def __init__(self, shell): self.child = pexpect.spawn(shell, encoding='utf-8') diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 4cafbe4..0000000 --- a/tox.ini +++ /dev/null @@ -1,8 +0,0 @@ -[tox] -envlist = py27, py35, py36, py37, pypy, pypy3 - -[testenv] -deps = - pexpect -commands = - python test/test.py -v -- GitLab