Argparse: игнорировать множественные позиционные аргументы при указании необязательного аргумента
Я пытаюсь заставить argparse игнорировать тот факт, что два обычно требуемых позиционных аргумента не следует оценивать, когда указан необязательный аргумент (-l
).
В основном я пытаюсь реплицировать поведение -help: когда вы указываете -h, все отсутствующие необходимые аргументы игнорируются.
Пример кода:
parser = argparse.ArgumentParser(description="Foo bar baz")
parser.add_argument('arg1', help='arg1 is a positional argument that does this')
parser.add_argument('arg2', help='arg2 is a positional argument that does this')
parser.add_argument('-l', '--list', dest='list', help='this is an optional argument that prints stuff')
options, args = parser.parse_args()
if options.list:
print "I list stuff"
И, конечно, если я запустил его сейчас, я получаю:
error: too few arguments
Я пробовал разные вещи, например nargs='?'
, но ничего не мог заставить работать.
Этот вопрос очень похож, но на него не ответил.
Любая помощь оценивается,
Спасибо,
Ответы
Ответ 1
К сожалению, argparse
недостаточно для этого достаточно гибким. Лучшее, что вы можете сделать, это сделать arg1
и arg2
необязательным с помощью nargs="?"
и проверить, предоставлены ли они, если это необходимо.
Внутреннее действие help
реализуется путем печати справочного сообщения и выхода из программы, как только -h
или --help
встречаются в командной строке. Вы можете написать подобное действие самостоятельно, что-то вроде
class MyAction(argparse.Action):
def __call__(self, parser, values, namespace, option_string):
print "Whatever"
parser.exit()
(Предупреждение: непроверенный код!)
Тем не менее, существуют определенные недостатки последнего подхода. Сообщение справки безоговорочно показывает arg1
и arg2
как обязательные аргументы. И синтаксический анализ командной строки просто прекращается, когда встречается -l
или --list
, игнорируя любые дополнительные аргументы. Это поведение вполне приемлемо для --help
, но оно меньше желаемого для других параметров.
Ответ 2
Я столкнулся с этой проблемой и решил использовать подкоманды. Подкоманды могут быть чрезмерными, но если вы обнаружите, что ваша программа не использует некоторые позиционные аргументы во многих случаях (как и я), тогда подкоманды могут быть хорошим решением.
В данном примере я бы использовал следующее:
parser = argparse.ArgumentParser(description="Foo bar baz")
subparsers = parser.add_subparsers(description='available subcommands')
parser_main = subparsers.add_parser('<main_command_name>')
parser_main.add_argument('arg1', help='arg1 is a positional argument that does this')
parser_main.add_argument('arg2', help='arg2 is a positional argument that does this')
parser_list = subparsers.add_parser('list', help='this is a subcommand that prints stuff')
options, args = parser.parse_args()
Я забыл некоторые детали, которые вы можете включить (например, set_defaults(func=list)
), которые упоминаются в документации argparse.
Ответ 3
Я думаю, что nargs='*'
полезен.
Позиционные аргументы игнорируются, тогда вы можете использовать if
для проверки позиционных аргументов как true или false.
http://docs.python.org/library/argparse.html#nargs
Ответ 4
Возможно, я нашел решение. Правда, это грязный хак, но он работает.
Примечание. Все перечисленные ниже применимы к Python 3.3.2.
В соответствии с ответом здесь, parse_args
проверяет, какие действия требуются, и выдает ошибку, если какой-либо из них отсутствует. Я предлагаю переопределить это поведение.
При подклассификации ArgumentParser
мы можем определить новый метод ArgumentParser.error
(оригинальный здесь), который будет проверять, была ли ошибка выбрана, потому что некоторые аргументы отсутствуют и предпринимаются необходимые действия. Код следует:
import argparse
import sys
from gettext import gettext as _
class ArgumentParser(argparse.ArgumentParser):
skip_list = []
def error(self, message):
# Let see if we are missing arguments
if message.startswith('the following arguments are required:'):
missingArgs = message.split('required: ')[1].split(', ')
newArgs = [] # Creating a list of whatever we should not skip but is missing
for arg in missingArgs:
if arg not in self.skip_list:
newArgs.append(arg)
else:
self.skip_list.remove(arg) # No need for it anymore
if len(newArgs) == 0:
return # WARNING! UNTESTED! MAY LEAD TO SPACETIME MELTDOWN!
else: # Some required stuff is still missing, so we show a corrected error message
message = _('the following arguments are required: %s') % ', '.join(newArgs)
self.print_usage(sys.stderr) # Original method behavior
args = {'prog': self.prog, 'message': message}
self.exit(2, _('%(prog)s: error: %(message)s\n') % args)
Новый метод сначала проверяет, является ли ошибка, потому что в командной строке отсутствуют аргументы (см. здесь для кода, который генерирует ошибку). Если это так, метод получает имена аргументов из сообщения об ошибке и помещает их в список (missingArgs
).
Затем мы перебираем этот список и проверяем, какие аргументы должны быть пропущены, и которые по-прежнему необходимы. Чтобы определить, какие аргументы пропустить, мы сравниваем их с skip_list
. Это поле в нашем подклассе ArgumentParser
, которое должно содержать имена аргументов, которые нужно пропустить, даже если они необходимы парсеру. Обратите внимание, что аргументы, которые находятся в skip_list
, удаляются из него, когда они найдены.
Если все еще требуются аргументы, отсутствующие в командной строке, метод выдает исправленное сообщение об ошибке. Однако, если все отсутствующие аргументы должны быть пропущены, метод возвращает.
ПРЕДУПРЕЖДЕНИЕ! Исходное определение ArgumentParser.error
гласит, что если он переопределен в подклассе , он не должен возвращать, а скорее должен выйти или создать исключение. Таким образом, то, что показано здесь, потенциально опасно и может привести к сбою вашей программы, вашему компьютеру загореться или хуже - ЭТО МОЖЕТ ИСПРАВИТЬ ВСЕ ВАШЕ ЧАЙ. Однако, похоже, в этом конкретном случае (отсутствующие требуемые аргументы) можно безопасно вернуться из метода. Но может и не быть. Вы были предупреждены.
Чтобы заполнить skip_list
, мы могли бы использовать такой код:
class SpecialHelp(argparse._HelpAction):
def __call__(self, parser, namespace, values, option_string=None):
parser.print_help()
print()
for action in parser._actions:
if action != self and action.required:
parser.skip_list.append(argparse._get_action_name(action))
Этот конкретный класс имитирует встроенное действие help
, но вместо его выхода он вставляет все оставшиеся необходимые аргументы в skip_list
.
Надеюсь, мой ответ поможет и удачи.
Ответ 5
Это может быть уродливо, но это то, что я обычно делаю.
def print_list():
the_list = ["name1", "name2"]
return "{0}".format(the_list)
...
parser.add_argument("-l", "--list", action='version',
version=print_list(), help="print the list")
Ответ 6
Это то, что вы ищете?
Когда вызывается parse_args(), необязательные аргументы будут определены префиксом, а остальные аргументы будут считаться позиционными: