Parsing arguments with docopt

In 2008, I hacked a group of ID3 tag utilities to work around MP3 files with incorrect string encoding. These scripts are less useful nowadays due to the lacking support of MP4. I’d like more practice of python application release, since I just recently did so, so I took this opportunity to rewrite from the scratch, tagcli.

Thanks to mutagen, manipulating ID3 and MP4 tags is quite trivial, especially I am using the easy interface. The challenge then mainly focuses on the easy use. Since tagcli is pitched to the power users, a nice command line UI with comprehensive online help will make it more appealing, where docopt will shine.

Just like pip, tag uses the subcommand pattern to loosely aggregate all functionalities. First, the subcommand is extracted by docopt, then the corresponding subcommand is invoked with the remaining arguments. Each subcommand is essentially a python function with help copy in its doc string. It will use docopt to parse the arguments again to get the semantics of the user input.

Here is the global help examples:

$ tag --help
A mutagen-based tag editor.

Usage:
  tag <command> [<options>...]
General Options:
  -h, --help    Show help.
  --version     Show version and exit.

Commands:
 rename         Rename file using pattern with tags.
 update         Update the tags.
 dump           Dumps the tags.
 tags           Show generic tag names.

See 'tag help <command>' for more information on a specific command.

and help on the subcommand:

$ tag help rename

usage: tag rename [options] <pattern> <files>...

Rename <files> with the naming <pattern> formated by the tags.

Options:
  <pattern>           The file name pattern using python string format
                      syntax. See 'tag help tags' for supported tags.
  -p, --dry-run       Print the action the command will take without
                      actually changing any files.
  --verbose           Output extra information about the work being done.

Examples:

  tag rename '{discnumber}-{tracknumber:02}.{album} - {title}' foo.mp3

Unlike argparse or optparse, which build the argument parsing in a bottom-up fashion; docopt takes a top-down approach:

  • you make the exact help copy with predefined placeholders.
  • the docopt library parses the string and returns a key/value dictionary
  • or print the usage and exit for if arguments are invalid

This approach has its own Pros and Cons. The good is the developers have full control of the aesthetic of the help, just WYSIWYG. The bad is the docopt specification does not support reStructuredText, so you may have to repeat yourself for the document, and man page if you want to go extra miles. The ugly is that you cannot debug your help string and fitting the help copy to your doc string could be quite tricky if you are religious about PEP8.