From 40af6def7da90c4166e575baf51e7800b3eac49f Mon Sep 17 00:00:00 2001 From: Wu Zhenyu Date: Thu, 8 Dec 2022 11:32:52 +0800 Subject: [PATCH] Fix #608 --- README.rst | 29 +++++++++++++++++++++++++++++ keyring/_shtab.py | 24 ++++++++++++++++++++++++ keyring/cli.py | 29 +++++++++++++++++++++++++++-- setup.cfg | 4 ++++ 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 keyring/_shtab.py diff --git a/README.rst b/README.rst index 5e0f8acf..32f375ce 100644 --- a/README.rst +++ b/README.rst @@ -93,6 +93,35 @@ package, suitable for invoking from Python like so:: $ python -m keyring get system username password +If you install keyring by ``pip install 'keyring[completion]'``, you can +generate shell completions by: + + keyring --print-completion bash | sudo tee /usr/share/bash-completion/compleitons/keyring + keyring --print-completion zsh | sudo tee /usr/share/zsh/site-functions/_keyring + keyring --print-completion tcsh | sudo tee /etc/profile.d/keyring.csh + +**Note**: the path of `/usr/share` is only for GNU/Linux mostly. If you use other OSs, +try: + +- macOS, GUN/Linux (Homebrew): /usr/local/share +- Android (Termux): /data/data/com.termux/files/usr/share +- Windows (mingw64 of msys2): /mingw64/share +- ... + +If you install keyring by your package manager (apt, pacman, nix, homebrew, etc), +these shell completions should have been packaged and you don't need generate +them by yourself. + +After install shell completions, you can enable shell completions by: + +- bash: install [bash-completion](https://github.com/scop/bash-completion), and + ``. /usr/share/bash-completion/bash_completion`` in your bashrc. +- zsh: ``autoload -Uz compinit && compinit`` in your zshrc, and + ``grep -w keyring ~/.zcompdump`` to check if it is installed correctly: + + $ grep -w keyring ~/.zcompdump + 'keyring' '_keyring' + Configuring =========== diff --git a/keyring/_shtab.py b/keyring/_shtab.py new file mode 100644 index 00000000..0781f85b --- /dev/null +++ b/keyring/_shtab.py @@ -0,0 +1,24 @@ +from argparse import Action + +FILE = None +DIRECTORY = DIR = None + + +class _PrintCompletionAction(Action): + """Print completion action.""" + + def __call__(self, parser, namespace, values, option_string): + print("Please install shtab firstly!") + parser.exit(0) + + +def add_argument_to(parser, *args, **kwargs): + """Add completion argument to parser.""" + Action.complete = None # type: ignore + parser.add_argument( + "--print-completion", + choices=["bash", "zsh", "tcsh"], + action=_PrintCompletionAction, + help="print shell completion script", + ) + return parser diff --git a/keyring/cli.py b/keyring/cli.py index e4a4c78f..d2bb87d1 100755 --- a/keyring/cli.py +++ b/keyring/cli.py @@ -9,24 +9,49 @@ from . import backend from . import set_keyring, get_password, set_password, delete_password +try: + import shtab +except ImportError: + from . import _shtab as shtab + +# it completes keyring backends for `keyring -b` by the output of `keyring --list-backends` +# % keyring -b +# keyring priority +# keyring.backends.chainer.ChainerBackend 10 +# keyring.backends.fail.Keyring 0 +# ... ... +PREAMBLE = { + "zsh": r""" +keyring_complete() { + local line + while read -r line; do + choices+=(${${line/ \(priority: /\\\\:}/)/}) + done <<< "$($words[1] --list-backends)" + _arguments "*:keyring priority:(($choices))" +} +""" +} +KEYRING_COMPLETE = {"zsh": "keyring_complete"} + class CommandLineTool: def __init__(self): self.parser = argparse.ArgumentParser() + shtab.add_argument_to(self.parser, preamble=PREAMBLE) self.parser.add_argument( "-p", "--keyring-path", dest="keyring_path", default=None, help="Path to the keyring backend", - ) + ).complete = shtab.DIR self.parser.add_argument( "-b", "--keyring-backend", dest="keyring_backend", default=None, help="Name of the keyring backend", - ) + ).complete = KEYRING_COMPLETE self.parser.add_argument( "--list-backends", action="store_true", diff --git a/setup.cfg b/setup.cfg index e7db40f2..d661e377 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,6 +63,10 @@ docs = # local +completion = + # upstream + shtab + [options.entry_points] console_scripts = keyring=keyring.cli:main