From 07858bb478a6b30d435b1550c1280202bb6ab149 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sun, 28 Jun 2020 02:33:45 +0100 Subject: [PATCH] blog: shtab-complete --- content/blog/2020-06-28-shtab-completion.md | 155 ++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 content/blog/2020-06-28-shtab-completion.md diff --git a/content/blog/2020-06-28-shtab-completion.md b/content/blog/2020-06-28-shtab-completion.md new file mode 100644 index 0000000000..38665e2b35 --- /dev/null +++ b/content/blog/2020-06-28-shtab-completion.md @@ -0,0 +1,155 @@ +--- +title: '(Tab) Complete Any Python Application in 1 Minute or Less' +date: 2020-06-28 +description: | + We've made a painless tab-completion script generator for Python applications! + Find out how to take advantage of it in this blog post. + +descriptionLong: | + We've made a painless tab-completion script generator for Python applications! + It's called `shtab` and it currently works with `argparse`, `docopt`, and + `docopt` to produce `bash` and `zsh` completion scripts. DVC itself uses it. + Find out how to take advantage of it in this blog post. + +author: casperdcl +--- + +> Other potential titles: +> +> - Zero Effort Tab Completion for Python Applications +> - Finally: 1-Click Tab Completion for [`argparse`], [`docopt`] and [`argopt`] +> - A [`shtab`] in the dark at completing your ~~sentences~~ commands + +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. + +```bash +$ git +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 `` 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 `` to do the heavy lifting. + +Now there's a tool called [`dvc`] which is like 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! + +### `argparse` example + +Suppose you have some code in a module `hello.main`: + +```python +import argparse + +def get_main_parser(): + parser = argparse.ArgumentParser(prog="hello") + parser.add_argument("who", help="a 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: + +```bash +shtab --shell=bash hello.main.get_main_parser \ + | sudo tee "$BASH_COMPLETION_COMPAT_DIR"/hello >/dev/null +``` + +Zsh user? Not a problem. Simply run: + +```bash +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`. + +### `docopt` example + +Feeling minimal? How about adding `import shtab` to your code for a cleaner user +interface? And let's use `argopt` to convert `docopt`'s syntax to `argparse` +while we're at it. + +```python +"""Greetings and partings. + +Usage: + greeter [options] [] [] + +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: + : Your name [default: Anon] + : 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)) +``` + +### Try it out + +There are many more options and features. The [documentation][`shtab`] 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`! + +[`argopt`]: https://pypi.org/project/argopt +[`argparse`]: https://docs.python.org/library/argparse +[`docopt`]: https://pypi.org/project/docopt +[`dvc`]: https://github.com/iterative/dvc +[`git`]: https://git-scm.com +[`make`]: https://en.wikipedia.org/wiki/Make_(software) +[`shtab`]: https://github.com/iterative/shtab