Skip to content

Commit

Permalink
Merge pull request #611 from Freed-Wu/main
Browse files Browse the repository at this point in the history
Add support for shell completion
  • Loading branch information
jaraco authored Dec 18, 2022
2 parents 5a263f3 + a588e65 commit 1d76158
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 0 deletions.
6 changes: 6 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
v23.13.0
--------

* #608: Added support for tab completion on the ``keyring`` command
if the ``completion`` extra is installed (``keyring[completion]``).

v23.12.1
--------

Expand Down
35 changes: 35 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,41 @@ package, suitable for invoking from Python like so::
$ python -m keyring get system username
password

Tab Completion
--------------

If installed via a package manager (apt, pacman, nix, homebrew, etc),
these shell completions may already have been distributed with the package
(no action required).

Keyring provides tab completion if the ``completion`` extra is installed::

$ pip install 'keyring[completion]'

Then, generate shell completions, something like::

$ keyring --print-completion bash | sudo tee /usr/share/bash-completion/completions/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 mainly for GNU/Linux. For other OSs,
consider:

- macOS (Homebrew x86): /usr/local/share
- macOS (Homebrew ARM): /opt/homebrew/share
- Android (Termux): /data/data/com.termux/files/usr/share
- Windows (mingw64 of msys2): /mingw64/share
- ...

After installing the shell completions, enable them following your shell's
recommended instructions. e.g.:

- bash: install [bash-completion](https://github.com/scop/bash-completion),
and ensure ``. /usr/share/bash-completion/bash_completion`` in ``~/.bashrc``.
- zsh: ensure ``autoload -Uz compinit && compinit`` appears in ``~/.zshrc``,
then ``grep -w keyring ~/.zcompdump`` to verify keyring appears, indicating
it was installed correctly.

Configuring
===========

Expand Down
14 changes: 14 additions & 0 deletions keyring/backend_complete.zsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Complete keyring backends for `keyring -b` from `keyring --list-backends`
# % keyring -b <TAB>
# keyring priority
# keyring.backends.chainer.ChainerBackend 10
# keyring.backends.fail.Keyring 0
# ... ...

backend_complete() {
local line
while read -r line; do
choices+=(${${line/ \(priority: /\\\\:}/)/})
done <<< "$($words[1] --list-backends)"
_arguments "*:keyring priority:(($choices))"
}
2 changes: 2 additions & 0 deletions keyring/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from . import core
from . import backend
from . import completion
from . import set_keyring, get_password, set_password, delete_password


Expand Down Expand Up @@ -48,6 +49,7 @@ def __init__(self):
'username',
nargs="?",
)
completion.install(self.parser)

def run(self, argv):
args = self.parser.parse_args(argv)
Expand Down
51 changes: 51 additions & 0 deletions keyring/completion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import argparse
import sys

try:
import shtab
except ImportError:
pass

if sys.version_info < (3, 9):
from importlib_resources import files
else:
from importlib.resources import files


class _MissingCompletionAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string):
print("Install keyring[completion] for completion support.")
parser.exit(0)


def add_completion_notice(parser):
"""Add completion argument to parser."""
parser.add_argument(
"--print-completion",
choices=["bash", "zsh", "tcsh"],
action=_MissingCompletionAction,
help="print shell completion script",
)
return parser


def get_action(parser, option):
(match,) = (action for action in parser._actions if option in action.option_strings)
return match


def install_completion(parser):
preamble = dict(
zsh=files(__package__).joinpath('backend_complete.zsh').read_text(),
)
shtab.add_argument_to(parser, preamble=preamble)
get_action(parser, '--keyring-path').completion = shtab.DIR
get_action(parser, '--keyring-backend').completion = dict(zsh='backend_complete')
return parser


def install(parser):
try:
install_completion(parser)
except NameError:
add_completion_notice(parser)
4 changes: 4 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ install_requires =
jeepney>=0.4.2; sys_platform=="linux"
importlib_metadata >= 4.11.4; python_version < "3.12"
jaraco.classes
importlib_resources; python_version < "3.9"

[options.packages.find]
exclude =
Expand Down Expand Up @@ -63,6 +64,9 @@ docs =

# local

completion =
shtab

[options.entry_points]
console_scripts =
keyring=keyring.cli:main
Expand Down

0 comments on commit 1d76158

Please sign in to comment.