Python CLIs

Putting the “argument” in “command-line argument”

July 2, 2021 — September 29, 2022

computers are awful
python

An infuriating quagmire. Parsing command line arguments in python is just hard enough to be a friction, but not so hard that enough developers are dissuaded from attempting to reinvent it. All the various solutions claim to be beautiful, and/or seamless and/or elegant, which individually they may be. Collectively they feel like aggressive hawkers shouting in your face and at each other, wasting your time by reducing the probability that any two projects have the same dependencies.

The best dependency system is … any of the three CLI libraries your project already uses. Do not add an additional one.

Figure 1

OK, since I contribute to more than two python projects with CLIs, I have more than three CLI systems that I need to deal with. Below is a spotter’s guide.

tl;dr: My use case involves configuring lots of ML so wherever feasible I use Hydra, which does that very well, and supports every feature I need, and will generate command-line parsers as a side effect.

1 built-in: argparse

argparse is built-in to python stdlib and is adequate, so why not just use that and avoid other dependencies? Answer: a dependency you might already have is likely to have introduced an additional CLI parsing library.

Cute hack: chriskiehl/Gooey will construct a GUI for programs by examining argparse code.

However, this is not flexible enough for the kind of stuff that I frequently need to do (complicated nested options…)

2 hydra

Hydra is a framework for elegantly configuring complex applications.” As a special case it builds CLIs with autocomplete and other fun stuff. Because it can do so many things of use to an ML researcher, within a very simple paradigm, this tool is all I need for experiment and workflow invocation. Because that is what I mostly do, that is now my main tool. See my hydra notes.

3 docopt

docopt/docopt: Pythonic command line arguments parser, that will make you smile

docopt helps you:

  • define the interface for your command-line app, and
  • automatically generate a parser for it.

docopt is based on conventions that have been used for decades in help messages and man pages for describing a program’s interface. An interface description in docopt is such a help message, but formalized.

Bonus feature: It is implemented in many languages, so the same command line description will also generate parsers in C++, julia etc.

4 Typer

Typer (source):

FFS there is another hip one that uses even more shiny features yay. This one has unusually compact syntax, since it uses typed-hinting in arguments to sort it out, which, I get it, is nice. Is it nice enough to throw everything out and start again with a new, incompatible system? Whe yes, it is nice enough for at least a few developers, and now you need to know about it or you will be less cool than them.

Typer is internally based upon the next one, Click, so maybe it doesn’t count as a new dependency to add to your project if you already use Click.

5 Click

Click

is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary. It’s the “Command Line Interface Creation Kit”. It’s highly configurable but comes with sensible defaults out of the box. […]

  • arbitrary nesting of commands
  • automatic help page generation
  • supports lazy loading of subcommands at runtime

Aims to offer an alternative to the built-in argparse, which they regard as excessively magical. Its special feature is setuptools integration enabling installation of command-line tools from your current ipython virtualenv.

6 Abseil

Google framework abseil has a python CLI system whose selling points are that it

  1. Works across Google ML apps
  2. integrates C++ arguments somehow? (Build arguments? Run-time? Someone who cares can answer that)
  3. allows distributed definition of arguments rather than centralized, and
  4. logging and testing features are bolted together into the same library.

Actual value proposition: Because AFAICT abseil is a dependency of jax and Tensorflow if you do machine learning, this is pre-installed in all the examples. Thus you may as well keep it when copy-pasting Google sample code.

On the other hand, pretty much every machine learning framework has an equivalent command-line whatsit, so you will probably end up copy-pasting some other stuff from somewhere else which doesn’t work with abseil, so maybe you could just ditch this? It is not amazing.

7 Invoke

Invoke

provides a clean, high level API for running shell commands and defining/organizing task functions from a tasks.py file […] it offers advanced features as well — namespacing, task aliasing, before/after hooks, parallel execution and more.

8 Argh

argh was/is a popular extension to argparse

Argh is fully compatible with argparse. You can mix Argh-agnostic and Argh-aware code. Just keep in mind that the dispatcher does some extra work that a custom dispatcher may not do.

9 clip.py

clip.py comes with a passive-aggressive app name, (+1) is all about wrapping generic python commands in command-line applications easily, much like click. But different.

10 Misc

  • Pext is a python extension to script interactive CLI processes things in a handy GUI.