web-dev-qa-db-ja.com

argparseおよび複数の-vオプションを使用した詳細レベル

コマンドラインに-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
34
Charles Brunet

これは、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の動作を確認します。

20
unutbu

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)

Noneではなく冗長レベル0を与えるためにdefault=0引数が必要ない場合は、-vを明示的に設定する必要があります。

111
Ben

質問の最初の部分はappend_constで処理できます。そうでなければ、罰金で提案されているように、おそらくカスタムアクションを書くのに行き詰まっています nutbuによる回答

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)
13
FMc

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]+$', 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_、_--quiet_の4つのオプションハンドラーすべてを設定するために使用できる便利なクラスメソッドがあります。次のように使用します。

_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_文字でフォローアップでき、結果のレベルはthe old level + sum(count('v')) - sum(count('q'))になります。
  • 全体的なデフォルトは0です

別の動作が必要な場合に備えて、カスタムアクションはかなり簡単に変更できるはずです。たとえば、_--quiet_がレベルを0、さらには-1にリセットすることを好む人もいます。このためには、_-q_と_--quiet_のadd_argumentからnargsを削除し、ハードコードして_value = 0_ _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
_
3
cfi

これは、新しいクラスを使用せず、Python 2と3の両方で機能し、「-v」/「-verbose」と「-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
3
Eric Pruitt

argparseは、複数の引数を指定できるappendアクションをサポートします。チェック http://docs.python.org/library/argparse.html 、「append」を検索します。

2
yan

最初に提案された方法は、混乱する可能性が高くなります。さまざまなレベルの詳細度に対するさまざまなオプション名、またはオプションで詳細度のレベルの数値インジケータが後に続く1つの詳細度フラグは、ユーザーを混乱させる可能性が低く、詳細度レベルをより柔軟に割り当てることができます。

0
Paddy3118