web-dev-qa-db-ja.com

Python argparseと終了ステータスコードの制御/オーバーライド

argparseソースをいじる以外に、必要なスイッチがないなど、parse_args()が呼び出されたときに問題が発生した場合に終了ステータスコードを制御する方法はありますか?

41
Kev

引数ごとに終了コードを指定するメカニズムは知りません。 .parse_args()で発生したSystemExit例外をキャッチできますが、具体的にを確認する方法がわかりません)がエラーの原因です。

編集:実用的な解決策を探してこれに来る人にとって、以下は状況です:

  • ArgumentError()は、引数の解析が失敗したときに適切に発生します。引数インスタンスとメッセージが渡されます
  • ArgumentError()not引数をインスタンス属性として格納しますが、渡されます(これは便利です)
  • ArgumentErrorをサブクラス化し、.error()をオーバーライドしてsys.exc_info()から例外を取得することにより、ArgumentParser例外を再発生させることができます。

つまり、醜い一方で、次のコードを使用すると、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

有用な方法でテストされていません。通常は、責任を負わないでください。

44
Rob Cowie

すべての回答は、argparse実装の詳細をうまく説明しています。

実際、 [〜#〜] pep [〜#〜] で提案されているように(そしてRob Cowieによって指摘されています)、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.
39

おそらく、SystemExit例外をキャッチすると、簡単な回避策になります。

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

インタラクティブなセッションでも、私にとってはうまくいきます。

編集: @Rob Cowieが私をスイッチに倒したようです。彼が言ったように、あなたが愚かになってトレースバックから情報を収集しようとしない限り、これはあまり診断の可能性を持ちません。

34
Greg Haskins

既存のメソッドのいずれかを使用できます: http://docs.python.org/library/argparse.html#exiting-methods 。ただし、引数が無効である状況をすでに処理しているはずです(引数が適切に定義されている場合)。

無効な引数を使用:

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

あなたはいじくる必要があります。見る argparse.ArgumentParser.error、これは内部的に呼び出されるものです。または、引数を非必須にしてから、argparseの外部でチェックして終了することもできます。

2
Tobu

Argparse.errorはメソッドであり、クラスではありませんが、「試行」することはできませんが、「認識されない引数」のエラーはすべて「除きます」。そうする場合は、argparseのエラー関数をオーバーライドする必要があります。

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

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

無効な入力では、次のように出力されます。

unrecognised
1
d0n

アプリケーションの起動時に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
0
Mace