DryParse, a don't-repeat-yourself command-line parser
DryParse is a command-line parser for Python 3. It maps command-line "commands" to Python callables, transforming the command-line arguments into Python values and passing them in as arguments.
DryParse's goal is to minimize repetition.
DryParse uses the following terms for things one might find on a command-line:
% ./program.py --debug foo --xyz 123 ^ ^ ^ ^ | | | | | | | argument | | | | | option | | | command | global option
A "command" is the first thing on the command-line (after the program) that doesn't start with a dash. This is how many modern programs operate, for example "svn", "hg", and "git"; for the command-line "hg clone foo bar", "clone" is a "command".
"Options" are things that start with '-'. Single-character options always use a single dash, and can be grouped together ('-a -b -c' and '-abc' are equivalent). Multiple-character options must use a double dash and must be specified separately.
An option can itself take an argument. This can be of the form '-n 3' or '-n=3' or '-n3'. You can specify a single-letter option that takes an argument as part of a multiple option group ('-abc'), but it must be specified last ('-abcn5').
There are two kinds of options: "global options", which apply to the program itself (or to all commands), and "command options" (also just called "options") which are specific to each command. Options specified on the command-line before the command are global options; options specified on the command-line after the command are command options.
"Arguments" are things that don't start with a dash that come after the command. They are always positional.
You don't have to use commands if you don't want them. In that case:
- All options are global options.
- Everything on the command-line that doesn't start with a dash is an argument (a "global argument").
DryParse maps Python callables to "commands", and the parameters of those callables become the options and arguments for that command. (DryParse also accepts a special callable that handles global options.)
Positional-or-keyword (POK) parameters in your Python function map to "arguments". By default, the arguments from the command-line are passed in to the POK parameters of your function as strings. POK parameters that have no default are required.
If your function takes a *args parameter, it will accept an arbitrary number of additional positional parameters.
Keyword-only (KO) parameters in your Python function map to "options". The name of the parameter is used as the name of the option. Options are always optional; therefore, if your function specifies a KO parameter with no default, a default is inferred. By default, options are boolean, and default to False, and specifying them on the command-line toggles them between True and False.
If a parameter to your callable has no default value, it is required, and therefore the option or argument it maps to is also required.
If the parameter does have a default value, then naturally it's optional. But also, the type of the default value is used to "cast" the string from the command-line before passing it in to your callable.
The parameters to your callable can optionally specify an "annotation" using Python 3's function annotation syntax. If specified, the annotation should be a set. Values in this annotation set are interpreted as follows:
If the value is a string, and it does not start with a dash, it's documentation for this option or argument.
If the value is a callable, it will be used to "cast" a value from the command-line to a native Python type. The callable should behave as like a scalar type constructor (str, bool, int):
- If called with no arguments, it should return a reasonable 'default' value, preferably a false value.
- If called with a single positional argument (which in DryParse will always be a string), it should return a value. The value should theoretically represent the value of the command-line parameter, converted to some other type.
If the value is a string starting with a dash, it's a whitespace-separated list of substrings relevant to this option.
These substrings are interpreted as follows:
- If the substring begins with a single dash followed by an isalpha() character, this argument will map to that single-character option. (This is only valid for positional-only parameters.)
- If the substring begins with two dashes followed by two or more isalpha() characters, this argument will map to that option. (This is only valid for positional-only parameters.)
- If the substring is a single asterisk ('*'), this option must take a value, and you may specify it any number of times. DryParse will pass in a list containing all the option values from the command-line in order.
- If the substring consists entirely of one or two dashes, it is ignored.
You can execute the unit test suite as follows:
% python3 -m unittest dryparse