Помимо работы с источником argparse
, есть ли способ контролировать код состояния выхода, если возникнет проблема при вызове parse_args()
, например, отсутствие необходимого переключателя?
Python argparse и контроль/переопределение кода состояния выхода
Ответы (8)
Мне неизвестен какой-либо механизм для указания кода выхода для каждого аргумента. Вы можете перехватить исключение SystemExit
, вызванное .parse_args()
, но я не уверен, как вы тогда сможете выяснить, что конкретно вызвало ошибку.
EDIT: Для тех, кто ищет практическое решение, следующая ситуация:
ArgumentError()
поднимается соответствующим образом, когда происходит сбой синтаксического анализа аргумента. Ему передается экземпляр аргумента и сообщениеArgumentError()
не сохраняет аргумент как атрибут экземпляра, несмотря на то, что передается (что было бы удобно)- Можно повторно вызвать исключение
ArgumentError
путем создания подклассаArgumentParser
, переопределения .error() и получения исключения из sys.exc_info().
Все это означает, что следующий код, хотя и уродливый, позволяет нам перехватывать исключение ArgumentError, получать оскорбительный аргумент и сообщение об ошибке и делать то, что мы считаем нужным:
import argparse
import sys
class ArgumentParser(argparse.ArgumentParser):
def _get_action_from_name(self, name):
"""Given a name, get the Action instance registered with this parser.
If only it were made available in the ArgumentError object. It is
passed as it's first arg...
"""
container = self._actions
if name is None:
return None
for action in container:
if '/'.join(action.option_strings) == name:
return action
elif action.metavar == name:
return action
elif action.dest == name:
return action
def error(self, message):
exc = sys.exc_info()[1]
if exc:
exc.argument = self._get_action_from_name(exc.argument_name)
raise exc
super(ArgumentParser, self).error(message)
## usage:
parser = ArgumentParser()
parser.add_argument('--foo', type=int)
try:
parser.parse_args(['--foo=d'])
except argparse.ArgumentError, exc:
print exc.message, '\n', exc.argument
Не проверено каким-либо полезным способом. Применяется обычная гарантия возмещения ущерба «не вините меня, если это сломается».
exit=True
- person Thoughtful Dragon; 26.01.2016
def error
, это двухстрочный ;-).
- person JL Peyret; 06.01.2018
error
, потому что все, что вы знаете, это строка сообщения, а не первоначальная причина.
- person JL Peyret; 06.01.2018
Все ответы хорошо объясняют детали реализации argparse.
Действительно, как предложено в PEP (и указано Роб Коуи) следует наследовать ArgumentParser и переопределять поведение методов error или exit.
В моем случае я просто хотел заменить печать использования на полную справку в случае ошибки:
class ArgumentParser(argparse.ArgumentParser):
def error(self, message):
self.print_help(sys.stderr)
self.exit(2, '%s: error: %s\n' % (self.prog, message))
В случае переопределения основной код будет по-прежнему содержать минималистичный..
# Parse arguments.
args = parser.parse_args()
# On error this will print help and cause exit with explanation message.
Возможно, перехват исключения SystemExit
будет простым обходным путем:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('foo')
try:
args = parser.parse_args()
except SystemExit:
print("do something else")
У меня работает даже в интерактивном сеансе.
Редактировать: Похоже, @Rob Cowie опередил меня до выключателя. Как он сказал, у этого не очень большой диагностический потенциал, если только вы не хотите валять дурака и пытаться собрать информацию из трассировки.
Начиная с Python 3.9 это уже не так болезненно. Теперь вы можете справиться с этим с помощью нового аргумента инстанцирования argparse.ArgumentParser
exit_on_error
. Вот пример (слегка измененный из документации по python: argparse#exit_on_error а>):
parser = argparse.ArgumentParser(exit_on_error=False)
parser.add_argument('--integers', type=int)
try:
parser.parse_args('--integers a'.split())
except argparse.ArgumentError:
print('Catching an argumentError')
exit(-1)
Вам бы повозиться. Посмотрите на argparse.ArgumentParser.error
, который вызывается внутри. Или вы можете сделать аргументы необязательными, затем проверить и выйти за пределы argparse.
Вы можете использовать один из методов выхода: http://docs.python.org/library/argparse.html#exiting-methods. Однако он уже должен обрабатывать ситуации, когда аргументы недействительны (при условии, что вы правильно определили свои аргументы).
Использование недопустимых аргументов:
% [ $(./test_argparse.py> /dev/null 2>&1) ] || { echo error }
error # exited with status code 2
Хотя argparse.error является методом, а не классом, невозможно «попробовать», «за исключением» всех ошибок «нераспознанных аргументов». Если вы хотите сделать это, вам нужно переопределить функцию ошибки из argparse:
def print_help(errmsg):
print(errmsg.split(' ')[0])
parser.error = print_help
args = parser.parse_args()
при неверном вводе он теперь будет печатать:
unrecognised
Мне нужен был простой способ поймать ошибку argparse при запуске приложения и передать ее в форму wxPython. Объединение лучших ответов из приведенных выше привело к следующему небольшому решению:
import argparse
# sub class ArgumentParser to catch an error message and prevent application closing
class MyArgumentParser(argparse.ArgumentParser):
def __init__(self, *args, **kwargs):
super(MyArgumentParser, self).__init__(*args, **kwargs)
self.error_message = ''
def error(self, message):
self.error_message = message
def parse_args(self, *args, **kwargs):
# catch SystemExit exception to prevent closing the application
result = None
try:
result = super().parse_args(*args, **kwargs)
except SystemExit:
pass
return result
# testing -------
my_parser = MyArgumentParser()
my_parser.add_argument('arg1')
my_parser.parse_args()
# check for an error
if my_parser.error_message:
print(my_parser.error_message)
запустить его:
>python test.py
the following arguments are required: arg1