具有依赖和冲突的python argparse子命令
Posted
技术标签:
【中文标题】具有依赖和冲突的python argparse子命令【英文标题】:python argparse subcommand with dependency and conflict 【发布时间】:2011-07-07 03:07:04 【问题描述】:我想使用 argparse 来构建一个带有子命令的工具。可能的语法是
/tool.py 下载 --from 1234 --interval 60
/tool.py 下载--build 1432
/tool.py clean --numbers 10
所以我想用argparse来实现:
-
确保始终同时使用“--from”和“--interval”
确保“--build”永远不会与其他参数一起使用
但我没有找到将“--from”和“--internal”配对到一个组,然后使该组与“--build”互斥的方法。
以下是我当前的代码,它只会使“--from”和“--build”互斥。既不能确保 '--from' 和 '--interval' 结合在一起,也不能确保 '--interval' 和 '--build' 是互斥的。
parser = argparse.ArgumentParser(description='A Tool')
subparsers = parser.add_subparsers(help='sub-command help')
#create the parser for the 'download' command
download_parser = subparsers.add_parser('download', help='download help')
download_parser.add_argument('--interval', dest='interval', type=int,help='interval help')
group = download_parser.add_mutually_exclusive_group()
group.add_argument('--from',type=int, help='from help')
group.add_argument('--build', type=int, help='interval help')
例如,
/tool.py 下载——来自 1234
不应允许,因为“--from”必须与“--interval”一起使用。但我的代码默默地接受它。
和
/tool.py 下载--interval 1234 --build 5678
不应被允许,因为“--build”不能与其他参数一起使用。但我的代码也接受它。
任何建议都将受到高度赞赏。谢谢。
【问题讨论】:
那里有代码,但究竟是什么不起作用?举一个错误行为的例子,并解释你的期望。 我添加了 2 个错误行为示例。感谢您的建议。 【参考方案1】:您可以为此使用custom actions:
import argparse
import sys
class VerifyNoBuild(argparse.Action):
def __call__(self, parser, args, values, option_string=None):
# print 'No: n v o'.format(n=args, v=values, o=option_string)
if args.build is not None:
parser.error(
'--build should not be used with --from or --interval')
setattr(args, self.dest, values)
class VerifyOnlyBuild(argparse.Action):
def __call__(self, parser, args, values, option_string=None):
# print 'Only: n v o'.format(n=args, v=values, o=option_string)
if getattr(args, 'from') is not None:
parser.error('--from should not be used with --build')
if getattr(args, 'interval') is not None:
parser.error('--interval should not be used with --build')
setattr(args, self.dest, values)
parser = argparse.ArgumentParser(description='A Tool')
subparsers = parser.add_subparsers(help='sub-command help')
# create the parser for the 'download' command
download_parser = subparsers.add_parser('download', help='download help')
download_parser.add_argument('--interval',
type=int, help='interval help',
action=VerifyNoBuild)
download_parser.add_argument('--from',
type=int, action=VerifyNoBuild)
download_parser.add_argument('--build',
type=int, action=VerifyOnlyBuild)
args = parser.parse_args('download --from 1234 --interval 60'.split())
print(args)
# Namespace(build=None, from=1234, interval=60)
args = parser.parse_args('download --build 1432'.split())
print(args)
# Namespace(build=1432, from=None, interval=None)
args = parser.parse_args('download --build 1432 --from 1234'.split())
print(args)
# usage: test.py download [-h] [--interval INTERVAL] [--from FROM] [--build BUILD]
# test.py download: error: --build should not be used with --from or --interval
args = parser.parse_args('download --build 1432 --interval 60'.split())
print(args)
# usage: test.py download [-h] [--interval INTERVAL] [--from FROM] [--build BUILD]
# test.py download: error: --build should not be used with --from or --interval
但实际上,我认为这更短更简单:
def parse_options():
parser = argparse.ArgumentParser(description='A Tool')
subparsers = parser.add_subparsers(help='sub-command help')
#create the parser for the 'download' command
download_parser = subparsers.add_parser('download', help='download help')
download_parser.add_argument('--interval', type=int, help='interval help')
download_parser.add_argument('--from', type=int)
download_parser.add_argument('--build', type=int)
opt=parser.parse_args()
from_interval=[getattr(opt,key) is not None for key in ('from','interval')]
if opt.build is not None:
if any(from_interval):
sys.exit('error!')
elif not all(from_interval):
sys.exit('error!')
return opt
【讨论】:
谢谢。自定义操作非常有用。我修改了您的代码以确保 --from 和 --interval 始终一起使用。它运作良好。谢谢! :) @Landy:啊,是的,我忘记了那个条件。您是否设法从VerifyNoBuild
类中验证了该条件?如果是这样,请您发布您的解决方案吗?我想看看这是怎么做到的。我已经修改了替代解决方案来处理这种情况。
抱歉回复晚了。其实我的代码很简单。正如你所说,我修改了 VerifyNoBuild 类,在调用 setattr() 之前添加了以下几行:if (args.begin is None) or (args.interval is None): sys.exit('--from and--interval must be used together')
@Landy:你确定这行得通吗? VerifyNoBuild
操作通常被调用两次(当同时提供 --from
和 --interval
时)。第一次调用VerifyNoBuild.__call__
,getattr(args,'from')
或getattr(args,'interval')
将是None
。即使提供了两个参数,这也会导致调用 sys.exit
。
你是对的。我只是测试了不可接受的论点,没有尝试可接受的论点。 :(((以上是关于具有依赖和冲突的python argparse子命令的主要内容,如果未能解决你的问题,请参考以下文章
子命令中选项的 argparse 冲突解决程序将关键字参数转换为位置参数