使用 usage.Options 解析命令行¶
简介¶
程序经常需要解析类似 UNIX 的命令行程序:以 -
或 --
开头的选项,有时后面跟着参数,最后跟着参数列表。 twisted.python.usage
提供了一个类 Options
来简化这种解析。
虽然 Python 有 getopt
模块可以实现这一点,但它对选项的抽象级别非常低。Twisted 在 twisted.python.usage.Options
类中提供了更高层次的抽象。它使用 Python 的反射机制为命令行提供了一个易于使用且灵活的接口。虽然大多数命令行处理器要么强制应用程序编写者编写自己的循环,要么对命令行施加任意限制(最常见的是不能拥有同一个选项的多个实例,因此无法使用 program -v -v -v
这样的语法),Twisted 允许程序员决定他们想要多少控制权。
Options
类通过子类化使用。由于它经常在 twisted.tap
包中使用,而本地约定要求特定的选项解析类也称为 Options
,因此通常使用以下方式导入它
from twisted.python import usage
布尔选项¶
对于简单的布尔选项,请像这样定义属性 optFlags
class Options(usage.Options):
optFlags = [["fast", "f", "Act quickly"], ["safe", "s", "Act safely"]]
optFlags
应该是一个包含 3 个列表的列表。第一个元素是长名称,将在命令行中用作 --fast
。第二个元素是短名称,将在命令行中用作 -f
。最后一个元素是标志的描述,将用于生成使用信息文本。长名称也决定了将在 Options 实例上设置的键的名称。如果看到该选项,它的值将为 1,否则为 0。以下是一个使用示例
from __future__ import print_function
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 as errortext:
print('{}: {}'.format(sys.argv[0], errortext))
print('{}: Try --help for usage details.'.format(sys.argv[0]))
sys.exit(1)
if options['fast']:
print("fast", end='')
if options['good']:
print("good", end='')
if options['cheap']:
print("cheap", end='')
print()
以上代码将打印 fast good
。
请注意,Options 完全支持映射接口。您可以像访问任何其他字典一样访问它。选项作为映射项存储在 Options 实例中:参数为 'paramname':'value',标志为 'flagname': 1 或 0。
继承,或者:我如何学会停止担心并爱上超类¶
有时需要多个选项处理器,它们具有一个统一的核心。也许您希望所有命令都理解 -q
/--quiet
表示静默,或者类似的东西。从表面上看,这似乎不可能:在 Python 中,子类的 optFlags
将覆盖超类的 optFlags
。但是,usage.Options
使用特殊的反射代码来获取层次结构中定义的所有 optFlags
。因此,以下代码
class BaseOptions(usage.Options):
optFlags = [["quiet", "q", None]]
class SpecificOptions(BaseOptions):
optFlags = [
["fast", "f", None], ["good", "g", None], ["cheap", "c", None]
]
与以下代码相同
class SpecificOptions(usage.Options):
optFlags = [
["quiet", "q", "Silence output"],
["fast", "f", "Run quickly"],
["good", "g", "Don't validate input"],
["cheap", "c", "Use cheap resources"]
]
参数¶
参数使用属性 optParameters
指定。它们 *必须* 给定一个默认值。如果您想确保从命令行获取了参数,请给定一个非字符串默认值。由于命令行只有字符串,因此这完全可靠。
以下是一个示例
from __future__ import print_function
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 as errortext:
print('{}: {}'.format(sys.argv[0], errortext))
print('{}: Try --help for usage details.'.format(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", end='')
if config['good']:
print("good", end='')
if config['cheap']:
print("cheap", end='')
print()
与 optFlags
一样,optParameters
与继承一起顺利工作。
选项子命令¶
有时,根据逻辑“操作”将一组选项分组在一起非常有用。为此,usage.Options
类允许您定义一组“子命令”,每个子命令都可以提供自己的 usage.Options
实例来处理其特定的选项。
以下是一个 Options 类示例,它可能解析类似于 cvs 程序所接受的选项
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)
Options
的 subCommands
属性指示解析器在命令行中出现字符串 "import"
或 "checkout"
时,使用另外两个 Options
子类。给定命令字符串后的所有选项都将传递给指定的 Options 子类以进行进一步解析。一次只能指定一个子命令。解析完成后,Options 实例将有两个新属性 - subCommand
和 subOptions
- 它们分别保存命令字符串和用于解析剩余选项的 Options 实例。
选项的通用代码¶
有时,仅仅根据选项设置属性还不够灵活。在这些情况下,Twisted 甚至不会尝试提供诸如“计数”或“列表”之类的抽象,而是让您调用自己的方法,该方法将在遇到选项时被调用。
以下是一个计算详细程度的示例
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
类似于 command -v -v -v -v
的命令行将详细程度增加到 4,而 command -q -q -q
将详细程度降低到 -3。
usage.Options
类知道这些是无参数选项,因为方法没有接收参数。以下是一个带参数方法的示例
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
此示例对于使用 command -DFOO -DBAR
定义符号的常见语法很有用。
解析参数¶
usage.Options
在最后一个参数消失后不会停止帮助。所有其他参数都将发送到一个函数中,该函数应该处理它们。以下是一个类似于 cmp
命令的示例。
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
该命令应该类似于 command origin changed
。
如果您想拥有可变数量的剩余参数,只需使用 def parseArgs(self, *args):
。这对于类似于 UNIX cat(1)
的命令很有用。
后处理¶
有时,您可能希望对选项进行后处理以修复不一致之处等等。以下是一个示例
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")
类型强制¶
默认情况下,所有选项都作为字符串处理。您可能希望在某些特定情况下强制执行选项的类型,最典型的例子是端口号。任何可调用对象都可以指定在 optParameters
的第五行中,并且将使用传递的参数字符串值调用它。
from twisted.python import usage
class Options(usage.Options):
optParameters = [
["shiny_integer", "s", 1, None, int],
["dummy_float", "d", 3.14159, None, float],
]
请注意,默认值不会被强制转换,因此您应该要么使用正确的类型声明它(如上所示),要么在使用选项时处理它。
coerce 函数可能包含一个 coerceDoc 属性,其内容将在选项文档之后打印。这在多个地方重复使用该函数时特别有用。
def oneTwoThree(val):
val = int(val)
if val not in range(1, 4):
raise ValueError("Not in range")
return val
oneTwoThree.coerceDoc = "Must be 1, 2 or 3."
from twisted.python import usage
class Options(usage.Options):
optParameters = [["one_choice", "o", 1, None, oneTwoThree]]
将以下示例代码添加到您的程序中,将打印以下帮助信息
$ python myprogram.py --help
Usage: myprogram [options]
Options:
-o, --one_choice= [default: 0]. Must be 1, 2 or 3.
Shell 选项卡自动完成¶
Options
类可以为交互式命令 shell 提供选项卡自动完成功能。目前仅支持 zsh
,但未来可能会支持 bash
。
Twisted 附带的所有命令都自动支持选项卡自动完成。多年来,Zsh 一直提供一个完成函数,该函数与 Options
类提供的支持相结合。
如果您正在编写 twistd
插件,那么您的 twistd
子命令的选项卡自动完成也是自动的。
对于其他命令,您可以轻松地提供 zsh 选项卡自动完成支持。复制文件“twisted/python/twisted-completion.zsh”并将其命名为类似“_mycommand”的文件。zsh 使用以下划线开头的无扩展名作为完成函数文件的约定。
编辑新文件并将第一行更改为仅引用您的新命令,如下所示
#compdef mycommand
然后,通过将该文件放在 zsh 的 $fpath 中出现的目录之一中,确保该文件对 shell 可用。重新启动 zsh 并确保启用高级完成 (autoload -U compinit; compinit)
)。然后,您应该能够键入命令名称并按 Tab 键以完成命令行选项。
完成元数据¶
可选地,可以在您的 Options
子类上定义一个特殊属性 compData
,以向 shell 自动完成系统提供更多信息。该属性应该是一个 I DON’T KNOW WHAT TO DO WITH THIS LINK! 的实例。
此外,可以在继承层次结构中的父类上定义 compData
。来自每个 I DON’T KNOW WHAT TO DO WITH THIS LINK! 的信息。