我一直在使用argparse
的Python程序,可以-process
,-upload
或两者:
parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('-process', action='store_true')
parser.add_argument('-upload', action='store_true')
args = parser.parse_args()
没有至少一个参数,该程序毫无意义。如何配置argparse
以强制选择至少一个参数?
更新:
评论之后:使用至少一个选项参数化程序的Pythonic方法是什么?
if not (args.process or args.upload):
parser.error('No action requested, add -process or -upload')
也许使用子解析器?
import argparse
parser = argparse.ArgumentParser(description='Log archiver arguments.')
subparsers = parser.add_subparsers(dest='subparser_name', help='sub-command help')
parser_process = subparsers.add_parser('process', help='Process logs')
parser_upload = subparsers.add_parser('upload', help='Upload logs')
args = parser.parse_args()
print("Subparser: ", args.subparser_name)
现在--help
显示:
$ python /tmp/aaa.py --help
usage: aaa.py [-h] {process,upload} ...
Log archiver arguments.
positional arguments:
{process,upload} sub-command help
process Process logs
upload Upload logs
optional arguments:
-h, --help show this help message and exit
$ python /tmp/aaa.py
usage: aaa.py [-h] {process,upload} ...
aaa.py: error: too few arguments
$ python3 /tmp/aaa.py upload
Subparser: upload
您也可以为这些子解析器添加其他选项。此外,您还可以绑定在给定子命令上直接调用的函数,而不是使用该dest='subparser_name'
(请参阅docs)。
args = vars(parser.parse_args())
if not any(args.values()):
parser.error('No arguments provided.')
如果不是'或两者'部分(我最初错过了这个)你可以使用这样的东西:
parser = argparse.ArgumentParser(description='Log archiver arguments.')
parser.add_argument('--process', action='store_const', const='process', dest='mode')
parser.add_argument('--upload', action='store_const', const='upload', dest='mode')
args = parser.parse_args()
if not args.mode:
parser.error("One of --process or --upload must be given")
尽管如此,使用subcommands可能更好一点。
我知道这很古老,但需要一个选项但禁止多个(XOR)的方式是这样的:
parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('-process', action='store_true')
group.add_argument('-upload', action='store_true')
args = parser.parse_args()
print args
输出:
>opt.py
usage: multiplot.py [-h] (-process | -upload)
multiplot.py: error: one of the arguments -process -upload is required
>opt.py -upload
Namespace(process=False, upload=True)
>opt.py -process
Namespace(process=True, upload=False)
>opt.py -upload -process
usage: multiplot.py [-h] (-process | -upload)
multiplot.py: error: argument -process: not allowed with argument -upload
argparse
(我会忽略这个)在命令行上生活时也有一些隐含的要求:
docopt
(file managelog.py
):"""Manage logfiles
Usage:
managelog.py [options] process -- <logfile>...
managelog.py [options] upload -- <logfile>...
managelog.py [options] process upload -- <logfile>...
managelog.py -h
Options:
-V, --verbose Be verbose
-U, --user <user> Username
-P, --pswd <pswd> Password
Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
from docopt import docopt
args = docopt(__doc__)
print args
尝试运行它:
$ python managelog.py
Usage:
managelog.py [options] process -- <logfile>...
managelog.py [options] upload -- <logfile>...
managelog.py [options] process upload -- <logfile>...
managelog.py -h
显示帮助:
$ python managelog.py -h
Manage logfiles
Usage:
managelog.py [options] process -- <logfile>...
managelog.py [options] upload -- <logfile>...
managelog.py [options] process upload -- <logfile>...
managelog.py -h
Options:
-V, --verbose Be verbose
-U, --user <user> Username
-P, --pswd <pswd> P managelog.py [options] upload -- <logfile>...
Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
并使用它:
$ python managelog.py -V -U user -P secret upload -- alfa.log beta.log
{'--': True,
'--pswd': 'secret',
'--user': 'user',
'--verbose': True,
'-h': False,
'<logfile>': ['alfa.log', 'beta.log'],
'process': False,
'upload': True}
short.py
甚至可以有更短的变体:
"""Manage logfiles
Usage:
short.py [options] (process|upload)... -- <logfile>...
short.py -h
Options:
-V, --verbose Be verbose
-U, --user <user> Username
-P, --pswd <pswd> Password
Manage log file by processing and/or uploading it.
If upload requires authentication, you shall specify <user> and <password>
"""
if __name__ == "__main__":
from docopt import docopt
args = docopt(__doc__)
print args
用法如下:
$ python short.py -V process upload -- alfa.log beta.log
{'--': True,
'--pswd': None,
'--user': None,
'--verbose': True,
'-h': False,
'<logfile>': ['alfa.log', 'beta.log'],
'process': 1,
'upload': 1}
请注意,“process”和“upload”键的布尔值不是计数器。
事实证明,我们无法阻止这些词的重复:
$ python short.py -V process process upload -- alfa.log beta.log
{'--': True,
'--pswd': None,
'--user': None,
'--verbose': True,
'-h': False,
'<logfile>': ['alfa.log', 'beta.log'],
'process': 2,
'upload': 1}
设计良好的命令行界面有时可能具有挑战性。
基于命令行的程序有多个方面:
argparse
提供了很多,但限制了可能的情况,并可能变得非常复杂。
使用docopt
时,事情会变得更短,同时保持可读性并提供高度的灵活性。如果你管理从字典中获取解析的参数并手动(或通过名为schema
的其他库)进行一些转换(到整数,打开文件..),你可能会发现docopt
非常适合命令行解析。
最好的方法是使用python内置模块add_mutually_exclusive_group。
parser = argparse.ArgumentParser(description='Log archiver arguments.')
group = parser.add_mutually_exclusive_group()
group.add_argument('-process', action='store_true')
group.add_argument('-upload', action='store_true')
args = parser.parse_args()
如果您只想通过命令行选择一个参数,则只需使用required = True作为组的参数
group = parser.add_mutually_exclusive_group(required=True)
如果您需要使用至少一个参数运行python程序,请添加一个没有选项前缀的参数( - 或 - 默认情况下)并设置nargs=+
(最少需要一个参数)。我发现这个方法的问题是,如果你没有指定参数,argparse将生成“太少的参数”错误而不打印出帮助菜单。如果您不需要该功能,请按以下步骤操作:
import argparse
parser = argparse.ArgumentParser(description='Your program description')
parser.add_argument('command', nargs="+", help='describe what a command is')
args = parser.parse_args()
我认为当你用选项前缀添加一个参数时,nargs会控制整个参数解析器,而不仅仅是选项。 (我的意思是,如果你有一个--option
旗帜与nargs="+"
,那么--option
旗帜至少需要一个参数。如果你有option
与nargs="+"
,它预计至少有一个论点。)
对于http://bugs.python.org/issue11588,我正在探索如何推广mutually_exclusive_group
概念来处理这样的案例。
随着这个发展argparse.py
,https://github.com/hpaulj/argparse_issues/blob/nested/argparse.py我能写:
parser = argparse.ArgumentParser(prog='PROG',
description='Log archiver arguments.')
group = parser.add_usage_group(kind='any', required=True,
title='possible actions (at least one is required)')
group.add_argument('-p', '--process', action='store_true')
group.add_argument('-u', '--upload', action='store_true')
args = parser.parse_args()
print(args)
产生以下help
:
usage: PROG [-h] (-p | -u)
Log archiver arguments.
optional arguments:
-h, --help show this help message and exit
possible actions (at least one is required):
-p, --process
-u, --upload
这接受输入,如'-u',' - up',' - proc --up'等。
它最终运行类似于https://stackoverflow.com/a/6723066/901925的测试,但错误消息需要更清楚:
usage: PROG [-h] (-p | -u)
PROG: error: some of the arguments process upload is required
我想知道:
kind='any', required=True
足够清楚(接受任何一组;至少需要一个)?(-p | -u)
清楚?必需的mutual_exclusive_group产生相同的东西。有没有替代符号?phihag's
简单测试更直观吗?将append_const用于操作列表,然后检查列表是否已填充:
parser.add_argument('-process', dest=actions, const="process", action='append_const')
parser.add_argument('-upload', dest=actions, const="upload", action='append_const')
args = parser.parse_args()
if(args.actions == None):
parser.error('Error: No actions requested')
您甚至可以直接在常量中指定方法。
def upload:
...
parser.add_argument('-upload', dest=actions, const=upload, action='append_const')
args = parser.parse_args()
if(args.actions == None):
parser.error('Error: No actions requested')
else:
for action in args.actions:
action()