如何让optparse的OptionParser忽略无效选项?

26 投票
5 回答
16816 浏览
提问于 2025-04-15 16:54

在Python的OptionParser中,我该如何让它忽略在parse_args方法中提供的未定义选项呢?

比如说,
我只为我的OptionParser实例定义了选项--foo,但是我调用parse_args时传入的列表是:[ '--foo', '--bar' ]

我不在乎它是否会把这些未定义的选项从原始列表中过滤掉。我只想让未定义的选项被忽略。

我这么做的原因是因为我在使用SCons的AddOption接口来添加自定义构建选项。不过,其中一些选项会影响目标的声明。因此,我需要在脚本的不同地方从sys.argv中解析出这些选项,而不需要访问所有的选项。最后,顶层的SCons OptionParser会捕捉到命令行中所有未定义的选项。

5 个回答

11

默认情况下,当你传入一个未定义的选项时,调用 error() 的行为是无法修改的。根据文档中关于 optparse 如何处理错误 的部分:

如果 optparse 默认的错误处理方式不符合你的需求,你需要 创建一个 OptionParser 的子类,并重写它的 exit() 和/或 error() 方法。

最简单的例子是:

class MyOptionParser(OptionParser):
    def error(self, msg):
        pass

这样做会让所有对 error() 的调用都不执行任何操作。当然,这并不是最理想的做法,但我认为这能说明你需要做的事情。记住 error() 的文档说明,你在继续时应该没问题:

将包含 'msg' 的使用信息打印到标准错误输出,并 退出。 如果你在子类中重写这个方法,它不应该返回——它 应该要么退出,要么抛出一个异常。

38

这里有一种方法,可以把不确定的参数添加到 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
1

根据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

撰写回答