web-dev-qa-db-ja.com

python argparse-オプションの追加引数と選択肢

実行する事前定義されたアクションのリストをユーザーに要求するスクリプトがあります。また、ユーザーが何も定義していないときに、特定のアクションのリストを想定できるようにしたいです。ただし、これらの両方を一緒に実行しようとすることは不可能のようです。

ユーザーが引数を指定しない場合、デフォルトの選択が無効であるというエラーを受け取ります

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']])
args = p.parse_args([])
>>> usage: [-h] [{clear,copy,dump,lock} [{clear,copy,dump,lock} ...]]
: error: argument action: invalid choice: [['dump', 'clear']] (choose from 'clear', 'copy', 'dump', 'lock')

そして、それらが一連のアクションを定義する場合、結果の名前空間には、デフォルトを置き換えるのではなく、デフォルトにユーザーのアクションが追加されます

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', action='append', choices=acts, default=[['dump', 'clear']])
args = p.parse_args(['lock'])
args
>>> Namespace(action=[['dump', 'clear'], ['dump']])
17
Steve

次の例のように、カスタマイズされたargparse.Actionを使用して必要なことを実行できます。

import argparse

parser = argparse.ArgumentParser()

class DefaultListAction(argparse.Action):
    CHOICES = ['clear','copy','dump','lock']
    def __call__(self, parser, namespace, values, option_string=None):
        if values:
            for value in values:
                if value not in self.CHOICES:
                    message = ("invalid choice: {0!r} (choose from {1})"
                               .format(value,
                                       ', '.join([repr(action)
                                                  for action in self.CHOICES])))

                    raise argparse.ArgumentError(self, message)
            setattr(namespace, self.dest, values)

parser.add_argument('actions', nargs='*', action=DefaultListAction,
                    default = ['dump', 'clear'],
                    metavar='ACTION')

print parser.parse_args([])
print parser.parse_args(['lock'])

スクリプトの出力は次のとおりです。

$ python test.py 
Namespace(actions=['dump', 'clear'])
Namespace(actions=['lock'])
16
jcollado

ドキュメント(http://docs.python.org/dev/library/argparse.html#default)では、次のように述べられています。

Nargsが?に等しい位置引数の場合または*、コマンドライン引数が存在しない場合はデフォルト値が使用されます。

次に、そうすると:

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', choices=acts, default='clear')    
print p.parse_args([])

期待どおりの結果が得られます

Namespace(action='clear')

問題は、リストをデフォルトとして配置する場合です。しかし、私はそれをドキュメントで見ました、

parser.add_argument('bar', nargs='*', default=[1, 2, 3], help='BAR!')

だから、私は知りません:-(

とにかく、ここにあなたが望む仕事をする回避策があります:

import sys, argparse
acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('action', nargs='*', choices=acts)
args = ['dump', 'clear'] # I set the default here ... 
if sys.argv[1:]:
    args = p.parse_args()
print args
5
n1r3

私は次のことをすることになりました:

  • いいえappend
  • 空のリストを可能な選択肢に追加しないと、空の入力が壊れます
  • デフォルトなし
  • その後、空のリストを確認し、その場合は実際のデフォルトを設定します

例:

parser = argparse.ArgumentParser()
parser.add_argument(
    'is',
    type=int,
    choices=[[], 1, 2, 3],
    nargs='*',
)

args = parser.parse_args(['1', '3'])
assert args.a == [1, 3]

args = parser.parse_args([])
assert args.a == []
if args.a == []:
    args.a = [1, 2]

args = parser.parse_args(['1', '4'])
# Error: '4' is not valid.

ユーザーがアクションを提供しているか(この場合、必須の位置引数として解析する)、またはアクションを提供していないか(この場合、デフォルトでオプションの引数として解析する)をテストできます。

import argparse
import sys

acts = ['clear', 'copy', 'dump', 'lock']
p = argparse.ArgumentParser()
if sys.argv[1:]:
    p.add_argument('action', nargs = '*', choices = acts)
else:
    p.add_argument('--action', default = ['dump', 'clear'])

args = p.parse_args()
print(args)

実行すると、次の結果が得られます。

% test.py 
Namespace(action=['dump', 'clear'])
% test.py lock
Namespace(action=['lock'])
% test.py lock dump
Namespace(action=['lock', 'dump'])

おそらく、解析する他のオプションもあります。その場合、parse_known_argsを使用して他のオプションを解析し、2番目のパスでunknown引数を処理できます。

import argparse

acts = ['clear', 'copy', 'dump', 'lock']
p = argparse.ArgumentParser()
p.add_argument('--foo')
args, unknown = p.parse_known_args()
if unknown:
    p.add_argument('action', nargs = '*', choices = acts)
else:
    p.add_argument('--action', default = ['dump', 'clear'])

p.parse_args(unknown, namespace = args)
print(args)

実行すると、次の結果が得られます。

% test.py 
Namespace(action=['dump', 'clear'], foo=None)
% test.py --foo bar
Namespace(action=['dump', 'clear'], foo='bar')
% test.py lock dump
Namespace(action=['lock', 'dump'], foo=None)
% test.py lock dump --foo bar
Namespace(action=['lock', 'dump'], foo='bar')
3
unutbu

Argparseに渡した「action = 'append'」パラメーターが原因で、アクションが追加されていました。

このパラメーターを削除すると、ユーザーから渡された引数は独自に表示されますが、引数が渡されなかった場合、プログラムはエラーをスローします。

最初のパラメーターに「-」プレフィックスを追加すると、これが最も怠惰な方法で解決されます。

acts = ['clear','copy','dump','lock']
p = argparse.ArgumentParser()
p.add_argument('--action', nargs='*', choices=acts, default=[['dump', 'clear']])
args = p.parse_args()

このアプローチの欠点は、ユーザーから渡されたオプションの前に「--action」を付ける必要があることです。

app.py --action clear dump copy
1
Condiment