options.xhtml   [plain text]


<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Using usage.Options</title>
  </head>

  <body>
    <h1>Using usage.Options</h1>

    <h2>Introduction</h2>

    <p>There is frequently a need for programs to parse a UNIX-like
    command line program: options preceded by <code>-</code> or
    <code>--</code>, sometimes followed by a parameter, followed by
    a list of arguments. The <code
    class="API">twisted.python.usage</code> provides a class,
    <code>Options</code>, to facilitate such parsing.</p>

    <p>While Python has the <code>getopt</code> module for doing
    this, it provides a very low level of abstraction for options.
    Twisted has a higher level of abstraction, in the class <code
    class="API">twisted.python.usage.Options</code>. It uses
    Python's reflection facilities to provide an easy to use yet
    flexible interface to the command line. While most command line
    processors either force the application writer to write her own
    loops, or have arbitrary limitations on the command line (the
    most common one being not being able to have more then one
    instance of a specific option, thus rendering the idiom
    <code class="shell">program -v -v -v</code> impossible), Twisted allows the
    programmer to decide how much control she wants.</p>

    <p>The <code>Options</code> class is used by subclassing. Since
    a lot of time it will be used in the <code
    class="API">twisted.tap</code> package, where the local
    conventions require the specific options parsing class to also
    be called <code>Options</code>, it is usually imported with</p>
<pre class="python">
from twisted.python import usage
</pre>

    <h2>Boolean Options</h2>

    <p>For simple boolean options, define the attribute
    <code>optFlags</code> like this:</p>
<pre class="python">
class Options(usage.Options):

    optFlags = [["fast", "f", "Act quickly"], ["safe", "s", "Act safely"]]
</pre>
    <p><code>optFlags</code> should be a list of 3-lists. The first element
    is the long name, and will be used on the command line as
    <code>--fast</code>. The second one is the short name, and will be used
    on the command line as <code>-f</code>. The last element is a
    description of the flag and will be used to generate the usage
    information text.  The long name also determines the name of the key
    that will be set on the Options instance. Its value will be 1 if the
    option was seen, 0 otherwise. Here is an example for usage:</p>
<pre class="python">
class Options(usage.Options):

    optFlags = [
        ["fast", "f", "Act quickly"],
        ["good", "g", "Act well"],
        ["cheap", "c", "Act cheaply"]
    ]

command_line = ["-g", "--fast"]

options = Options()
try:
    options.parseOptions(command_line)
except usage.UsageError, errortext:
    print '%s: %s' % (sys.argv[0], errortext)
    print '%s: Try --help for usage details.' % (sys.argv[0])
    sys.exit(1)
if options['fast']:
    print "fast",
if options['good']:
    print "good",
if options['cheap']:
    print "cheap",
print
</pre>

    <p>The above will print <code>fast good</code>.</p>

    <p>Note here that Options fully supports the mapping interface. You can
    access it mostly just like you can access any other dict. Options are stored
    as mapping items in the Options instance: parameters as 'paramname': 'value'
    and flags as 'flagname': 1 or 0.</p>

    <h3>Inheritance, Or: How I Learned to Stop Worrying and Love
    the Superclass</h3>

    <p>Sometimes there is a need for several option processors with
    a unifying core. Perhaps you want all your commands to
    understand <code>-q</code>/<code>--quiet</code> means to be
    quiet, or something similar. On the face of it, this looks
    impossible: in Python, the subclass's <code>optFlags</code>
    would shadow the superclass's. However,
    <code>usage.Options</code> uses special reflection code to get
    all of the <code>optFlags</code> defined in the hierarchy. So
    the following:</p>
<pre class="python">
class BaseOptions(usage.Options):

    optFlags = [["quiet", "q", None]]

class SpecificOptions(BaseOptions):

    optFlags = [
        ["fast", "f", None], ["good", "g", None], ["cheap", "c", None]
    ]
</pre>
    <p>Is the same as: </p>
<pre class="python">
class SpecificOptions(BaseOptions):

    optFlags = [
        ["quiet", "q", "Silence output"],
        ["fast", "f", "Run quickly"],
        ["good", "g", "Don't validate input"],
        ["cheap", "c", "Use cheap resources"]
    ]
</pre>

    <h2>Parameters</h2>

    <p>Parameters are specified using the attribute
    <code>optParameters</code>. They <em>must</em> be given a
    default. If you want to make sure you got the parameter from
    the command line, give a non-string default. Since the command
    line only has strings, this is completely reliable.</p>

    <p>Here is an example:</p>
<pre class="python">
from twisted.python import usage

class Options(usage.Options):

    optFlags = [
        ["fast", "f", "Run quickly"],
        ["good", "g", "Don't validate input"],
        ["cheap", "c", "Use cheap resources"]
    ]
    optParameters = [["user", "u", None, "The user name"]]

config = Options()
try:
    config.parseOptions() # When given no argument, parses sys.argv[1:]
except usage.UsageError, errortext:
    print '%s: %s' % (sys.argv[0], errortext)
    print '%s: Try --help for usage details.' % (sys.argv[0])
    sys.exit(1)

if config['user'] is not None:
    print "Hello", config['user']
print "So, you want it:"

if config['fast']:
    print "fast",
if config['good']:
    print "good",
if config['cheap']:
    print "cheap",
print
</pre>

    <p>Like <code>optFlags</code>, <code>optParameters</code> works
    smoothly with inheritance.</p>

    <h2>Option Subcommands</h2>
    
    <p>It is useful, on occassion, to group a set of options together based
    on the logical <q>action</q> to which they belong.  For this, the
    <code>usage.Options</code> class allows you to define a set of
    <q>subcommands</q>, each of which can provide its own
    <code>usage.Options</code> instance to handle its particular
    options.</p>
    
    <p>Here is an example for an Options class that might parse
    options like those the cvs program takes</p>
<pre class="python">
from twisted.python import usage

class ImportOptions(usage.Options):
    optParameters = [
        ['module', 'm', None, None], ['vendor', 'v', None, None],
        ['release', 'r', None]
    ]

class CheckoutOptions(usage.Options):
    optParameters = [['module', 'm', None, None], ['tag', 'r', None, None]]

class Options(usage.Options):
    subCommands = [['import', None, ImportOptions, "Do an Import"],
                   ['checkout', None, CheckoutOptions, "Do a Checkout"]]
    
    optParameters = [
        ['compression', 'z', 0, 'Use compression'],
        ['repository', 'r', None, 'Specify an alternate repository']
    ]

config = Options(); config.parseOptions()
if config.subCommand == 'import':
    doImport(config.subOptions)
elif config.subCommand == 'checkout':
    doCheckout(config.subOptions)
</pre>
    
    <p>The <code>subCommands</code> attribute of <code>Options</code>
    directs the parser to the two other <code>Options</code> subclasses
    when the strings <code>"import"</code> or <code>"checkout"</code> are
    present on the command
    line.  All options after the given command string are passed to the
    specified Options subclass for further parsing.  Only one subcommand
    may be specified at a time.  After parsing has completed, the Options
    instance has two new attributes - <code>subCommand</code> and <code>
    subOptions</code> - which hold the command string and the Options
    instance used to parse the remaining options.</p>

    <h2>Generic Code For Options</h2>

    <p>Sometimes, just setting an attribute on the basis of the
    options is not flexible enough. In those cases, Twisted does
    not even attempt to provide abstractions such as <q>counts</q> or
    <q>lists</q>, but rathers lets you call your own method, which will
    be called whenever the option is encountered.</p>

    <p>Here is an example of counting verbosity</p>
<pre class="python">
from twisted.python import usage

class Options(usage.Options):

    def __init__(self):
        usage.Options.__init__(self)
        self['verbosity'] = 0 # default

    def opt_verbose(self):
        self['verbosity'] = self['verbosity']+1

    def opt_quiet(self):
        self['verbosity'] = self['verbosity']-1

    opt_v = opt_verbose
    opt_q = opt_quiet
</pre>

    <p>Command lines that look like
    <code class="shell">command -v -v -v -v</code> will
    increase verbosity to 4, while
    <code class="shell">command -q -q -q</code> will decrease
    verbosity to -3. 
    </p>

    <p>The <code class="API" base="twisted.python">usage.Options</code>
    class knows that these are
    parameter-less options, since the methods do not receive an
    argument. Here is an example for a method with a parameter:</p>

<pre class="python">
from twisted.python import usage

class Options(usage.Options):

    def __init__(self):
        usage.Options.__init__(self)
        self['symbols'] = []

    def opt_define(self, symbol):
        self['symbols'].append(symbol)

    opt_D = opt_define
</pre>

    <p>This example is useful for the common idiom of having
    <code>command -DFOO -DBAR</code> to define symbols.</p>

    <h2>Parsing Arguments</h2>

    <p><code>usage.Options</code> does not stop helping when the
    last parameter is gone. All the other arguments are sent into a
    function which should deal with them. Here is an example for a
    <code>cmp</code> like command.</p>
<pre class="python">
from twisted.python import usage

class Options(usage.Options):

    optParameters = [["max_differences", "d", 1, None]]

    def parseArgs(self, origin, changed):
        self['origin'] = origin
        self['changed'] = changed
</pre>

    <p>The command should look like <code>command origin
    changed</code>.</p>

    <p>If you want to have a variable number of left-over
    arguments, just use <code>def parseArgs(self, *args):</code>.
    This is useful for commands like the UNIX
    <code>cat(1)</code>.</p>

    <h2>Post Processing</h2>

    <p>Sometimes, you want to perform post processing of options to
    patch up inconsistencies, and the like. Here is an example:</p>
<pre class="python">
from twisted.python import usage

class Options(usage.Options):

    optFlags = [
        ["fast", "f", "Run quickly"],
        ["good", "g", "Don't validate input"],
        ["cheap", "c", "Use cheap resources"]
    ]

    def postOptions(self):
        if self['fast'] and self['good'] and self['cheap']:
            raise usage.UsageError, "can't have it all, brother"
</pre>

  </body>
</html>