Argparse:指定可选参数时忽略多个位置参数

时间:2011-09-20 10:58:20

标签: python argparse

我试图让argparse忽略这样一个事实,即当指定了可选参数(-l)时,不应该评估两个通常需要的位置参数。

基本上我正在尝试复制--help的行为:当你指定-h时,忽略所有缺少的必需参数。

示例代码:

parser = argparse.ArgumentParser(description="Foo bar baz")
parser.add_argument('arg1', help='arg1 is a positional argument that does this')
parser.add_argument('arg2', help='arg2 is a positional argument that does this')
parser.add_argument('-l', '--list', dest='list', help='this is an optional argument that prints stuff')

options, args = parser.parse_args()

if options.list:
   print "I list stuff"

当然,如果我现在运行它,我会得到:

error: too few arguments

我尝试了nargs='?'之类的不同内容,但无法正常工作。

This question非常相似,但未得到回答。

6 个答案:

答案 0 :(得分:8)

不幸的是,argparse对此不够灵活。您可以做的最好的事情是使用arg1使arg2nargs="?"可选,并检查自己是否需要提供。

通过在命令行上遇到help-h时打印帮助消息并退出程序,即可实现内部--help操作。你可以自己写一个类似的动作,比如

class MyAction(argparse.Action):
    def __call__(self, parser, values, namespace, option_string):
        print "Whatever"
        parser.exit()

(警告:未经测试的代码!)

但后一种方法有明显的缺点。帮助消息将无条件地显示arg1arg2作为强制参数。解析命令行只会在遇到-l--list时停止,忽略任何进一步的参数。这种行为对于--help来说是完全可以接受的,但对于其他选项来说则不太理想。

答案 1 :(得分:6)

我遇到了这个问题并决定使用子命令。子命令可能有点过分,但是如果你发现你的程序在很多实例中没有使用某些位置参数(就像我一样),那么子命令可能是一个很好的解决方案。

对于您给出的示例,我将使用以下内容:

parser = argparse.ArgumentParser(description="Foo bar baz")
subparsers = parser.add_subparsers(description='available subcommands')

parser_main = subparsers.add_parser('<main_command_name>')
parser_main.add_argument('arg1', help='arg1 is a positional argument that does this')
parser_main.add_argument('arg2', help='arg2 is a positional argument that does this')

parser_list = subparsers.add_parser('list', help='this is a subcommand that prints stuff')

options, args = parser.parse_args()

我遗漏了您可能想要包含的一些详细信息(例如set_defaults(func=list)),argparse documentation中提到了这些详细信息。

答案 2 :(得分:1)

我认为nargs='*'很有帮助。

位置参数是可忽略的,那么你可以使用if检查位置参数是真还是假。

http://docs.python.org/library/argparse.html#nargs

答案 3 :(得分:1)

我可能在这里找到了解决方案。没错,这是一个肮脏的黑客,但它的确有效。

注意:以下所有内容均适用于Python 3.3.2。

根据答案hereparse_args检查哪些操作是必需的,如果缺少任何操作则抛出错误。我建议改写这种行为。

通过继承ArgumentParser,我们可以定义一个新的ArgumentParser.error方法(原始here),该方法将检查是否抛出了错误,因为缺少某些参数并采取必要的操作。代码如下:

import argparse
import sys
from gettext import gettext as _

class ArgumentParser(argparse.ArgumentParser):
    skip_list = []

    def error(self, message):
        # Let's see if we are missing arguments
        if message.startswith('the following arguments are required:'):
            missingArgs = message.split('required: ')[1].split(', ')
            newArgs = []    # Creating a list of whatever we should not skip but is missing
            for arg in missingArgs:
                if arg not in self.skip_list:
                    newArgs.append(arg)
                else:
                    self.skip_list.remove(arg)  # No need for it anymore
            if len(newArgs) == 0:
                return  # WARNING! UNTESTED! MAY LEAD TO SPACETIME MELTDOWN!
            else:   # Some required stuff is still missing, so we show a corrected error message
                message = _('the following arguments are required: %s') % ', '.join(newArgs)

        self.print_usage(sys.stderr)    # Original method behavior
        args = {'prog': self.prog, 'message': message}
        self.exit(2, _('%(prog)s: error: %(message)s\n') % args)

新方法首先检查错误是否是因为命令行中缺少参数(有关生成错误的代码,请参阅here)。如果是,则该方法从错误消息中获取参数的名称,并将它们放入列表(missingArgs)。

然后,我们遍历此列表并检查应跳过哪些参数,哪些仍然是必需的。要确定要跳过哪些参数,我们会将它们与skip_list进行比较。它是我们的ArgumentParser子类中的一个字段,它应该包含要跳过的参数的名称,即使它们是解析器所需的。请注意,恰好位于skip_list的参数会在找到时删除。

如果命令行中仍然缺少必需的参数,则该方法会抛出更正的错误消息。但是,如果应该跳过所有缺少的参数,则该方法返回。

警告! ArgumentParser.error的原始定义指出,如果它在子类中被覆盖,则不应返回,而是退出或引发异常。因此,此处显示的内容可能不安全,可能导致程序崩溃,计算机着火或更糟 - 可能会使您的所有茶叶蒸发。但是,似乎就像在这种特殊情况下(缺少必需的参数)从方法返回是安全的。但它可能不是。你被警告了。

为了填补skip_list,我们可以使用以下代码:

class SpecialHelp(argparse._HelpAction):
    def __call__(self, parser, namespace, values, option_string=None):
        parser.print_help()
        print()
        for action in parser._actions:
            if action != self and action.required:
                parser.skip_list.append(argparse._get_action_name(action))

此特定类模仿内置help操作,但不是退出,而是将所有剩余的必需参数插入skip_list

希望我的回答有所帮助,祝你好运。

答案 4 :(得分:0)

这可能很难看,但这就是我通常做的事情。

def print_list():
    the_list = ["name1", "name2"]
    return "{0}".format(the_list)

...
parser.add_argument("-l", "--list", action='version',
                    version=print_list(), help="print the list")

答案 5 :(得分:0)

到目前为止,我能找到的最干净的方法是将解析分为两个阶段。首先检查-l/--list

parser = argparse.ArgumentParser(description="Foo bar baz")
parser.add_argument('-l', '--list', dest='list', action='store_true',
                    help='this is an optional argument that prints stuff')

options, remainder = parser.parse_known_args()

现在,由于您使用了parse_known_args,所以到这里为止都不会出错,您可以决定如何处理参数remainder

if options.list:
    print "I list stuff"
else:
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument('arg1', help='arg1 is a positional argument that does this')
    parser.add_argument('arg2', help='arg2 is a positional argument that does this')
    options = parser.parse_args(remainder)

您可能希望在第一个解析器中设置用法选项,以使帮助字符串更好看。