如何让optparse的OptionParser忽略无效选项?
在Python的OptionParser
中,我该如何让它忽略在parse_args
方法中提供的未定义选项呢?
比如说,
我只为我的OptionParser
实例定义了选项--foo
,但是我调用parse_args
时传入的列表是:[ '--foo', '--bar' ]
我不在乎它是否会把这些未定义的选项从原始列表中过滤掉。我只想让未定义的选项被忽略。
我这么做的原因是因为我在使用SCons的AddOption
接口来添加自定义构建选项。不过,其中一些选项会影响目标的声明。因此,我需要在脚本的不同地方从sys.argv
中解析出这些选项,而不需要访问所有的选项。最后,顶层的SCons OptionParser
会捕捉到命令行中所有未定义的选项。
5 个回答
默认情况下,当你传入一个未定义的选项时,调用 error()
的行为是无法修改的。根据文档中关于 optparse 如何处理错误 的部分:
如果 optparse 默认的错误处理方式不符合你的需求,你需要 创建一个 OptionParser 的子类,并重写它的 exit() 和/或 error() 方法。
最简单的例子是:
class MyOptionParser(OptionParser):
def error(self, msg):
pass
这样做会让所有对 error()
的调用都不执行任何操作。当然,这并不是最理想的做法,但我认为这能说明你需要做的事情。记住 error()
的文档说明,你在继续时应该没问题:
将包含 'msg' 的使用信息打印到标准错误输出,并 退出。 如果你在子类中重写这个方法,它不应该返回——它 应该要么退出,要么抛出一个异常。
这里有一种方法,可以把不确定的参数添加到 OptionParser.parse_args
的结果 args
中,这里用到了一个简单的子类。
from optparse import (OptionParser,BadOptionError,AmbiguousOptionError)
class PassThroughOptionParser(OptionParser):
"""
An unknown option pass-through implementation of OptionParser.
When unknown arguments are encountered, bundle with largs and try again,
until rargs is depleted.
sys.exit(status) will still be called if a known argument is passed
incorrectly (e.g. missing arguments or bad argument types, etc.)
"""
def _process_args(self, largs, rargs, values):
while rargs:
try:
OptionParser._process_args(self,largs,rargs,values)
except (BadOptionError,AmbiguousOptionError), e:
largs.append(e.opt_str)
下面是一个代码片段,来展示这个方法是有效的:
# Show that the pass-through option parser works.
if __name__ == "__main__": #pragma: no cover
parser = PassThroughOptionParser()
parser.add_option('-k', '--known-arg',dest='known_arg',nargs=1, type='int')
(options,args) = parser.parse_args(['--shazbot','--known-arg=1'])
assert args[0] == '--shazbot'
assert options.known_arg == 1
(options,args) = parser.parse_args(['--k','4','--batman-and-robin'])
assert args[0] == '--batman-and-robin'
assert options.known_arg == 4
根据synack在其他回答评论中的要求,我在这里分享一个解决方案的小技巧,这个技巧是在把输入传递给父级的OptionParser
之前,先对输入进行清理。
import optparse
import re
import copy
import SCons
class NoErrOptionParser(optparse.OptionParser):
def __init__(self,*args,**kwargs):
self.valid_args_cre_list = []
optparse.OptionParser.__init__(self, *args, **kwargs)
def error(self,msg):
pass
def add_option(self,*args,**kwargs):
self.valid_args_cre_list.append(re.compile('^'+args[0]+'='))
optparse.OptionParser.add_option(self, *args, **kwargs)
def parse_args(self,*args,**kwargs):
# filter out invalid options
args_to_parse = args[0]
new_args_to_parse = []
for a in args_to_parse:
for cre in self.valid_args_cre_list:
if cre.match(a):
new_args_to_parse.append(a)
# nuke old values and insert the new
while len(args_to_parse) > 0:
args_to_parse.pop()
for a in new_args_to_parse:
args_to_parse.append(a)
return optparse.OptionParser.parse_args(self,*args,**kwargs)
def AddOption_and_get_NoErrOptionParser( *args, **kwargs):
apply( SCons.Script.AddOption, args, kwargs)
no_err_optparser = NoErrOptionParser(optparse.SUPPRESS_USAGE)
apply(no_err_optparser.add_option, args, kwargs)
return no_err_optpars