Juha-Matti Santala
Community Builder. Dreamer. Adventurer.

Documentation-driven command line tools in Python with docopt

A month ago I wrote a blog post to document examples for how to use argparse to parse command line arguments in Python. After publishing that, I got a lot of suggestions for different libraries that allow you to parse arguments in different ways. I decided to take a look at some of them, starting today with docopt.

🚧 A quick note about project state. The original docopt project has not been updated in years. A fork of the project called docopt-ng was created to keep it updated (and is a drop-in replacement) and that has been maintained further. However, according to Jazzband, this project is at risk of losing maintenance as well. For what they do, I think they are still usable but for longevity and for building more complex projects, it's good to keep that in mind when considering your options.

Docopt claims to be a Pythonic command line arguments parser, that will make you smile. It works by reading & parsing the docstring of the application and provides a dictionary with parsed values. One nice benefit from this approach is that it allows/forces (depending on your viewpoint) you to develop your CLI tool documentation-first. I have experimented with documentation-driven development a couple of times. The idea there is that you first write the documentation of your application so you'll think about the interface and use cases first and then program your app to match that. So I'm quite excited about the idea.

"""Naval Fate.

Usage:
  naval_fate.py ship new <name>...
  naval_fate.py ship <name> move <x> <y> [--speed=<kn>]
  naval_fate.py ship shoot <x> <y>
  naval_fate.py mine (set|remove) <x> <y> [--moored | --drifting]
  naval_fate.py (-h | --help)
  naval_fate.py --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  --speed=<kn>  Speed in knots [default: 10].
  --moored      Moored (anchored) mine.
  --drifting    Drifting mine.

"""
from docopt import docopt


if __name__ == '__main__':
    arguments = docopt(__doc__, version='Naval Fate 2.0')
    print(arguments)
example from https://github.com/docopt/docopt

Here's an example from the documentation. It starts with a docstring that is both the specification of your application's arguments and the help message when running the application with python naval_fate.py --help.

You can also provide your own help message as a parameter to the function but for most use cases, writing it as the docstring seems the best option as then it's also easy to find and modify when developing the application further.

On the first glance, I like it. I like when the logic/configuration/specification is written in a way that is easy to read and understand. That's where docopt shines. It's easy for me to read through that and see what arguments it accepts.

There are also ports of docopt to other languages.