Source

dryparse / README.rst

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.

Terminology

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").

API

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.

Testing

You can execute the unit test suite as follows:

% python3 -m unittest dryparse