Using usage.Options

-- Jerry Marx [2004-09-15 21:41:07]

Using usage.Options

1. 介绍

(Introduction)

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.

程序需要频繁的分析类UNIX的命令行:由-或者--引导的选项,有时后面跟着一个选项,有时是一系列."twisted.python.usage"提供了一个类Options来做这样的命令行解析.

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.

Python本身的模块getopt也可以做这件事,它提供了一个非常低层次的选项提取.Twisted有一个高层次的选项提取,就是类twisted.python.usage.Options.他使用了Python的反射机制,提供更容易使用的非常灵活的命令行接口.大多数命令行处理或者要求应用程序有自己的处理循环,或者对于命令行格式有一些武断的限制(最常见的就是不能处理一个选项多次出现,解析类似于 program -v -v -v 是不可能的),而Twisted则允许程序员自己决定他(她)控制到什么程度.

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

类Options一般通过子类来使用.由于在twisted.tag包种也使用了它,而且因为一些习惯和其它原因它自己的选项分析类也叫做Options,因此通常用如下的方式来引入这个类

   1 from twisted.python import usage

2. Boolean Options

For simple boolean options, define the attribute optFlags like this:

对于简单的布尔选项,可以定义属性optFlags:

   1 class Options(usage.Options):
   2 
   3     optFlags = [["fast", "f", "Act quickly"], ["safe", "s", "Act safely"]]

optFlags should be a list of 3-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 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:

optFlags应该是一个每个表项都是一个三项列表的列表,第一个项是长名称,可以这样使用: --fast. 第二个项是短名称,可以这样使用: -f. 最后一个项是用于使用说明文字的关于这个选项的描述文字.长名字也决定了在Options实例这个选项对应的变量的名称.如果有这个选项,变量就设置为1,如果没有就设置为0.下面是个例子:

   1 class Options(usage.Options):
   2 
   3     optFlags = [
   4         ["fast", "f", "Act quickly"],
   5         ["good", "g", "Act well"],
   6         ["cheap", "c", "Act cheaply"]
   7     ]
   8 
   9 command_line = ["-g", "--fast"]
  10 
  11 options = Options()
  12 try:
  13     options.parseOptions(command_line)
  14 except usage.UsageError, errortext:
  15     print '%s: %s' % (sys.argv[0], errortext)
  16     print '%s: Try --help for usage details.' % (sys.argv[0])
  17     sys.exit(1)
  18 if options['fast']:
  19     print "fast",
  20 if options['good']:
  21     print "good",
  22 if options['cheap']:
  23     print "cheap",
  24 print

The above will print fast good.

上面的程序会打印: fast good.

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.

注意这里Options完全支持mapping接口.可以像访问其它的字典一样访问它.在Options实例中各个选项作为字典的项被存入,parameters作为'paramname': 'value'和flags作为'flagname': 1或者0.

2.1. 继承,或者说:我如何才能爱上Superclass,不再因它而烦心?

(Inheritance, Or: How I Learned to Stop Worrying and Love the Superclass)

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 optFlags defined in the hierarchy. So the following:

有时需要用同样的方式来处理几个选项,也许你想让你所有的命令都理解 -q/--quiet 的意思就是安静,或者类似的其它东西.从表面上看起来这是不可能的,子类的optFlags会覆盖父类中的optFlags.然而,usage.Options使用了特殊的反射代码获得整个继承体系的定义,所以下面这样:

   1 class BaseOptions(usage.Options):
   2 
   3     optFlags = [["quiet", "q", None]]
   4 
   5 class SpecificOptions(BaseOptions):
   6 
   7     optFlags = [
   8         ["fast", "f", None], ["good", "g", None], ["cheap", "c", None]
   9     ]

Is the same as:

和下面这样是相同的

   1 class SpecificOptions(BaseOptions):
   2 
   3     optFlags = [
   4         ["quiet", "q", "Silence output"],
   5         ["fast", "f", "Run quickly"],
   6         ["good", "g", "Don't validate input"],
   7         ["cheap", "c", "Use cheap resources"]
   8     ]

3. Parameters

Parameters 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.

Parameters被指定使用了属性optParameters.他们必需给定一个缺省值.如果你想确认你从命令行获得了一个parameter,给他一个非字符串作为缺省值.因命令行只会产生字符串,这样做是可靠的.

Here is an example:

下面是个例子:

   1 from twisted.python import usage
   2 
   3 class Options(usage.Options):
   4 
   5     optFlags = [
   6         ["fast", "f", "Run quickly"],
   7         ["good", "g", "Don't validate input"],
   8         ["cheap", "c", "Use cheap resources"]
   9     ]
  10     optParameters = [["user", "u", None, "The user name"]]
  11 
  12 config = Options()
  13 try:
  14     config.parseOptions() # When given no argument, parses sys.argv[1:]
  15 except usage.UsageError, errortext:
  16     print '%s: %s' % (sys.argv[0], errortext)
  17     print '%s: Try --help for usage details.' % (sys.argv[0])
  18     sys.exit(1)
  19 
  20 if config['user'] is not None:
  21     print "Hello", config['user']
  22 print "So, you want it:"
  23 
  24 if config['fast']:
  25     print "fast",
  26 if config['good']:
  27     print "good",
  28 if config['cheap']:
  29     print "cheap",
  30 print

Like optFlags, optParameters works smoothly with inheritance.

就像optFlags一样,optPrameters也可以在继承关系下很好的工作.

4. 可选择的子命令(Option Subcommands)

It is useful, on occassion, to group a set of options together based on the logical action to which they belong. For this, the usage.Options class allows you to define a set of subcommands, each of which can provide its own usage.Options instance to handle its particular options.

在某些场合下,把一组逻辑相关的选项组织在一起很有用.使用usage.Options类可以定义一组子命令,每个子命令都有一个它自己的usage.Options实例来处理他自己的选项.

Here is an example for an Options class that might parse options like those the cvs program takes

下面是一个可以处理类似cvs命令的例子:

   1 from twisted.python import usage
   2 
   3 class ImportOptions(usage.Options):
   4     optParameters = [
   5         ['module', 'm', None, None], ['vendor', 'v', None, None],
   6         ['release', 'r', None]
   7     ]
   8 
   9 class CheckoutOptions(usage.Options):
  10     optParameters = [['module', 'm', None, None], ['tag', 'r', None, None]]
  11 
  12 class Options(usage.Options):
  13     subCommands = [['import', None, ImportOptions, "Do an Import"],
  14                    ['checkout', None, CheckoutOptions, "Do a Checkout"]]
  15 
  16     optParameters = [
  17         ['compression', 'z', 0, 'Use compression'],
  18         ['repository', 'r', None, 'Specify an alternate repository']
  19     ]
  20 
  21 config = Options(); config.parseOptions()
  22 if config.subCommand == 'import':
  23     doImport(config.subOptions)
  24 elif config.subCommand == 'checkout':
  25     doCheckout(config.subOptions)

The subCommands attribute of Options directs the parser to the two other Options subclasses when the strings "import" or "checkout" 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 - subCommand and subOptions - which hold the command string and the Options instance used to parse the remaining options.

当命令行宗出现字符串"import"或者"checkout"地时候,选项的子命令属性就会指引分析器去分析另外两个选项子类.在给定命令串后所有的选项都会传递给特定的Options子类去进一步解析.每次只会指定一个子命令.解析完成后,Options实例有两个新属性 - subCommand 和 subOptions - 他们持有用来解析命令行剩余选项所需要的命令字符串和Options实例.

5. 实现选项的通用代码(Generic Code For Options)

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.

有时,只是把属性设置给options并不够灵活.对应这种情况,Twisted并没有试图提供像counts或者lists这样的属性,而是允许你调用你自己的方法,这些方法可以在选项被计数的任何时候调用.

Here is an example of counting verbosity

下面是一个对冗长参数的计数的例子:

   1 from twisted.python import usage
   2 
   3 class Options(usage.Options):
   4 
   5     def __init__(self):
   6         usage.Options.__init__(self)
   7         self['verbosity'] = 0 # default
   8 
   9     def opt_verbose(self):
  10         self['verbosity'] = self['verbosity']+1
  11 
  12     def opt_quiet(self):
  13         self['verbosity'] = self['verbosity']-1
  14 
  15     opt_v = opt_verbose
  16     opt_q = opt_quiet

Command lines that look like command -v -v -v -v will increase verbosity to 4, while command -q -q -q will decrease verbosity to -3.

如 command -v -v -v -v 的命令行将会使verbosity增加到4, command -q -q -q 会使verbosity增加到-3.

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:

usage.Options类知道哪些无参数的选项,因此那些方法不接受参数.下面是个例子:

   1 from twisted.python import usage
   2 
   3 class Options(usage.Options):
   4 
   5     def __init__(self):
   6         usage.Options.__init__(self)
   7         self['symbols'] = []
   8 
   9     def opt_define(self, symbol):
  10         self['symbols'].append(symbol)
  11 
  12     opt_D = opt_define

This example is useful for the common idiom of having command -DFOO -DBAR to define symbols

这个例子对于定义符号的习惯用法 -DFOO -DBAR 很有用.

6. 解析参数(Parsing Arguments)

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.

usage.Options并没有在分析完最后一个参数就停止.所有的其它的参数都被送到相应的处理函数中去处理,下面是一个类似cmp的例子:

   1 from twisted.python import usage
   2 
   3 class Options(usage.Options):
   4 
   5     optParameters = [["max_differences", "d", 1, None]]
   6 
   7     def parseArgs(self, origin, changed):
   8         self['origin'] = origin
   9         self['changed'] = changed

The command should look like command origin changed.

这个命令看起来应该是 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).

如果你想有不定参数,只需要使用定义 parseArgs(self, *args):.这对于像UNIX cat(1)之类的命令很有用.

7. 后处理(Post Processing)

Sometimes, you want to perform post processing of options to patch up inconsistencies, and the like. Here is an example:

有时,你想在处理完选项后处理冲突,或者其它事情.下面是个例子:

   1 from twisted.python import usage
   2 
   3 class Options(usage.Options):
   4 
   5     optFlags = [
   6         ["fast", "f", "Run quickly"],
   7         ["good", "g", "Don't validate input"],
   8         ["cheap", "c", "Use cheap resources"]
   9     ]
  10 
  11     def postOptions(self):
  12         if self['fast'] and self['good'] and self['cheap']:
  13             raise usage.UsageError, "can't have it all, brother"
  • 翻译 -- Jerry Marx

"目录(Index)"

Version 1.3.0