We've made a painless tab-completion script generator for Python applications!
It's called shtab
and it currently works with argparse
, docopt
, and
argopt
to produce bash
and zsh
completion scripts.
This tool was originally created to help dvc
, but we realised it could be made
more generic and valuable to the world's entire ecosystem of Python CLI
applications. Find out how to take advantage of it in this blog post.
Zero Effort Tab Completion for Python Applications
Command line tools are powerful. Things like make
have manual pages
spanning, well,
pages,
while just the list of git
subcommands is longer than can fit on a standard
80 x 24
terminal screen.
$ git <TAB>
add filter-branch rebase
am format-patch reflog
annotate fsck relink
...
describe prco unassume
--More--
Notice the --More--
at the bottom? That's the joy of pagination.
Notice the <TAB>
at the top? That represents actually pressing the tab key.
Ah, the joy of shell tab completion.
Tab completion is an indispensable part of writing anything on the command-line.
Personally, I can't imaging trying to git co
(aliased to git checkout
) a
branch without <TAB>
to do the heavy lifting.
They say "E" is the most
common vowel, and "T" the most common consonant. My keyboard use probably looks
more like this:
Now there's a tool called dvc
which is like Git for data.
It can be viewed as a cross-platform combination of git
and make
designed for handling big data and multiple cloud storage repositories, as well
as tracking machine learning experiments. As you can imagine, supporting that
many buzzwords means it also has a large number of subcommands and options.
Every time a new feature is added, maintainers and contributors have to update tab completion scripts for multiple supported shells. At best, it's a pain, and at worst, error-prone. If you've worked on maintaining CLI applications, you'll sympathise.
Surely the parser code you've written is informative enough to automate tab completion? Surely you shouldn't have to maintain and synchronise separate tab completion scripts?
Good news: shtab
is a new tool which magically does all of this work.
Any Python CLI application using argparse
, docopt
, or argopt
can
have tab completion for free!
Simply hand your parser object to shtab
(either via the CLI or the Python
API), and a tab completion script will be generated for your preferred shell.
It's as easy as:
shtab --shell=bash myprogram.main.parser
, orimport shtab; print(shtab.complete(parser, shell="bash"))
.argparse
exampleSuppose you have some code in a module hello.main
:
import argparse
def get_main_parser():
parser = argparse.ArgumentParser(prog="hello")
parser.add_argument(
"who", help="good question", nargs="?", default="world")
parser.add_argument(
"--what", help="a better question", default="hello",
choices=["hello", "goodbye"])
return parser
if __name__ == "__main__":
parser = get_main_parser()
args = parser.parse_args()
print("{}, {}!".format(args.what, args.who))
To get tab completion for bash
, simply install shtab
and then run:
shtab --shell=bash hello.main.get_main_parser \
| sudo tee "$BASH_COMPLETION_COMPAT_DIR"/hello >/dev/null
Zsh user? Not a problem. Simply run:
shtab --shell=zsh hello.main.get_main_parser \
| sudo tee /usr/local/share/zsh/site-functions/_hello >/dev/null
# note the underscore `_` prefix in the filename
Handily you can install shtab
's own completions by following the above
examples replacing hello
with shtab
.
shtab
-driven dvc
completion in
bash
and zsh
Using shtab
, here's what
dvc
's completion looks like when
installed:
% dvc <TAB>
Completing dvc commands
add -- Track data files or directories with DVC.
cache -- Manage cache settings.
checkout -- Checkout data files from cache.
commit -- Save changed data to cache and update DVC-files.
completion -- Prints out shell tab completion scripts.
At Top: Hit TAB for more, or the character to insert
All completion suggestions guaranteed in-sync with the code! The maintainers of
dvc
were very surprised to find no less than
84 commits
touching their old completion scripts. Such churn is now a thing of the past!
You might notice one of the subcommands provided by dvc
is
completion
. Here's a quick example
of how to provide such convenience for users:
Feeling minimal? How about adding import shtab
to your application itself for
a cleaner user interface? And let's use argopt
to convert docopt
's neat
syntax to argparse
while we're at it.
"""Greetings and partings.
Usage:
greeter [options] [<you>] [<me>]
Options:
-g, --goodbye : Say "goodbye" (instead of "hello")
-b, --print-bash-completion : Output a bash tab-completion script
-z, --print-zsh-completion : Output a zsh tab-completion script
Arguments:
<you> : Your name [default: Anon]
<me> : My name [default: Casper]
"""
import sys, argopt, shtab
parser = argopt.argopt(__doc__)
if __name__ == "__main__":
args = parser.parse_args()
if args.print_bash_completion:
print(shtab.complete(parser, shell="bash"))
sys.exit(0)
if args.print_zsh_completion:
print(shtab.complete(parser, shell="zsh"))
sys.exit(0)
msg = "k thx bai!" if args.goodbye else "hai!"
print("{} says '{}' to {}".format(args.me, msg, args.you))
There are many more options and features. The documentation includes
examples of working with custom file completions and providing a completion
subcommand when integrating more tightly with existing applications.
Try it out with pip install -U shtab
or conda install -c conda-forge shtab
!
Is it worth the time?
It's worth it xkcd#1205
shtab
would be on the second row, far left (maybe even off grid). It's worth
spending days to get right yet only takes seconds to install.