我试图让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非常相似,但未得到回答。
答案 0 :(得分:8)
不幸的是,argparse
对此不够灵活。您可以做的最好的事情是使用arg1
使arg2
和nargs="?"
可选,并检查自己是否需要提供。
通过在命令行上遇到help
或-h
时打印帮助消息并退出程序,即可实现内部--help
操作。你可以自己写一个类似的动作,比如
class MyAction(argparse.Action):
def __call__(self, parser, values, namespace, option_string):
print "Whatever"
parser.exit()
(警告:未经测试的代码!)
但后一种方法有明显的缺点。帮助消息将无条件地显示arg1
和arg2
作为强制参数。解析命令行只会在遇到-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
检查位置参数是真还是假。
答案 3 :(得分:1)
我可能在这里找到了解决方案。没错,这是一个肮脏的黑客,但它的确有效。
注意:以下所有内容均适用于Python 3.3.2。
根据答案here,parse_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)
您可能希望在第一个解析器中设置用法选项,以使帮助字符串更好看。