Pythonでカスタムメッセージを使用して同じ例外を発生させるにはどうすればよいですか?


145

try私のコードにこのブロックがあります:

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise ValueError(errmsg)

厳密に言えば、私は実際には、スローされたではなく、別の をレイズしています。これは、この場合と呼ばれます。カスタムメッセージを添付するにはどうすればよいですか。私は、次のコードを試してみたが起因して失敗した、例えば、呼び出し可能なものではありません。ValueErrorValueErrordo_something...()errerrerrValueError

try:
    do_something_that_might_raise_an_exception()
except ValueError as err:
    errmsg = 'My custom error message.'
    raise err(errmsg)

13
@Hamish、追加情報を添付して例外を再発生させると、デバッグ時に非常に役立ちます。
Johan Lundberg、2012

@Johan絶対に-そして、それがスタックトレースの目的です。新しいエラーを発生させるのではなく、既存のエラーメッセージを編集する理由がよくわかりません。
ハミッシュ2012

@ハミッシュ。もちろん、他のものを追加できます。あなたの質問については、私の答えとUnicodeDecodeErrorの例を見てください。もしそれについてコメントがあるなら、多分代わりに私の答えにコメントしてください。
Johan Lundberg、2012


1
@Kitそれは2020であり、python 3は至る所にあります。受け入れられた回答をベンの回答に変更しないでください:-)
mit

回答:


88

更新:Python 3の場合は、ベンの答えを確認してください


メッセージを現在の例外に添付して再度発生させるには:(外側のtry / exceptは効果を示すためだけです)

python 2.xの場合x> = 6:

try:
    try:
      raise ValueError  # something bad...
    except ValueError as err:
      err.message=err.message+" hello"
      raise              # re-raise current exception
except ValueError as e:
    print(" got error of type "+ str(type(e))+" with message " +e.message)

また、これは正しいことを行います場合errされる派生からValueError。例えばUnicodeDecodeError

好きなように追加できることに注意してくださいerr。例えばerr.problematic_array=[1,2,3]


編集:コメント内の@Ducanポイント.messageは、のメンバーではないため、上記はpython 3では機能しませんValueError。代わりにこれを使用できます(有効なpython 2.6以降または3.x)。

try:
    try:
      raise ValueError
    except ValueError as err:
       if not err.args: 
           err.args=('',)
       err.args = err.args + ("hello",)
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e.args))

Edit2:

目的に応じて、独自の変数名で追加情報を追加することもできます。python2とpython3の両方:

try:
    try:
      raise ValueError
    except ValueError as err:
       err.extra_info = "hello"
       raise 
except ValueError as e:
    print(" error was "+ str(type(e))+str(e))
    if 'extra_info' in dir(e):
       print e.extra_info

9
Python 3スタイルの例外処理とを使用する努力をprint終えたのでmessage、例外には属性がないため、Python 3.xではコードが機能しないことに注意してください。err.args = (err.args[0] + " hello",) + err.args[1:]より確実に動作する可能性があります(メッセージを取得するために文字列に変換するだけです)。
ダンカン

1
残念ながら、args [0]がエラーメッセージを表す文字列型である保証はありません-「例外コンストラクタに渡される引数のタプル。IOErrorなどの一部の組み込み例外は、特定の数の引数を予期し、特別な意味を割り当てますこのタプルの要素。他の要素は通常、エラーメッセージを示す単一の文字列でのみ呼び出されます。」そのため、コードは機能しませんarg [0]はエラーメッセージではありません(intの場合もあれば、ファイル名を表す文字列の場合もあります)。
トレント

1
@Taras、面白い。そのことについてのリファレンスはありますか?次に、完全に新しいメンバーに追加します:err.my_own_extra_info。または、新しい情報と元の情報を保持しながら、それをすべて自分の例外にカプセル化します。
Johan Lundberg、2013年

2
args [0]がエラーメッセージでない場合の実際の例-docs.python.org/2/library/exceptions.html-"exception EnvironmentError Pythonシステムの外部で発生する可能性がある例外の基本クラス:IOError、OSError。このタイプの例外が2タプルで作成される場合、最初の項目はインスタンスのerrno属性で使用でき(エラー番号と見なされます)、2番目の項目はstrerror属性で使用できます(通常、関連するエラーメッセージ)。タプル自体もargs属性で使用できます。」
トレント

2
全然分からない。.messageここで属性を設定する唯一の理由は、この属性が明示的に出力されるためです。あなたがキャッチし、印刷せずに例外を発生した場合、あなたは考えていない参照.message属性は便利な何かを。
DanielSank 2016

171

運が良ければpython 3.xのみをサポートするのであれば、これは本当に美しさになる:)

から上げる

例外を連鎖させることができます raise fromます。

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks') from e

この場合、呼び出し元がキャッチする例外には、例外を発生させる場所の行番号があります。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks') from e
Exception: Smelly socks

下の例外には、例外を発生させた場所からのスタックトレースしかないことに注意してください。呼び出し元は__cause__、キャッチした例外の属性にアクセスすることで、元の例外を引き続き取得できます。

with_traceback

または、with_tracebackを使用できます。

try:
    1 / 0
except ZeroDivisionError as e:
    raise Exception('Smelly socks').with_traceback(e.__traceback__)

この形式を使用すると、呼び出し元がキャッチする例外には、元のエラーが発生した場所からのトレースバックがあります。

Traceback (most recent call last):
  File "test.py", line 2, in <module>
    1 / 0
ZeroDivisionError: division by zero

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "test.py", line 4, in <module>
    raise Exception('Smelly socks').with_traceback(e.__traceback__)
  File "test.py", line 2, in <module>
    1 / 0
Exception: Smelly socks

下の例外には、無効な除算を実行した行と、例外を再発生させた行があります。


1
追加のトレースバックなしで例外にカスタムメッセージを追加することは可能ですか?たとえばraise Exception('Smelly socks') from e、独自の新しいトレースバックを導入するのではなく、「Smelly socks」をコメントとして元のトレースバックに追加するように変更できます。
joelostblom

それはあなたがヨハン・ランドバーグの回答から得られます行動だ
ベン

3
これは本当に素敵です。ありがとうございました。
アランベリー2017

3
新しい例外を再発生させたり、新しいメッセージで例外を発生させたりすると、多くの場合必要以上に混乱が生じます。例外自体は、処理が複雑です。より良い戦略は、可能であればerr.args + =( "message"、)のように元の例外の引数にメッセージを追加し、例外メッセージを再発生させることです。トレースバックでは、例外がキャッチされた行番号に移動しない場合がありますが、例外が発生した場所に確実に移動します。
user-asterix 2018年

2
あなたはまた、明示的に抑えることができるディスプレイ句からNoneを指定しないことにより、例外チェーンのを:raise RuntimeError("Something bad happened") from None
pfabri

10
try:
    try:
        int('a')
    except ValueError as e:
        raise ValueError('There is a problem: {0}'.format(e))
except ValueError as err:
    print err

プリント:

There is a problem: invalid literal for int() with base 10: 'a'

1
別のインスタンスを発生させる以外に、私がやろうとしていることのためのPythonイディオムがあるのだろうかと思っていました。
キット

@キット-私はそれを「例外の再発生」と呼びます:docs.python.org/reference/simple_stmts.html#raise
eumiro

1
@eumiro、新しい例外はありません。私の答えを見てください。あなたのリンクから:「しかし、再発生される例外が現在のスコープで最も最近アクティブな例外であった場合、式なしの発生が優先されるべきです。」
Johan Lundberg

3
@JohanLundberg- raiseパラメータなしで再調達されています。OPがメッセージを追加したい場合、OPは新しい例外を発生させ、元の例外のメッセージ/タイプを再利用できます。
eumiro

2
メッセージを追加したい場合は、「ValueError」をスローして最初から新しいメッセージを作成することはできません。そうすることで、それがどのようなValueErrorであるかという根本的な情報を破壊します(C ++でのスライスに似ています)。引数なしでraiseを使用して同じ例外を再スローすることにより、(ValueErrorから派生した)その正しい特定のタイプで元のオブジェクトを渡します。
Johan Lundberg、2015年

9

すべての回答がe.args [0]に情報を追加しているため、既存のエラーメッセージが変更されているようです。代わりにargsタプルを拡張することの欠点はありますか?考えられる利点は、その文字列の解析が必要な場合は、元のエラーメッセージをそのままにしておくことです。また、トレースバックがプログラムで(システム監視ツールなどを介して)解析される場合のために、カスタムエラー処理で複数のメッセージまたはエラーコードが生成される場合は、タプルに複数の要素を追加できます。

## Approach #1, if the exception may not be derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args = (e.args if e.args else tuple()) + ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

または

## Approach #2, if the exception is always derived from Exception and well-behaved:

def to_int(x):
    try:
        return int(x)
    except Exception as e:
        e.args += ('Custom message',)
        raise

>>> to_int('12')
12

>>> to_int('12 monkeys')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in to_int
ValueError: ("invalid literal for int() with base 10: '12 monkeys'", 'Custom message')

このアプローチにはマイナス面がありますか?


私の古い答えはe.args [0]を変更しません。
Johan Lundberg、2013年

4

このコードテンプレートを使用すると、カスタムメッセージで例外を発生させることができます。

try:
     raise ValueError
except ValueError as err:
    raise type(err)("my message")

3
これはスタックトレースを保持しません。
プロク2014年

質問者は、スタックトレースが保持されることを指定しません。
shrewmouse

4

エラーメッセージで新しい例外を発生させるか、

raise Exception('your error message')

または

raise ValueError('your error message')

発生させたい場所内、または 'from'を使用して現在の例外にエラーメッセージを添付(置換)します(Python 3.xのみサポート):

except ValueError as e:
  raise ValueError('your message') from e

Thanx、@ gberger、 'from e'アプローチは実際にはpython 2.xではサポートされていません
Alexey Antonenko

3

これは、元のトレースバックを保持しながら、Python 2.7および3.xで例外メッセージを変更するために使用する関数です。必要ですsix

def reraise_modify(caught_exc, append_msg, prepend=False):
    """Append message to exception while preserving attributes.

    Preserves exception class, and exception traceback.

    Note:
        This function needs to be called inside an except because
        `sys.exc_info()` requires the exception context.

    Args:
        caught_exc(Exception): The caught exception object
        append_msg(str): The message to append to the caught exception
        prepend(bool): If True prepend the message to args instead of appending

    Returns:
        None

    Side Effects:
        Re-raises the exception with the preserved data / trace but
        modified message
    """
    ExceptClass = type(caught_exc)
    # Keep old traceback
    traceback = sys.exc_info()[2]
    if not caught_exc.args:
        # If no args, create our own tuple
        arg_list = [append_msg]
    else:
        # Take the last arg
        # If it is a string
        # append your message.
        # Otherwise append it to the
        # arg list(Not as pretty)
        arg_list = list(caught_exc.args[:-1])
        last_arg = caught_exc.args[-1]
        if isinstance(last_arg, str):
            if prepend:
                arg_list.append(append_msg + last_arg)
            else:
                arg_list.append(last_arg + append_msg)
        else:
            arg_list += [last_arg, append_msg]
    caught_exc.args = tuple(arg_list)
    six.reraise(ExceptClass,
                caught_exc,
                traceback)

3

Python 3の組み込み例外には、次のstrerrorフィールドがあります。

except ValueError as err:
  err.strerror = "New error message"
  raise err

これは機能していないようです。あなたは何かを逃していますか?
MasayoMusic

2

例外が再キャッチされない場合、現在の回答はうまくいきませんでした。追加されたメッセージは表示されません。

ただし、以下のように実行すると、例外が再キャッチされるかどうかに関係なく、トレースが保持され、追加されたメッセージが表示されます。

try:
  raise ValueError("Original message")
except ValueError as err:
  t, v, tb = sys.exc_info()
  raise t, ValueError(err.message + " Appended Info"), tb

(私はPython 2.7を使用しましたが、Python 3では試していません)


1

上記の解決策のどれも、私が望んでいたことを正確に実行しませんでした。つまり、エラーメッセージの最初の部分に情報を追加することでした。つまり、ユーザーに最初にカスタムメッセージを表示させました。

これは私のために働きました:

exception_raised = False
try:
    do_something_that_might_raise_an_exception()
except ValueError as e:
    message = str(e)
    exception_raised = True

if exception_raised:
    message_to_prepend = "Custom text"
    raise ValueError(message_to_prepend + message)

0

これはPython 3でのみ機能します。例外の元の引数を変更して、独自の引数を追加できます。

例外はそれが作成された引数を記憶しています。これは、例外を変更できるようにするためだと思います。

関数でreraiseは、例外の元の引数に、必要な新しい引数(メッセージなど)を付加します。最後に、トレースバック履歴を保持しながら、例外を再発生させます。

def reraise(e, *args):
  '''re-raise an exception with extra arguments
  :param e: The exception to reraise
  :param args: Extra args to add to the exception
  '''

  # e.args is a tuple of arguments that the exception with instantiated with.
  #
  e.args = args + e.args

  # Recreate the expection and preserve the traceback info so thta we can see 
  # where this exception originated.
  #
  raise e.with_traceback(e.__traceback__)   


def bad():
  raise ValueError('bad')

def very():
  try:
    bad()
  except Exception as e:
    reraise(e, 'very')

def very_very():
  try:
    very()
  except Exception as e:
    reraise(e, 'very')

very_very()

出力

Traceback (most recent call last):
  File "main.py", line 35, in <module>
    very_very()
  File "main.py", line 30, in very_very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 28, in very_very
    very()
  File "main.py", line 24, in very
    reraise(e, 'very')
  File "main.py", line 15, in reraise
    raise e.with_traceback(e.__traceback__)
  File "main.py", line 22, in very
    bad()
  File "main.py", line 18, in bad
    raise ValueError('bad')
ValueError: ('very', 'very', 'bad')

-3

エラータイプをカスタマイズしたい場合は、ValueErrorに基づいてエラークラスを定義するだけです。


この場合、どのように役立ちますか?
Johan Lundberg、2012
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.