Optik Tutorial

While Optik is quite flexible and powerful, it's also straightforward to use in most cases. This document covers the code patterns that are common to any Optik-based program.

First, you need to import the OptionParser class; then, early in the main program, create an OptionParser instance:

from optik import OptionParser
parser = OptionParser()

Then you can start defining options. The basic syntax is:

parser.add_option(opt_str, ...,
                  attr=value, ...)

Each option has one or more option strings, such as "-f" or "--file", and several option attributes that tell Optik what to expect and what to do when it encounters that option on the command line.

Typically, each option will have one short option string and one long option string, e.g.:

parser.add_option("-f", "--file", ...)

You're free to define as many short option strings and as many long option strings as you like (including zero), as long as there is at least one option string overall.

The option strings passed to add_option() are effectively labels for the option defined by that call. For brevity, we will frequently refer to encountering an option on the command line; in reality, Optik encounters option strings and looks up options from them.

Once all of your options are defined, instruct Optik to parse your program's command line:

(options, args) = parser.parse_args()

(If you like, you can pass a custom argument list to parse_args(), but that's rarely necessary: by default it uses sys.argv[1:].)

parse_args() returns two values:

This tutorial document only covers the four most important option attributes: action, type, dest (destination), and help. Of these, action is the most fundamental.

Understanding option actions

Actions tell Optik what to do when it encounters an option on the command line. There is a fixed set of actions hard-coded into Optik; adding new actions is an advanced topic covered in Extending Optik. Most actions tell Optik to store a value in some variable -- for example, take a string from the command line and store it in an attribute of options.

If you don't specify an option action, Optik defaults to store.

The store action

The most common option action is store, which tells Optik to take the next argument (or the remainder of the current argument), ensure that it is of the correct type, and store it to your chosen destination.

For example:

parser.add_option("-f", "--file",
                  action="store", type="string", dest="filename")

Now let's make up a fake command line and ask Optik to parse it:

args = ["-f", "foo.txt"]
(options, args) = parser.parse_args(args)

When Optik sees the option string "-f", it consumes the next argument, "foo.txt", and stores it in options.filename. So, after this call to parse_args(), options.filename is "foo.txt".

Some other option types supported by Optik are int and float. Here's an option that expects an integer argument:

parser.add_option("-n", type="int", dest="num")

Note that this option has no long option string, which is perfectly acceptable. Also, there's no explicit action, since the default is store.

Let's parse another fake command-line. This time, we'll jam the option argument right up against the option: since "-n42" (one argument) is equivalent to "-n 42" (two arguments), the code

(options, args) = parser.parse_args(["-n42"])
print options.num

will print "42".

If you don't specify a type, Optik assumes string. Combined with the fact that the default action is store, that means our first example can be a lot shorter:

parser.add_option("-f", "--file", dest="filename")

If you don't supply a destination, Optik figures out a sensible default from the option strings: if the first long option string is "--foo-bar", then the default destination is foo_bar. If there are no long option strings, Optik looks at the first short option string: the default destination for "-f" is f.

Optik also includes built-in long and complex types. Adding types is covered in Extending Optik.

Handling boolean (flag) options

Flag options -- set a variable to true or false when a particular option is seen -- are quite common. Optik supports them with two separate actions, store_true and store_false. For example, you might have a verbose flag that is turned on with "-v" and off with "-q":

parser.add_option("-v", action="store_true", dest="verbose")
parser.add_option("-q", action="store_false", dest="verbose")

Here we have two different options with the same destination, which is perfectly OK. (It just means you have to be a bit careful when setting default values -- see below.)

When Optik encounters "-v" on the command line, it sets options.verbose to True; when it encounters "-q", options.verbose is set to False.

Other actions

Some other actions supported by Optik are:

store a constant value
append this option's argument to a list
increment a counter by one
call a specified function

These are covered in Reference Guide and Option Callbacks.

Default values

All of the above examples involve setting some variable (the "destination") when certain command-line options are seen. What happens if those options are never seen? Since we didn't supply any defaults, they are all set to None. This is usually fine, but sometimes you want more control. Optik lets you supply a default value for each destination, which is assigned before the command line is parsed.

First, consider the verbose/quiet example. If we want Optik to set verbose to True unless "-q" is seen, then we can do this:

parser.add_option("-v", action="store_true", dest="verbose", default=True)
parser.add_option("-q", action="store_false", dest="verbose")

Since default values apply to the destination rather than to any particular option, and these two options happen to have the same destination, this is exactly equivalent:

parser.add_option("-v", action="store_true", dest="verbose")
parser.add_option("-q", action="store_false", dest="verbose", default=True)

Consider this:

parser.add_option("-v", action="store_true", dest="verbose", default=False)
parser.add_option("-q", action="store_false", dest="verbose", default=True)

Again, the default value for verbose will be True: the last default value supplied for any particular destination is the one that counts.

A clearer way to specify default values is the set_defaults() method of OptionParser, which you can call at any time before calling parse_args():

(options, args) = parser.parse_args()

As before, the last value specified for a given option destination is the one that counts. For clarity, try to use one method or the other of setting default values, not both.

Generating help

Optik's ability to generate help and usage text automatically is useful for creating user-friendly command-line interfaces. All you have to do is supply a help value for each option, and optionally a short usage message for your whole program. Here's an OptionParser populated with user-friendly (documented) options:

usage = "usage: %prog [options] arg1 arg2"
parser = OptionParser(usage=usage)
parser.add_option("-v", "--verbose",
                  action="store_true", dest="verbose", default=True,
                  help="make lots of noise [default]")
parser.add_option("-q", "--quiet",
                  action="store_false", dest="verbose", 
                  help="be vewwy quiet (I'm hunting wabbits)")
parser.add_option("-f", "--filename",
                  metavar="FILE", help="write output to FILE"),
parser.add_option("-m", "--mode",
                  help="interaction mode: novice, intermediate, "
                       "or expert [default: %default]")

If Optik encounters either "-h" or "--help" on the command-line, or if you just call parser.print_help(), it prints the following to standard output:

usage: <yourscript> [options] arg1 arg2

  -h, --help            show this help message and exit
  -v, --verbose         make lots of noise [default]
  -q, --quiet           be vewwy quiet (I'm hunting wabbits)
  -f FILE, --filename=FILE
                        write output to FILE
  -m MODE, --mode=MODE  interaction mode: novice, intermediate, or
                        expert [default: intermediate]

(If the help output is triggered by a help option, Optik exits after printing the help text.)

There's a lot going on here to help Optik generate the best possible help message:

Printing a version string

Similar to the brief usage string, Optik can also print a version string for your program. You have to supply the string as the version argument to OptionParser:

parser = OptionParser(usage="%prog [-f] [-q]", version="%prog 1.0")

"%prog" is expanded just like it is in usage. Apart from that, version can contain anything you like. When you supply it, Optik automatically adds a "--version" option to your parser. If it encounters this option on the command line, it expands your version string (by replacing "%prog"), prints it to stdout, and exits.

For example, if your script is called /usr/bin/foo:

$ /usr/bin/foo --version
foo 1.0

How Optik handles errors

There are two broad classes of errors that Optik has to worry about: programmer errors and user errors. Programmer errors are usually erroneous calls to parser.add_option(), e.g. invalid option strings, unknown option attributes, missing option attributes, etc. These are dealt with in the usual way: raise an exception (either optik.OptionError or TypeError) and let the program crash.

Handling user errors is much more important, since they are guaranteed to happen no matter how stable your code is. Optik can automatically detect some user errors, such as bad option arguments (passing "-n 4x" where -n takes an integer argument), missing arguments ("-n" at the end of the command line, where -n takes an argument of any type). Also, you can call parser.error() to signal an application-defined error condition:

(options, args) = parser.parse_args()
if options.a and options.b:
    parser.error("options -a and -b are mutually exclusive")

In either case, Optik handles the error the same way: it prints the program's usage message and an error message to standard error and exits with error status 2.

Consider the first example above, where the user passes "4x" to an option that takes an integer:

$ /usr/bin/foo -n 4x
usage: foo [options]

foo: error: option -n: invalid integer value: '4x'

Or, where the user fails to pass a value at all:

$ /usr/bin/foo -n
usage: foo [options]

foo: error: -n option requires an argument

Optik-generated error messages take care always to mention the option involved in the error; be sure to do the same when calling parser.error() from your application code.

If Optik's default error-handling behaviour does not suite your needs, you'll need to subclass OptionParser and override exit() and/or error().

Putting it all together

Here's what Optik-based scripts usually look like:

from optik import OptionParser
def main():
    usage = "usage: %prog [options] arg"
    parser = OptionParser(usage)
    parser.add_option("-f", "--file", dest="filename",
                      help="read data from FILENAME")
    parser.add_option("-v", "--verbose",
                      action="store_true", dest="verbose")
    parser.add_option("-q", "--quiet",
                      action="store_false", dest="verbose")
    (options, args) = parser.parse_args()
    if len(args) != 1:
        parser.error("incorrect number of arguments")
    if options.verbose:
        print "reading %s..." % options.filename

if __name__ == "__main__":