There is frequently a need for programs to parse a UNIX-like
command line program: options preceded by -
or
--
, sometimes followed by a parameter, followed by a
list of arguments. The twisted.python.usage
provides a
class, Options
, to facilitate such parsing.
While Python has the getopt
module for doing this,
it provides a very low level of abstraction for options. Twisted
has a higher level of abstraction, in the class
twisted.python.usage.Options
. 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 "program -v -v -v
"
impossible), Twisted allows the programmer to decide how much
control she wants.
The Options
class is used by subclassing. Since a
lot of time it will be used in the twisted.tap
package, where the local conventions require the specific options
parsing class to also be called Options
, it is usually
imported with
from twisted.python import usage
For simple boolean options, define the attribute
optFlags
like this:
class Options(usage.Options): optFlags = [["fast", "f"], ["safe", "s"]]
optFlags
should be a list of 2-lists. The first
element is the long name, and will be used on the command line as
--fast
. The second one is the short name, and will be
used on the command line as -f
. The long name also
determines the name of the key that will be set in the
'opts' dict. Its value will be 1 if the option was seen, 0 otherwise.
Here is an example for usage:
class Options(usage.Options): optFlags = [["fast", "f"], ["good", "g"], ["cheap", "c"]] command_line = ["-g", "--fast"] options = Options() options.parseOptions(command_line) if options.opts['fast']: print "fast", if options.opts['good']: print "good", if options.opts['cheap']: print "cheap", print
The above will print fast good
.
Sometimes there is a need for several option processors with a
unifying core. Perhaps you want all your commands to understand
-q
/--quiet
means to be quiet, or
something similar. On the face of it, this looks impossible: in
Python, the subclass's optFlags
would shadow the
superclass's. However, usage.Options
uses special
reflection code to get all of the optFlag
s defined in
the hierarchy. So the following:
class BaseOptions(usage.Options): optFlags = [["quiet", "q"]] class SpecificOptions(BaseOptions): optFlags = [["fast", "f"], ["good", "g"], ["cheap", "c"]]Is the same as:
class SpecificOptions(BaseOptions): optFlags = [["quiet", "q"], ["fast", "f"], ["good", "g"], ["cheap", "c"]]
String options are specified using the attribute
optParameters
. They must 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.
Here is an example:
from twisted.python import usage class Options(usage.Options): optFlags = [["fast", "f"], ["good", "g"], ["cheap", "c"]] optParameters = [["user", "u", None]] config.parseOptions() # When given no argument, parses sys.argv[1:] if config.opts['user'] is not None: print "Hello," config.opts['user'] print "So, you want it:" if config.opts['fast']: print "fast", if config.opts['good']: print "good", if config.opts['cheap']: print "cheap", print
Like optFlags
, optParameters
works
smoothly with inheritance.
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 "counts" or "lists", but rathers lets you call your own method, which will be called whenever the option is encountered.
Here is an example of counting verbosity
from twisted.python import usage class Options(usage.Options): verbosity = 0 # default def opt_verbose(self): self.opts['verbosity'] = self.opts['verbosity']+1 def opt_quiet(self): self.opts['verbosity'] = self.['verbosity']-1 opt_v = opt_verbose opt_q = opt_quiet
Command lines that like like "command -v -v -v -v" will increase verbosity to 4, while "command -q -q -q" will decrease verbosity to -3. Note that while it is not required, it is recommended that you store option-related state in the 'self.opts' dict, which is where the rest of the options are stored.
The usage.Options
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:
from twisted.python import usage class Options(usage.Options): def __init__(self): usage.Options.__init__(self) self.opts['symbols'] = [] def opt_define(self, symbol): self.opts['symbols'].append(symbol) opt_D = opt_define
This example is useful for the common idiom of having
command -DFOO -DBAR
to define symbols.
usage.Options
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
cmp
like command.
from twisted.python import usage class Options(usage.Options): optParameters = [["max_differences", "d", 1]] def parseArgs(self, origin, changed): self.opts['origin'] = origin self.opts['changed'] = changed
The command should look like command origin
changed
.
If you want to have a variable number of left-over arguments,
just use def parseArgs(self, *args):
. This is useful
for commands like the UNIX cat(1)
.
Sometimes, you want to perform post processing of options to patch up inconsistencies, and the like. Here is an example:
from twisted.python import usage class Options(usage.Options): optFlags = [["fast", "f"], ["good", "g"], ["cheap", "c"]] def postOptions(self): if self.opts['fast'] and self.opts['good'] and self.opts['cheap']: raise usage.UsageError, "can't have it all, brother"
twisted.tap
Modules