Implementing sub-commands in Python with argparse
Implementing sub-commands in Python with argparse
Long chains of easier-to-read sub-commands have come to be all the rage these days, or maybe it’s just me. To fit into contemporary parlance, I feel like most programs historically used prefixes for sub-commands like -
, like find
’s -exec
. These days, calling an application with docker volume ls
or kubectl get all --all-namespaces
is an everyday thing. Nowadays sed -i
would be written like sed inplace
.
Anyways, I have this Python Project where I originally used a complex web of one-dimensional optional arguments (all prefixed with --
) for circumstances that were sometimes optional, and sometimes required.
My first go was ugly. It mucks up the --help
because nothing had any context relative to other options. Example:
--add --cf --dns --record --type A --name subdomain.domain.com --content 8.8.8.8
When enumerating additional arguments within the context of a sub-command, the --help
is a lot more helpful. The equivalent of the above, but with greater elegance:
add cf dns record --type A --name subdomain.domain.com --content 8.8.8.8
Any sub-command not prefixed with --
is required until the last sub-command. Anything prefixed with --
is optional and/or non-sequential. It makes things much more clear and readable. Hence why it’s becoming more common.
I had found some argparse
tutorials where I could implement one sub-command, but it just didn’t make sense to me.
I then tried using the Click package, but it’s paradigm made things even more confusing for me. Even after trying to tweak the example in Advanced Patterns, I was helplessly lost.
But eventually, I found a way to have multiple levels of sub-commands, and even wrapped their enumeration in for
loops:
I hope this helps someone out there.
References
- I wouldn’t have figured it out if it weren’t for this user and this gist: