Python argparse:至少需要一个参数

发布于 2024-11-25 05:18:48 字数 462 浏览 4 评论 0原文

我一直在使用 argparse 作为一个可以 -process-upload 或两者兼有的 Python 程序:

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 方法是什么?

I've been using argparse for a Python program that can -process, -upload or both:

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()

The program is meaningless without at least one parameter. How can I configure argparse to force at least one parameter to be chosen?

UPDATE:

Following the comments: What's the Pythonic way to parametrize a program with at least one option?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

星星的轨迹 2024-12-02 05:18:48
if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')
if not (args.process or args.upload):
    parser.error('No action requested, add -process or -upload')
岁月静好 2024-12-02 05:18:48

我知道这已经很古老了,但是需要一个选项但禁止多个选项(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  

I know this is old as dirt, but the way to require one option but forbid more than one (XOR) is like this:

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)
    

Output:

>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  
温暖的光 2024-12-02 05:18:48
args = vars(parser.parse_args())
if not any(args.values()):
    parser.error('No arguments provided.')
args = vars(parser.parse_args())
if not any(args.values()):
    parser.error('No arguments provided.')
财迷小姐 2024-12-02 05:18:48

如果不是“或两者”部分(我最初错过了这一点),你可以使用这样的东西:

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")

不过,使用 子命令 代替。

If not the 'or both' part (I have initially missed this) you could use something like this:

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")

Though, probably it would be a better idea to use subcommands instead.

凌乱心跳 2024-12-02 05:18:48

需求审查

  • 使用 argparse(我将忽略这一点)
  • 允许调用一两个操作(至少需要一个)。
  • 尝试使用Pythonic(我宁愿称其为“POSIX”式的)

在命令行中也有一些隐含的要求:

  • 以易于理解的方式向用户解释用法
  • 选项应该是可选的
  • 允许指定标志和选项
  • 允许与其他参数(如文件名或名称)组合。

使用 docopt 的示例解决方案(文件 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 非常适合命令行解析。

Requirements Review

  • use argparse (I will ignore this one)
  • allow one or two actions to be called (at least one required).
  • try to by Pythonic (I would rather call it "POSIX"-like)

There are also some implicit requirements when living on command line:

  • explain the usage to the user in a way which is easy to understand
  • options shall be optional
  • allow specifying flags and options
  • allow combining with other parameters (like file name or names).

Sample solution using 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

Try to run it:

$ python managelog.py
Usage:
    managelog.py [options] process -- <logfile>...
    managelog.py [options] upload -- <logfile>...
    managelog.py [options] process upload -- <logfile>...
    managelog.py -h

Show the help:

$ 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>

And use it:

$ 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 alternative short.py

There can be even shorter variant:

"""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

Usage looks like this:

$ 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}

Note, that instead of boolean values for "process" and "upload" keys there are counters.

It turns out, we cannot prevent duplication of these words:

$ 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}

Conclusions

Designing good command line interface can be challenging sometime.

There are multiple aspects of command line based program:

  • good design of command line
  • selecting/using proper parser

argparse offers a lot, but restricts possible scenarios and can become very complex.

With docopt things go much shorter while preserving readability and offering high degree of flexibility. If you manage getting parsed arguments from dictionary and do some of conversions (to integer, opening files..) manually (or by other library called schema), you may find docopt good fit for command line parsing.

御守 2024-12-02 05:18:48

对于http://bugs.python.org/issue11588,我正在探索概括mutually_exclusive_group的方法 概念来处理这样的情况。

通过此开发 argparse.pyhttps://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 的 简单测试更直观吗?

For http://bugs.python.org/issue11588 I am exploring ways of generalizing the mutually_exclusive_group concept to handle cases like this.

With this development argparse.py, https://github.com/hpaulj/argparse_issues/blob/nested/argparse.py
I am able to write:

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)

which produces the following 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

This accepts inputs like '-u', '-up', '--proc --up' etc.

It ends up running a test similar to https://stackoverflow.com/a/6723066/901925, though the error message needs to be clearer:

usage: PROG [-h] (-p | -u)
PROG: error: some of the arguments process upload is required

I wonder:

  • are the parameters kind='any', required=True clear enough (accept any of the group; at least one is required)?

  • is usage (-p | -u) clear? A required mutually_exclusive_group produces the same thing. Is there some alternative notation?

  • is using a group like this more intuitive than phihag's simple test?

不及他 2024-12-02 05:18:48

最好的方法是使用 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)

The best way to do this is by using python inbuilt module 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()

If you want only one argument to be selected by command line just use required=True as an argument for group

group = parser.add_mutually_exclusive_group(required=True)
意中人 2024-12-02 05:18:48

这达到了目的,并且这也将反映在 argparse 自动生成的 --help 输出中,恕我直言,这是大多数理智的程序员想要的(也适用于可选参数):

parser.add_argument(
    'commands',
    nargs='+',                      # require at least 1
    choices=['process', 'upload'],  # restrict the choice
    help='commands to execute'
)

关于此的官方文档:
https://docs.python.org/3/library/argparse.html#选择

This achieves the purpose and this will also be reflected in the argparse autogenerated --help output, which is IMHO what most sane programmers want (also works with optional arguments):

parser.add_argument(
    'commands',
    nargs='+',                      # require at least 1
    choices=['process', 'upload'],  # restrict the choice
    help='commands to execute'
)

Official documentation on this:
https://docs.python.org/3/library/argparse.html#choices

一个人练习一个人 2024-12-02 05:18:48

如果您需要 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 控制整个参数解析器而不仅仅是选项。 (我的意思是,如果您有一个带有 nargs="+"--option 标志,那么 --option 标志至少需要如果您有 optionnargs="+",则它至少需要一个参数。)

If you require a python program to run with at least one parameter, add an argument that doesn't have the option prefix (- or -- by default) and set nargs=+ (Minimum of one argument required). The problem with this method I found is that if you do not specify the argument, argparse will generate a "too few arguments" error and not print out the help menu. If you don't need that functionality, here's how to do it in code:

import argparse

parser = argparse.ArgumentParser(description='Your program description')
parser.add_argument('command', nargs="+", help='describe what a command is')
args = parser.parse_args()

I think that when you add an argument with the option prefixes, nargs governs the entire argument parser and not just the option. (What I mean is, if you have an --option flag with nargs="+", then --option flag expects at least one argument. If you have option with nargs="+", it expects at least one argument overall.)

无敌元气妹 2024-12-02 05:18:48

也许使用子解析器?

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' (请参阅文档)。

Maybe use sub-parsers?

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)

Now --help shows:

$ 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

You can add additional options to these sub-parsers as well. Also instead of using that dest='subparser_name' you can also bind functions to be directly called on given sub-command (see docs).

旧话新听 2024-12-02 05:18:48

对于像这样的情况

parser.add_argument("--a")
parser.add_argument("--b")

我们可以使用以下

if not args.a and not args.b:
    parser.error("One of --a or --b must be present")

For cases like

parser.add_argument("--a")
parser.add_argument("--b")

We can use the following

if not args.a and not args.b:
    parser.error("One of --a or --b must be present")
鸵鸟症 2024-12-02 05:18:48

对操作列表使用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()

Use append_const to a list of actions and then check that the list is populated:

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')

You can even specify the methods directly within the constants.

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()
云裳 2024-12-02 05:18:48

使用

    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()

Maybe try:

    if len([False for arg in vars(args) if vars(args)[arg]]) == 0: 
        parsers.print_help()
        exit(-1)

至少这是我刚刚使用的;希望这对将来的人有帮助!

Using

    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()

Maybe try:

    if len([False for arg in vars(args) if vars(args)[arg]]) == 0: 
        parsers.print_help()
        exit(-1)

At least this is what I just used; hopefully this helps someone in the future!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文