Как собрать все возможные аргументы argparse.ArgumentParser

#python #argparse

#python #argparse

Вопрос:

Учитывая argparse.ArgumentParser , например

parser.py

 from argparse import ArgumentParser
p = ArgumentParser()

subs = p.add_subparsers()
a = subs.add_parser('a')
a.add_argument('aa')
b = subs.add_parser('b')
b.add_argument('bb')

p.parse_args()
  

Теперь я могу вызвать это с помощью python parser.py -h / b -h / a -h, и каждый вызов с предоставляет мне все параметры, относящиеся к указанному вспомогательному анализатору. Суммируя их вместе, я могу получить полный список всех аргументов.

Я могу сделать это следующим образом:

 from parser import p
p.print_help()
  

а затем начать синтаксический анализ выходных данных, что, очевидно, не очень хорошая идея.

Я могу получить доступ p._subparsers , но это не дало мне слишком многого.

— Как сделать это правильно?

Что я в конечном итоге хочу получить: Тест pytest, где я могу протестировать все параметры, выдающие допустимый ответ, например, python parser a aa действительно будет делать aa

Комментарии:

1. Предоставление аргумента в p.parser_args(['arg_a', 'arg_b']) не то, что вам нужно? Вы также пытаетесь найти, какие аргументы существуют? Вы сможете сделать: def some_test(): p.parse_args(['a', 'aa']) p.parse_args(['b', 'bb']) Затем вы сможете либо использовать except SystemExit , либо извлекать из ArgumentParser и переопределять error метод.

2. Во время тестирования я не знаю аргументов, поэтому я не могу передать их parse_args . Я могу получить доступ только к инициализированному анализатору и продолжить свой путь оттуда

3. При доступе p . Вы можете получить доступ к его _actions списку. Вы можете выполнить итерацию по нему и проверить типы. если его тип _StoreAction , вы можете получить доступ к его dest, чтобы проверить его имя. если это _SubParsersAction — Вы можете выполнить итерацию по внутреннему choices и получить его действия. Немного больно, но это сработает

4. @OrY Доступ к любым атрибутам, начинающимся с _ , по своей сути хрупок, поскольку они могут быть изменены без уведомления в будущих выпусках. Есть ли способ использовать «общедоступные» методы?

5. @alaniwi Я знаю, что это опасно, но, к сожалению, нет общедоступного способа сделать это. Более безопасным способом достижения желаемого OP может быть получение из ArgumentParser и изменение add_argument и (также add_subparsers и позже add_subparser) таким образом, чтобы при каждом вызове этой функции новые аргументы сохранялись во внешнем списке.

Ответ №1:

 from argparse import ArgumentParser
p = ArgumentParser()

subs = p.add_subparsers()
a = subs.add_parser('a')
aa = a.add_argument('aa')
b = subs.add_parser('b')
bb = b.add_argument('bb')

p.parse_args()
  

add_argument возвращает Action объект. На эти объекты также есть ссылки в parser._actions списке, но вы можете собрать ссылки самостоятельно, либо по имени, либо в виде списка (или dict). add_subparsers это вариант на add_argument , который создает специальный подкласс Action , который имеет add_parser метод.

Попробуйте запустить этот код в интерактивном сеансе и посмотрите на созданные объекты. У большинства есть repr метод, который показывает ключевые (но не все) атрибуты. Для действий это будет включать такие вещи, как dest , nargs , default и т.д. Вы даже можете изменить эти атрибуты после создания. argparse написан как обычная иерархия классов Python.

Что касается атрибутов _ и __ — они не документированы, но это не значит, что они будут изменены в любой момент. Изменения появляются в argparse на этапе snails, при этом много внимания уделяется обратной совместимости (хотя иногда этого недостаточно). Легче изменить документацию, чем сам код, особенно такие базовые вещи, как _actions .

Но я не совсем уверен, что вы пытаетесь сделать. Запускать unittests с argparse несколько неудобно. Вы можете смоделировать, args = parse.parse_args(argv) где argv — список строк по вашему выбору. Или вы можете установить sys.argv[1:] новый набор строк. Или вы могли бы создать новый args=argparse.Namespace(...) объект. Попытка сгенерировать все возможные комбинации аргументов только на основе parser настройки звучит как большая работа, особенно при использовании подпараметров.