带有 argparse 和多个 -v 选项的详细级别

发布于 2024-11-08 22:14:07 字数 297 浏览 0 评论 0原文

我希望能够通过向命令行添加更多 -v 选项来指定不同的详细级别。例如:

$ myprogram.py    
$ myprogram.py -v
$ myprogram.py -vv
$ myprogram.py -v -v -v

将分别导致 verbose=0、verbose=1、verbose=2 和 verbose=3。我如何使用 argparse 来实现这一点?

或者,如果能够像这样指定它可能会很棒

$ myprogram -v 2

I'd like to be able to specify different verbose level, by adding more -v options to the command line. For example:

$ myprogram.py    
$ myprogram.py -v
$ myprogram.py -vv
$ myprogram.py -v -v -v

would lead to verbose=0, verbose=1, verbose=2, and verbose=3 respectively. How can I achieve that using argparse?

Optionally, it could be great to also be able to specify it like

$ myprogram -v 2

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

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

发布评论

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

评论(8

我家小可爱 2024-11-15 22:14:07

argparse 支持 action='count'

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='count', default=0)

for c in ['', '-v', '-v -v', '-vv', '-vv -v', '-v -v --verbose -vvvv']:
    print(parser.parse_args(c.split()))

输出:

Namespace(verbose=0)
Namespace(verbose=1)
Namespace(verbose=2)
Namespace(verbose=2)
Namespace(verbose=3)
Namespace(verbose=7)

唯一的小问题是,如果您不希望 -v 参数为您提供 0 而不是 的详细级别,则必须显式设置 default=0

argparse supports action='count':

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-v', '--verbose', action='count', default=0)

for c in ['', '-v', '-v -v', '-vv', '-vv -v', '-v -v --verbose -vvvv']:
    print(parser.parse_args(c.split()))

Output:

Namespace(verbose=0)
Namespace(verbose=1)
Namespace(verbose=2)
Namespace(verbose=2)
Namespace(verbose=3)
Namespace(verbose=7)

The only very minor niggle is you have to explicitly set default=0 if you want no -v arguments to give you a verbosity level of 0 rather than None.

盛夏尉蓝 2024-11-15 22:14:07

您可以使用 nargs='?' (在 -v 标志后接受 0 或 1 个参数)和自定义操作(处理 0 或 1 个参数)来完成此操作:

import sys
import argparse

class VAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, const=None, 
                 default=None, type=None, choices=None, required=False, 
                 help=None, metavar=None):
        super(VAction, self).__init__(option_strings, dest, nargs, const, 
                                      default, type, choices, required, 
                                      help, metavar)
        self.values = 0
    def __call__(self, parser, args, values, option_string=None):
        # print('values: {v!r}'.format(v=values))
        if values is None:
            self.values += 1
        else:
            try:
                self.values = int(values)
            except ValueError:
                self.values = values.count('v')+1
        setattr(args, self.dest, self.values)

# test from the command line
parser = argparse.ArgumentParser()
parser.add_argument('-v', nargs='?', action=VAction, dest='verbose')
args = parser.parse_args()
print('{} --> {}'.format(sys.argv[1:], args))

print('-'*80)

for test in ['-v', '-v -v', '-v -v -v', '-vv', '-vvv', '-v 2']:
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', nargs='?', action=VAction, dest='verbose')
    args=parser.parse_args([test])
    print('{:10} --> {}'.format(test, args))

从命令行运行 script.py -v -v 会产生取消

['-v', '-v'] --> Namespace(verbose=2)
--------------------------------------------------------------------------------
-v         --> Namespace(verbose=1)
-v -v      --> Namespace(verbose=2)
-v -v -v   --> Namespace(verbose=3)
-vv        --> Namespace(verbose=2)
-vvv       --> Namespace(verbose=3)
-v 2       --> Namespace(verbose=2)

注释 print 语句以更好地查看 VAction 正在执行的操作。

You could do this with nargs='?' (to accept 0 or 1 arguments after the -v flag) and a custom action (to process the 0 or 1 arguments):

import sys
import argparse

class VAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, const=None, 
                 default=None, type=None, choices=None, required=False, 
                 help=None, metavar=None):
        super(VAction, self).__init__(option_strings, dest, nargs, const, 
                                      default, type, choices, required, 
                                      help, metavar)
        self.values = 0
    def __call__(self, parser, args, values, option_string=None):
        # print('values: {v!r}'.format(v=values))
        if values is None:
            self.values += 1
        else:
            try:
                self.values = int(values)
            except ValueError:
                self.values = values.count('v')+1
        setattr(args, self.dest, self.values)

# test from the command line
parser = argparse.ArgumentParser()
parser.add_argument('-v', nargs='?', action=VAction, dest='verbose')
args = parser.parse_args()
print('{} --> {}'.format(sys.argv[1:], args))

print('-'*80)

for test in ['-v', '-v -v', '-v -v -v', '-vv', '-vvv', '-v 2']:
    parser = argparse.ArgumentParser()
    parser.add_argument('-v', nargs='?', action=VAction, dest='verbose')
    args=parser.parse_args([test])
    print('{:10} --> {}'.format(test, args))

Running script.py -v -v from the command line yields

['-v', '-v'] --> Namespace(verbose=2)
--------------------------------------------------------------------------------
-v         --> Namespace(verbose=1)
-v -v      --> Namespace(verbose=2)
-v -v -v   --> Namespace(verbose=3)
-vv        --> Namespace(verbose=2)
-vvv       --> Namespace(verbose=3)
-v 2       --> Namespace(verbose=2)

Uncomment the print statement to see better what the VAction is doing.

提笔书几行 2024-11-15 22:14:07

您可以使用 append_const 处理问题的第一部分。否则,您可能会陷入编写自定义操作的困境,如细 unutbu 回答

import argparse

ap = argparse.ArgumentParser()
ap.add_argument('-v', action = 'append_const', const = 1)

for c in ['', '-v', '-v -v', '-vv', '-vv -v']:
    opt = ap.parse_args(c.split())
    opt.v = 0 if opt.v is None else sum(opt.v)
    print opt

输出:

Namespace(v=0)
Namespace(v=1)
Namespace(v=2)
Namespace(v=2)
Namespace(v=3)

You could handle the first part of your question with append_const. Otherwise, you're probably stuck writing a custom action, as suggested in the fine answer by unutbu.

import argparse

ap = argparse.ArgumentParser()
ap.add_argument('-v', action = 'append_const', const = 1)

for c in ['', '-v', '-v -v', '-vv', '-vv -v']:
    opt = ap.parse_args(c.split())
    opt.v = 0 if opt.v is None else sum(opt.v)
    print opt

Output:

Namespace(v=0)
Namespace(v=1)
Namespace(v=2)
Namespace(v=2)
Namespace(v=3)
白衬杉格子梦 2024-11-15 22:14:07

这是我对此的看法,它不使用任何新类,适用于 Python 2 和 3,并支持使用“-v”/“--verbose”和“-q”/“--quiet”从默认值进行相对调整,但它支持使用数字,例如“-v 2”:

#!/usr/bin/env python
import argparse
import logging
import sys

LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
DEFAULT_LOG_LEVEL = "INFO"


def main(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--verbose", "-v",
        dest="log_level",
        action="append_const",
        const=-1,
    )
    parser.add_argument(
        "--quiet", "-q",
        dest="log_level",
        action="append_const",
        const=1,
    )

    args = parser.parse_args(argv[1:])
    log_level = LOG_LEVELS.index(DEFAULT_LOG_LEVEL)

    # For each "-q" and "-v" flag, adjust the logging verbosity accordingly
    # making sure to clamp off the value from 0 to 4, inclusive of both
    for adjustment in args.log_level or ():
        log_level = min(len(LOG_LEVELS) - 1, max(log_level + adjustment, 0))

    log_level_name = LOG_LEVELS[log_level]
    print(log_level_name)
    logging.getLogger().setLevel(log_level_name)


if __name__ == "__main__":
    main(sys.argv)

示例:

$ python2 verbosity.py -vvv
DEBUG
$ python3 verbosity.py -vvv -q
INFO
$ python2 verbosity.py -qqq -vvv -q
WARNING
$ python2 verbosity.py -qqq
CRITICAL

Here's my take on this that doesn't use any new classes, works in both Python 2 and 3 and supports relative adjustments from the default using "-v"/"--verbose" and "-q"/"--quiet", but it doesn't support using numbers e.g. "-v 2":

#!/usr/bin/env python
import argparse
import logging
import sys

LOG_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
DEFAULT_LOG_LEVEL = "INFO"


def main(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--verbose", "-v",
        dest="log_level",
        action="append_const",
        const=-1,
    )
    parser.add_argument(
        "--quiet", "-q",
        dest="log_level",
        action="append_const",
        const=1,
    )

    args = parser.parse_args(argv[1:])
    log_level = LOG_LEVELS.index(DEFAULT_LOG_LEVEL)

    # For each "-q" and "-v" flag, adjust the logging verbosity accordingly
    # making sure to clamp off the value from 0 to 4, inclusive of both
    for adjustment in args.log_level or ():
        log_level = min(len(LOG_LEVELS) - 1, max(log_level + adjustment, 0))

    log_level_name = LOG_LEVELS[log_level]
    print(log_level_name)
    logging.getLogger().setLevel(log_level_name)


if __name__ == "__main__":
    main(sys.argv)

Example:

$ python2 verbosity.py -vvv
DEBUG
$ python3 verbosity.py -vvv -q
INFO
$ python2 verbosity.py -qqq -vvv -q
WARNING
$ python2 verbosity.py -qqq
CRITICAL
日记撕了你也走了 2024-11-15 22:14:07

扩展 unutbu 的答案,这是一个自定义操作,包括处理 --quiet/-q 组合。这是在Python3中测试的。在Python >=2.7 中使用它应该没什么大不了的。

class ActionVerbose(argparse.Action):
    def __call__(self, parser, args, values, option_string=None):
        #print(parser, args, values, option_string)
        # Obtain previously set value in case this option call is incr/decr only
        if args.verbose == None:
            base = 0
        else:
            base = args.verbose
        # One incr/decr is determined in name of option in use (--quiet/-q/-v/--verbose)
        option_string = option_string.lstrip('-')
        if option_string[0] == 'q':
            incr = -1
        elif option_string[0] == 'v':
            incr = 1
        else:
            raise argparse.ArgumentError(self,
                                         'Option string for verbosity must start with v(erbose) or q(uiet)')
        # Determine if option only or values provided
        if values==None:
            values = base + incr
        else:
            # Values might be an absolute integer verbosity level or more 'q'/'v' combinations
            try:
                values = int(values)
            except ValueError:
                values = values.lower()
                if not re.match('^[vq]+

有一个方便的类方法,可用于设置 --verbose-v-q 的所有四个选项处理程序>--安静。像这样使用它:

parser = argparse.ArgumentParser()
ActionVerbose.add_to_parser(parser, default=defaults['verbose'])
# add more arguments here with: parser.add_argument(...)
args = parser.parse_args()

当使用具有这些参数的脚本时,您可以执行以下操作:

./script -vvvvvv -v 4 -v 0 -v -vvv --verbose --quiet 2 -v qqvvqvv

使用此命令行args.verbose将为4

  • 任何具有给定数字的 -v/-q/--verbose/--quiet 都是该给定数字的硬性、绝对的 args.verbose 集(=详细级别)。
  • 任何不带数字的 -v/--verbose 都是该级别的增量。
  • 任何不带数字的 -q/--quiet 都是该级别的递减。
  • 任何 -v/-q 可能会立即跟随更多 v/q 字母,结果级别为旧级别 + sum(count('v' )) - sum(count('q'))
  • 总体默认值为 0

如果您想要不同的行为,自定义操作应该相当容易修改。例如,有些人喜欢任何 --quiet 将级别重置为 0,甚至重置为 -1。为此,请从 -q--quiet 的 add_argument 中删除 nargs,并硬编码以设置 value = 0< /code> if option_string[0] == 'q'

如果使用错误,正确的解析器错误会被很好地打印出来:

./script -vvvvvv -v 4 -v 0 -v -vvv --verbose --quiet 2 -v qqvvqvav
usage: script [-h] [--verbose [level]]
              [-v [level]] [--quiet [level]] [-q [level]]
script: error: argument -v: Option string for -v/-q must contain only further 'v'/'q' letters
, values): raise argparse.ArgumentError(self, "Option string for -v/-q must contain only further 'v'/'q' letters") values = base + incr + values.count('v') - values.count('q') setattr(args, self.dest, values) @classmethod def add_to_parser(cls, parser, dest='verbose', default=0, help_detail='(0:errors, 1:info, 2:debug)'): parser.add_argument('--verbose', nargs='?', action=ActionVerbose, dest=dest, metavar='level', default=default, help='Increase or set level of verbosity {}'.format(help_detail)) parser.add_argument('-v', nargs='?', action=ActionVerbose, dest=dest, metavar='level', help='Increase or set level of verbosity') parser.add_argument('--quiet', nargs='?', action=ActionVerbose, dest=dest, metavar='level', help='Decrease or set level of verbosity') parser.add_argument('-q', nargs='?', action=ActionVerbose, dest=dest, metavar='level', help='Decrease or set level of verbosity')

有一个方便的类方法,可用于设置 --verbose-v-q的所有四个选项处理程序>--安静。像这样使用它:

当使用具有这些参数的脚本时,您可以执行以下操作:

使用此命令行args.verbose将为4

  • 任何具有给定数字的 -v/-q/--verbose/--quiet 都是该给定数字的硬性、绝对的 args.verbose 集(=详细级别)。
  • 任何不带数字的 -v/--verbose 都是该级别的增量。
  • 任何不带数字的 -q/--quiet 都是该级别的递减。
  • 任何 -v/-q 可能会立即跟随更多 v/q 字母,结果级别为旧级别 + sum(count('v' )) - sum(count('q'))
  • 总体默认值为 0

如果您想要不同的行为,自定义操作应该相当容易修改。例如,有些人喜欢任何 --quiet 将级别重置为 0,甚至重置为 -1。为此,请从 -q--quiet 的 add_argument 中删除 nargs,并硬编码以设置 value = 0< /code> if option_string[0] == 'q'

如果使用错误,正确的解析器错误会被很好地打印出来:

Expanding on unutbu's answer, here's a custom action including handling of a --quiet/-q combination. This is tested in Python3. Using it in Python >=2.7 should be no big deal.

class ActionVerbose(argparse.Action):
    def __call__(self, parser, args, values, option_string=None):
        #print(parser, args, values, option_string)
        # Obtain previously set value in case this option call is incr/decr only
        if args.verbose == None:
            base = 0
        else:
            base = args.verbose
        # One incr/decr is determined in name of option in use (--quiet/-q/-v/--verbose)
        option_string = option_string.lstrip('-')
        if option_string[0] == 'q':
            incr = -1
        elif option_string[0] == 'v':
            incr = 1
        else:
            raise argparse.ArgumentError(self,
                                         'Option string for verbosity must start with v(erbose) or q(uiet)')
        # Determine if option only or values provided
        if values==None:
            values = base + incr
        else:
            # Values might be an absolute integer verbosity level or more 'q'/'v' combinations
            try:
                values = int(values)
            except ValueError:
                values = values.lower()
                if not re.match('^[vq]+

There's a convenience class method which can be used to set up all four option handlers for --verbose, -v, -q, --quiet. Use it like this:

parser = argparse.ArgumentParser()
ActionVerbose.add_to_parser(parser, default=defaults['verbose'])
# add more arguments here with: parser.add_argument(...)
args = parser.parse_args()

When using a script having these arguments you can do:

./script -vvvvvv -v 4 -v 0 -v -vvv --verbose --quiet 2 -v qqvvqvv

With this command line args.verbose would be 4.

  • Any -v/-q/--verbose/--quiet with a given number is a hard, absolute set of args.verbose to that given number (=verbosity level).
  • Any -v/--verbose without a number is an increment of that level.
  • Any -q/--quiet without a number is a decrement of that level.
  • Any -v/-q may immediately be followed up with more v/q letters, the resulting level is the old level + sum(count('v')) - sum(count('q'))
  • Overall default is 0

The custom action should be fairly easy to modify in case you want a different behaviour. For example, some people prefer that any --quiet resets the level to 0, or even to -1. For this, dremove the nargs from the add_argument of -q and --quiet, and also hardcode to set value = 0 if option_string[0] == 'q'.

Proper parser errors are nicely printed if usage is wrong:

./script -vvvvvv -v 4 -v 0 -v -vvv --verbose --quiet 2 -v qqvvqvav
usage: script [-h] [--verbose [level]]
              [-v [level]] [--quiet [level]] [-q [level]]
script: error: argument -v: Option string for -v/-q must contain only further 'v'/'q' letters
, values): raise argparse.ArgumentError(self, "Option string for -v/-q must contain only further 'v'/'q' letters") values = base + incr + values.count('v') - values.count('q') setattr(args, self.dest, values) @classmethod def add_to_parser(cls, parser, dest='verbose', default=0, help_detail='(0:errors, 1:info, 2:debug)'): parser.add_argument('--verbose', nargs='?', action=ActionVerbose, dest=dest, metavar='level', default=default, help='Increase or set level of verbosity {}'.format(help_detail)) parser.add_argument('-v', nargs='?', action=ActionVerbose, dest=dest, metavar='level', help='Increase or set level of verbosity') parser.add_argument('--quiet', nargs='?', action=ActionVerbose, dest=dest, metavar='level', help='Decrease or set level of verbosity') parser.add_argument('-q', nargs='?', action=ActionVerbose, dest=dest, metavar='level', help='Decrease or set level of verbosity')

There's a convenience class method which can be used to set up all four option handlers for --verbose, -v, -q, --quiet. Use it like this:

When using a script having these arguments you can do:

With this command line args.verbose would be 4.

  • Any -v/-q/--verbose/--quiet with a given number is a hard, absolute set of args.verbose to that given number (=verbosity level).
  • Any -v/--verbose without a number is an increment of that level.
  • Any -q/--quiet without a number is a decrement of that level.
  • Any -v/-q may immediately be followed up with more v/q letters, the resulting level is the old level + sum(count('v')) - sum(count('q'))
  • Overall default is 0

The custom action should be fairly easy to modify in case you want a different behaviour. For example, some people prefer that any --quiet resets the level to 0, or even to -1. For this, dremove the nargs from the add_argument of -q and --quiet, and also hardcode to set value = 0 if option_string[0] == 'q'.

Proper parser errors are nicely printed if usage is wrong:

初见终念 2024-11-15 22:14:07

argparse 支持 append 操作,可让您指定多个参数。检查 http://docs.python.org/library/argparse.html,搜索“追加”。

argparse supports the append action which lets you specify multiple arguments. Check http://docs.python.org/library/argparse.html, search for "append".

戏蝶舞 2024-11-15 22:14:07

您提出的第一个方法更可能会造成混乱。不同详细级别的不同选项名称,或者一个可选地后跟详细级别的数字指示符的详细标志不太可能使用户感到困惑,并且将允许在分配详细级别时具有更大的灵活性。

Your first proposed method would be more likely to confuse. Different option names for different levels of verbosity, or one verbose flag optionally followed by a numeric indicator of the level of verbosity is less likely to confuse a user and would allow more flexibility in assigning verbosity levels.

窗影残 2024-11-15 22:14:07

我想出了一个替代方案;虽然它并不完全符合OP的要求,但它满足了我的要求,我认为值得分享。

使用互斥组来计算短选项的数量或存储长选项的整数值。

import argparse

parser = argparse.ArgumentParser()
verbosity_group = parser.add_mutually_exclusive_group()
verbosity_group.add_argument(
  '-v',
  action='count',
  dest='verbosity',
  help='Turn on verbose output. Use more to turn up the verbosity level'
)
verbosity_group.add_argument(
  '--verbose',
  action='store',
  type=int,
  metavar='N',
  dest='verbosity',
  help='Set verbosity level to `N`'
)
parser.set_defaults(
  verbosity=0
)
parser.parse_args()
parser.parse_args([])
# Namespace(verbosity=0)
parser.parse_args(['-v', '-vv'])
# Namespace(verbosity=3)
parser.parse_args(['--verbose=4'])
# Namespace(verbosity=4)
parser.parse_args(['--verbose'])
# error: argument --verbose: expected one argument

正如您所看到的,它允许您“堆叠”单个字符选项,并允许您使用长选项名称来显式设置值。缺点是您不能使用长选项作为开关(最后一个示例会生成异常。)

I've come up with an alternative; while it doesn't exactly match OP's request, it fulfilled my requirements and I thought it worth sharing.

Use a mutually exclusive group to either count the number of short options or store the integer value of a long option.

import argparse

parser = argparse.ArgumentParser()
verbosity_group = parser.add_mutually_exclusive_group()
verbosity_group.add_argument(
  '-v',
  action='count',
  dest='verbosity',
  help='Turn on verbose output. Use more to turn up the verbosity level'
)
verbosity_group.add_argument(
  '--verbose',
  action='store',
  type=int,
  metavar='N',
  dest='verbosity',
  help='Set verbosity level to `N`'
)
parser.set_defaults(
  verbosity=0
)
parser.parse_args()
parser.parse_args([])
# Namespace(verbosity=0)
parser.parse_args(['-v', '-vv'])
# Namespace(verbosity=3)
parser.parse_args(['--verbose=4'])
# Namespace(verbosity=4)
parser.parse_args(['--verbose'])
# error: argument --verbose: expected one argument

As you can see, it allows you to "stack" single char options and allows you to use the long option name to set the value explicitly. The downside is that you cannot use the long option as a switch (the last example generates an exception.)

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