web-dev-qa-db-ja.com

Python Argparseの条件付きで必要な引数

私はできる限り多くの調査を行いましたが、特定のコマンドライン引数を特定の条件下でのみ必要とする最善の方法を見つけていません。この場合、他の引数が指定されている場合のみです。これが、非常に基本的なレベルでやりたいことです。

p = argparse.ArgumentParser(description='...')
p.add_argument('--argument', required=False)
p.add_argument('-a', required=False) # only required if --argument is given
p.add_argument('-b', required=False) # only required if --argument is given

私が見たことから、他の人は最後に自分のチェックを追加するようです:

if args.argument and (args.a is None or args.b is None):
    # raise argparse error here

これをargparseパッケージ内でネイティブに行う方法はありますか?

36
DJMcCarthy12

私はしばらくの間、この種の質問に対する簡単な答えを探してきました。 '--argument'sys.argvにあるかどうかを確認するだけでよいので、基本的に、コードサンプルでは次のようにすることができます。

import argparse
import sys

if __name__ == '__main__':
    p = argparse.ArgumentParser(description='...')
    p.add_argument('--argument', required=False)
    p.add_argument('-a', required='--argument' in sys.argv) #only required if --argument is given
    p.add_argument('-b', required='--argument' in sys.argv) #only required if --argument is given
    args = p.parse_args()

このように、requiredは、ユーザーが--argumentを使用したかどうかに応じて、TrueまたはFalseを受け取ります。すでにテスト済みで、動作しているようで、-a-bが相互に独立した動作をすることが保証されています。

42
Mira

--argumentのカスタムアクションを提供することでチェックを実装できます。これは、--argumentが使用されている場合に必要になる他のアクションを指定する追加のキーワード引数を取ります。

import argparse

class CondAction(argparse.Action):
    def __init__(self, option_strings, dest, nargs=None, **kwargs):
        x = kwargs.pop('to_be_required', [])
        super(CondAction, self).__init__(option_strings, dest, **kwargs)
        self.make_required = x

    def __call__(self, parser, namespace, values, option_string=None):
        for x in self.make_required:
            x.required = True
        try:
            return super(CondAction, self).__call__(parser, namespace, values, option_string)
        except NotImplementedError:
            pass

p = argparse.ArgumentParser()
x = p.add_argument("--a")
p.add_argument("--argument", action=CondAction, to_be_required=[x])

CondActionの正確な定義は、--argumentが何をすべきかによって異なります。ただし、たとえば、--argumentが通常の、引数を1つ取り、それを保存するタイプのアクションの場合、argparse._StoreActionから継承するだけで十分です。

パーサーの例では、--aオプション内の--argumentオプションへの参照を保存し、--argumentがコマンドラインに表示されると、requiredフラグを設定します--aからTrueまで。すべてのオプションが処理されると、argparseは、必須としてマークされたオプションが設定されていることを確認します。

11
chepner

特に_is None_を使用したデフォルトのテストがニーズに合っている場合は、ポスト解析テストで問題ありません。

http://bugs.python.org/issue11588 _'Add "necessarily inclusive" groups to argparse'_は、groupsメカニズム(mutual_exclusive_groupsの一般化)を使用して、このようなテストの実装を調べます。

UsageGroups(相互に排他的)、xorandorなどのテストを実装するnotのセットを作成しました。包括的だと思っていたのですが、それらの運用に関しては申し上げられませんでした。 (nandが必要なようです-必要ありません。以下を参照してください)

このスクリプトは、基本的に解析後のテストを実装するカスタムTestクラスを使用します。 _seen_actions_は、解析で確認されたアクションのリストです。

_class Test(argparse.UsageGroup):
    def _add_test(self):
        self.usage = '(if --argument then -a and -b are required)'
        def testfn(parser, seen_actions, *vargs, **kwargs):
            "custom error"
            actions = self._group_actions
            if actions[0] in seen_actions:
                if actions[1] not in seen_actions or actions[2] not in seen_actions:
                    msg = '%s - 2nd and 3rd required with 1st'
                    self.raise_error(parser, msg)
            return True
        self.testfn = testfn
        self.dest = 'Test'
p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
g1 = p.add_usage_group(kind=Test)
g1.add_argument('--argument')
g1.add_argument('-a')
g1.add_argument('-b')
print(p.parse_args())
_

出力例は次のとおりです。

_1646:~/mypy/argdev/usage_groups$ python3 issue25626109.py --arg=1 -a1
usage: issue25626109.py [-h] [--argument ARGUMENT] [-a A] [-b B]
                        (if --argument then -a and -b are required)
issue25626109.py: error: group Test: argument, a, b - 2nd and 3rd required with 1st
_

usageとエラーメッセージはまだ作業が必要です。そして、構文解析後のテストでできないことは何もしません。


_(argument & (!a or !b))_の場合、テストでエラーが発生します。逆に、許可されているのは!(argument & (!a or !b)) = !(argument & !(a and b))です。 nandテストをUsageGroupクラスに追加することで、次のようにケースを実装できます。

_p = argparse.ArgumentParser(formatter_class=argparse.UsageGroupHelpFormatter)
g1 = p.add_usage_group(kind='nand', dest='nand1')
arg = g1.add_argument('--arg', metavar='C')
g11 = g1.add_usage_group(kind='nand', dest='nand2')
g11.add_argument('-a')
g11.add_argument('-b')
_

使い方は(!()を使用して「nand」テストをマークします):

_usage: issue25626109.py [-h] !(--arg C & !(-a A & -b B))
_

これは、汎用の使用グループを使用してこの問題を表現する最も短くて明確な方法だと思います。


私のテストでは、正常に解析される入力は次のとおりです。

_''
'-a1'
'-a1 -b2'
'--arg=3 -a1 -b2'
_

エラーを発生させることになっているものは次のとおりです。

_'--arg=3'
'--arg=3 -a1'
'--arg=3 -b2'
_
3
hpaulj

議論のために、私はこのような迅速で汚い解決策を考え出しました。前提:(1) '--help'はヘルプを表示し、必要な引数について文句を言わないようにする必要があります。(2)解析中ですsys.argv

p = argparse.ArgumentParser(...)
p.add_argument('-required', ..., required = '--help' not in sys.argv )

これは、特定の設定に合わせて簡単に変更できます。必要なポジション(コマンドラインで '--help'が指定されている場合は不要になります)については、次のようになります:[ポジションはrequired=...キーワード引数!]

p.add_argument('pattern', ..., narg = '+' if '--help' not in sys.argv else '*' )

基本的に、これにより、コマンドラインで必要な 'pattern'の出現回数が、 '-help'が指定されている場合に1つ以上から0以上に変わります。

1
haavee

これは本当に@Miraの回答と同じですが、オプションが指定されているときに追加の引数が必要な場合に備えて、それを表示したいと思います。

たとえば、--option fooが指定されている場合、--option barが指定されている場合は不要ないくつかの引数も必要です。

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--option', required=True,
        help='foo and bar need different args')

    if 'foo' in sys.argv:
        parser.add_argument('--foo_opt1', required=True,
           help='--option foo requires "--foo_opt1"')
        parser.add_argument('--foo_opt2', required=True,
           help='--option foo requires "--foo_opt2"')
        ...

    if 'bar' in sys.argv:
        parser.add_argument('--bar_opt', required=True,
           help='--option bar requires "--bar_opt"')
        ...

それは完璧ではありません-たとえばproggy --option foo --foo_opt1 barはあいまいですが、私がそれを行うために必要なものについてはそうです。

0
keithpjolley

http://bugs.python.org/issue11588 が解決されるまで、 nargs を使用します:

p = argparse.ArgumentParser(description='...')
p.add_argument('--arguments', required=False, nargs=2, metavar=('A', 'B'))

このように、誰かが--arguments、2つの値があります。

CLIの結果は読みにくいかもしれませんが、コードははるかに小さくなります。あなたは良いドキュメント/ヘルプでそれを修正することができます。

0
Yajo