Отключить/удалить аргумент в 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 после синтаксического разбора, как предложено здесь, состоит в том, что аргумент все еще отображается в справке

Ответ 1

Несмотря на ошибку, о которой я упоминал ниже, использование resolve предполагает возможный метод. Это не для новичков или тех, кто должен придерживаться публичного API.

parser имеет список объектов Action (аргумент) (созданных 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. (хотя в результате будет отображаться только один, если они не являются добавочными действиями).

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
  ...

Играя немного больше, похоже, что я могу удалить один из этих новых позиций:

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/issue19462 Add remove_argument() method to argparse.ArgumentParser

Я обсудил трудности полного удаления и предложил некоторые альтернативы. argparse.SUPPRESS может использоваться для скрытия подсказок. optionals можно игнорировать, если они не требуются. positionals сложнее, хотя я предложил настроить их атрибуты (nargs и default). Но это было какое-то время, поэтому мне нужно просмотреть эти сообщения.

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

Мне было интересно узнать о проблеме @2rs2ts (см. комментарий).

Я сделал синтаксический анализатор, а затем использовал его как родительский для другого синтаксического анализатора (нет необходимости использовать механизм subparser). Затем я удалил аргумент из одного парсера и посмотрел на изменения в другом парсере.

Сделать родительский синтаксический анализатор с одним аргументом:

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)]

Обратите внимание, что второе действие одинаково для обоих синтаксических анализаторов (тот же идентификатор). parents просто скопировал ссылку на исходное --foo действие, оно не сделало копию.

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

Теперь удалите '-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 использует несколько способов отличить positionals от optionals, но тот, который чаще всего используется при разборе, заключается в том, что атрибут option_strings пуст или нет. Путем опорожнения этого атрибута resolve эффективно превратил optional в positional.

Упс, моя память не то, что должно быть.:) Год назад я ответил на аналогичный вопрос с участием parents и resolve

fooobar.com/questions/548037/... argparse conflict resolver for options in subcommands turns keyword argument into positional argument

Ответ 2

Можно ли удалить или отключить аргумент в argparse, чтобы он не отображается в справке?

Установите help в argparse.SUPPRESS при добавлении аргумента, например:

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

Это предотвратит появление аргумента в выводе справки по умолчанию.

Ответ 3

Функция для удаления параметров 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