web-dev-qa-db-ja.com

argparseの引数の無効化/削除

ヘルプに表示されないように、argparseの引数を削除または無効にすることはできますか?どうやって?

新しい引数を追加するのは簡単です:

parser = argparse.ArgumentParser()
parser.add_argument('--arg1', help='Argument 1')
parser.add_argument('--arg2', help='A second one')

そして、「解決」競合ハンドラを指定することで、引数を新しい定義で上書きできることを知っています。

#In one script that should stand-alone and include arg1:

parser = argparse.ArgumentParser(conflict_handler='resolve')
parser.add_argument('--arg1', help='Argument 1')
parser.add_argument('--arg2', help='A second one')

#In another script with similar options
parser.add_argument('--arg1', help='New number 1')

しかし、これにはまだヘルプメッセージのarg1とparse_argsの結果が含まれています。

#Wishful thinking
#In another script with similar options, that shouldn't include arg1
parser.remove_argument('--arg1')

またはこれを達成するための別の合理的に簡単な方法?

また、引数が位置引数の場合、アプローチは異なりますか?

注:推奨された解析後のarg1の削除に関する問題 here は、引数が引き続きヘルプに表示されることです

18
Bryan P

以下で言及するバグの問題にもかかわらず、resolveの使用は可能な方法を示唆しています。これは、初心者や、パブリックAPIを使い続ける必要のある人向けではありません。

parserには、アクション(引数)オブジェクトのリストがあります(_add_argument_によって作成されます)。

2番目のパーサー定義を使用すると、その__actions_リストは次のようになります。

_In [22]: parser._actions
Out[22]: 
[_HelpAction(option_strings=['-h', '--help'], dest='help'...),
 _StoreAction(option_strings=['--arg2'], dest='arg2', nargs=None,
      const=None, default=None, type=None, choices=None, 
      help='A second one', metavar=None),
 _StoreAction(option_strings=['--arg1'], dest='arg1', nargs=None,
      const=None, default=None, type=None, choices=None, 
      help='New number 1', metavar=None)]
_

resolveで競合するアクションを追加すると、競合する既存のアクションが削除されます。詳細については、__handle_conflict_resolve_メソッドを参照してください。しかし、私はそれをだまして、新しいアクションを追加せずにアクションを削除することができます。

_In [23]: parser._handle_conflict_resolve(None, [('--arg1',parser._actions[2])])
_

__actions_を見て、_--arg1_がなくなったことを確認します。

_In [24]: parser._actions
Out[24]: 
[_HelpAction(option_strings=['-h', '--help'], dest='help',....),
 _StoreAction(option_strings=['--arg2'], dest='arg2', nargs=None,...)]

In [25]: parser.print_help()
usage: ipython3 [-h] [--arg2 ARG2]

optional arguments:
  -h, --help   show this help message and exit
  --arg2 ARG2  A second one
_

resolveは、フラグ文字列が競合する可能性があるoptionalsを処理するだけです。そして、最初に競合するフラグを削除し、フラグが残っていない場合にのみ競合するアクションを削除します。したがって、短いオプションと長いオプションの両方がある場合は、特に注意してください。

そして、これはポジションのケースには対応していません。フラグはなく、destパラメータを共有する場合があります。 (ただし、アクションを追加していない限り、結果には1つしか表示されません)。

_In [27]: foo1 = parser.add_argument('foo',help='foo 1 positional')
In [28]: foo2 = parser.add_argument('foo',help='foo 2 positional')
In [29]: parser.print_help()
usage: ipython3 [-h] [--arg2 ARG2] foo foo
positional arguments:
  foo          foo 1 positional
  foo          foo 2 positional
  ...
_

もう少し遊んでみると、これらの新しいポジションの1つを削除できるようです。

_In [33]: parser._actions[-1]
Out[33]: _StoreAction(option_strings=[], dest='foo',... help='foo 2 positional', metavar=None)
In [35]: foo2=parser._actions[-1]
In [36]: foo2.container._remove_action(foo2)
In [39]: parser.print_help()
usage: ipython3 [-h] [--arg2 ARG2] foo    
positional arguments:
  foo          foo 1 positional
 ....
_

__actions[-2]_を選択した場合、最初のfooを削除することになります。 _add_argument_が返す値を変数に割り当てた場合、たとえば_foo1_、_parser._actions_リストの値を検索する代わりに、それを使用できます。対話型シェル(私はIPythonを使用しています)でサンプルパーサーを実行し、これらのオブジェクトを確認すると役立つ場合があります。

繰り返しますが、これは簡単な例で機能するようですが、より複雑なもの(または本番用)で使用する場合は、慎重なテストが必要です。


このトピックは、数年前にPythonバグ/問題で取り上げられました。

http://bugs.python.org/issue19462Add remove_argument() method to argparse.ArgumentParser

私は完全な除去の難しさについて話し合い、いくつかの代替案を提案しました。 _argparse.SUPPRESS_は、ヘルプを非表示にするために使用できます。 optionalsは、不要な場合は無視できます。 positionalsの方が扱いにくいですが、属性(nargsおよびdefault)を調整することをお勧めします。しかし、久しぶりなので、これらの投稿を確認する必要があります。

=============================

_@2rs2ts_の問題に興味がありました(コメントを参照)。

パーサーを作成し、それを別のパーサーの親として使用しました(サブパーサーメカニズムを使用する必要はありません)。次に、1つのパーサーから引数を削除し、他のパーサーの変更を調べました。

1つの引数で親パーサーを作成します。

_In [59]: p=argparse.ArgumentParser()
In [60]: p.add_argument('--foo')
Out[60]: _StoreAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)
_

parentsで別のものを作成:

_In [61]: p1=argparse.ArgumentParser(parents=[p],add_help=False)
In [62]: p1._actions
Out[62]: 
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
 _StoreAction(option_strings=['--foo'], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)]
_

2番目のアクションは両方のパーサーで同じであることに注意してください(同じID)。 parentsが元の_--foo_アクションへの参照をコピーしただけで、コピーは作成されませんでした。

_In [63]: id(p._actions[1])
Out[63]: 3000108652
In [64]: id(p1._actions[1])
Out[64]: 3000108652
_

ここで、以前に解決したトリックを使用して、1つのパーサーから '--foo'を削除します。

_In [65]: p1._handle_conflict_resolve(None,[('--foo',p1._actions[1])])
In [66]: p1._actions
Out[66]: [_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None)]
_

'--foo'は_p1_リストから削除されましたが、pリストにはまだ残っています。しかし、_option_strings_は現在空です。

_In [67]: p._actions
Out[67]: 
[_HelpAction(option_strings=['-h', '--help'], dest='help', nargs=0, const=None, default='==SUPPRESS==', type=None, choices=None, help='show this help message and exit', metavar=None),
 _StoreAction(option_strings=[], dest='foo', nargs=None, const=None, default=None, type=None, choices=None, help=None, metavar=None)]
_

resolveコードは、競合する_option_strings_を_--foo_アクションから削除し、次に_p1._actions_リストから削除しました。ただし、_option_strings_参照の_p1_を変更すると、p参照も変更されました。

argparseは、いくつかの方法でpositionalsoptionalsを区別しますが、解析で最も頻繁に使用されるのは、_option_strings_属性が空かどうかを調べることですない。この属性を空にすることで、resolveoptionalpositionalに効果的に変換しました。

おっと、私の記憶は本来あるべきものではありません。:) 1年前、parentsresolveに関する同様の質問に答えました

https://stackoverflow.com/a/25821043/901925 _argparse conflict resolver for options in subcommands turns keyword argument into positional argument_

4
hpaulj

Argparseの引数を削除または無効にして、ヘルプに表示されないようにすることはできますか?

次のように、引数を追加するときにhelpargparse.SUPPRESSに設定します。

parser.add_argument('--arg1', help=argparse.SUPPRESS)

これにより、引数がデフォルトのヘルプ出力に表示されなくなります。

9
Burhan Khalid

Argparseオプションを削除する関数:

def remove_options(parser, options):
    for option in options:
        for action in parser._actions:
            if vars(action)['option_strings'][0] == option:
                parser._handle_conflict_resolve(None,[(option,action)])
                break
5
Santi Oliveras

Hpauljの回答は素晴らしいですが、私の場合、parser._remove_action(action)だけを使用すると、「位置引数」から削除されず、削除されたアクションを支援しました。私の回避策は、_action_groupからも削除することでした。

def remove_option(parser, arg):
    for action in parser._actions:
        if (vars(action)['option_strings']
            and vars(action)['option_strings'][0] == arg) \
                or vars(action)['dest'] == arg:
            parser._remove_action(action)

    for action in parser._action_groups:
        vars_action = vars(action)
        var_group_actions = vars_action['_group_actions']
        for x in var_group_actions:
            if x.dest == arg:
                var_group_actions.remove(x)
                return
2
Lorenzo