Python argparse и контроль / переопределение кода выхода - PullRequest
36 голосов
/ 10 мая 2011

Помимо работы с источником argparse, есть ли способ управления кодом состояния выхода, если при вызове parse_args() возникает проблема, например, отсутствует требуемый переключатель?

Ответы [ 6 ]

39 голосов
/ 10 мая 2011

Мне неизвестен какой-либо механизм указания кода выхода для каждого аргумента. Вы можете поймать исключение SystemExit, сгенерированное на .parse_args(), но я не уверен, как бы вы тогда выяснили, что именно конкретно вызвало ошибку.

РЕДАКТИРОВАТЬ: Для тех, кто приходит к этому в поисках практического решения, следующая ситуация:

  • ArgumentError() соответствующим образом повышается, когда синтаксический анализ arg не удался. Передается экземпляр аргумента и сообщение
  • 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

Не проверено каким-либо полезным способом. Применяется обычное возмещение «не вини меня, если он ломает».

29 голосов
/ 10 мая 2011

Возможно, перехват исключения SystemExit будет простым обходным путем:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('foo')
try:
    args = parser.parse_args()
except SystemExit:
    print("do something else")

У меня работает, даже в интерактивном сеансе.

Редактировать: Похоже, @Rob Cowie опередил меня. Как он сказал, это не имеет большого диагностического потенциала, если только вы не хотите стать глупым и попытаться собрать информацию из трассировки.

26 голосов
/ 05 июня 2013

Все ответы хорошо объясняют детали реализации 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.
2 голосов
/ 10 мая 2011

Тебе придется повозиться.Посмотрите на argparse.ArgumentParser.error, это то, что вызывается внутри.Или вы можете сделать аргументы необязательными, а затем проверить и выйти из argparse.

1 голос
/ 19 декабря 2014

В то время как argparse.error является методом, а не классом, «попробовать» невозможно, кроме ошибок «всех нераспознанных аргументов» Если вы хотите сделать это, вам нужно переопределить функцию error из argparse:

def print_help(errmsg):
   print(errmsg.split(' ')[0])

parser.error = print_help
args = parser.parse_args()

на неверном вводе теперь будет печататься:

unrecognised
1 голос
/ 10 мая 2011

Вы можете использовать один из существующих методов: http://docs.python.org/library/argparse.html#exiting-methods. Однако он уже должен обрабатывать ситуации, когда аргументы недопустимы (при условии, что вы правильно определили свои аргументы).

Использование недопустимых аргументов:

% [ $(./test_argparse.py> /dev/null 2>&1) ] || { echo error } 
error # exited with status code 2
...