Skip to content

Commit

Permalink
blog: shtab-complete
Browse files Browse the repository at this point in the history
  • Loading branch information
casperdcl committed Jun 28, 2020
1 parent 3ab081a commit 07858bb
Showing 1 changed file with 155 additions and 0 deletions.
155 changes: 155 additions & 0 deletions content/blog/2020-06-28-shtab-completion.md
Original file line number Diff line number Diff line change
@@ -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 <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.

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] [<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))
```

### 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

0 comments on commit 07858bb

Please sign in to comment.