web-dev-qa-db-ja.com

Pythonの「内部例外」(トレースバック付き)?

私のバックグラウンドはC#で、最近Pythonでプログラミングを始めました。例外がスローされた場合、通常は、完全なスタックトレースを表示しながら、さらに情報を追加する別の例外にラップする必要があります。 C#では非常に簡単ですが、Pythonではどのようにすればよいですか?

例えば。 C#では、次のようにします。

try
{
  ProcessFile(filePath);
}
catch (Exception ex)
{
  throw new ApplicationException("Failed to process file " + filePath, ex);
}

Python似たようなことができます:

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file ' + filePath, e)

...しかし、これは内部例外のトレースバックを失います!

編集:両方の例外メッセージとスタックトレースの両方を表示し、2つを相関させたいです。つまり、出力で例外Xがここで発生し、次に例外Yが発生したことを確認したいのです。C#の場合と同じです。これは、Python 2.6?

try:
  ProcessFile(filePath)
except Exception as e:
  raise Exception('Failed to process file' + filePath, e), None, sys.exc_info()[2]

これにはメッセージと両方のトレースバックが含まれますが、トレースバックのどこでどの例外が発生したかは表示されません。

128
EMP

Python 2

簡単だ; raiseの3番目の引数としてトレースバックを渡します。

import sys
class MyException(Exception): pass

try:
    raise TypeError("test")
except TypeError, e:
    raise MyException(), None, sys.exc_info()[2]

ある例外をキャッチし、別の例外を再発生させる場合は、常にこれを実行してください。

132
Glenn Maynard

Python 3

python 3では、次のことができます。

try:
    raise MyExceptionToBeWrapped("I have twisted my ankle")

except MyExceptionToBeWrapped as e:

    raise MyWrapperException("I'm not in a good shape") from e

これにより、次のようなものが生成されます。

   Traceback (most recent call last):
   ...
   MyExceptionToBeWrapped: ("I have twisted my ankle")

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

   Traceback (most recent call last):
   ...
   MyWrapperException: ("I'm not in a good shape")
203
Alexei Tenitski

Python 3には raise ... from があり、例外を連鎖します。 Glenn's answer はPython 2.7に最適ですが、元の例外のトレースバックのみを使用し、エラーメッセージやその他の詳細を破棄します。以下は、現在のスコープから元の例外のエラーメッセージにコンテキスト情報を追加するPython 2.7のいくつかの例ですが、他の詳細はそのままにします。

既知の例外タイプ

_try:
    sock_common = xmlrpclib.ServerProxy(rpc_url+'/common')
    self.user_id = sock_common.login(self.dbname, username, self.pwd)
except IOError:
    _, ex, traceback = sys.exc_info()
    message = "Connecting to '%s': %s." % (config['connection'],
                                           ex.strerror)
    raise IOError, (ex.errno, message), traceback
_

raise statement のフレーバーは、例外タイプを最初の式、タプル内の例外クラスコンストラクター引数を2番目の式、トレースバックを3番目の式として受け取ります。 Python 2.2より前に実行している場合は、 sys.exc_info() の警告を参照してください。

例外タイプ

コードがどのような例外をキャッチする必要があるかわからない場合の、より一般的な目的の別の例を次に示します。欠点は、例外タイプを失い、RuntimeErrorを発生させるだけです。 tracebackモジュールをインポートする必要があります。

_except Exception:
    extype, ex, tb = sys.exc_info()
    formatted = traceback.format_exception_only(extype, ex)[-1]
    message = "Importing row %d, %s" % (rownum, formatted)
    raise RuntimeError, message, tb
_

メッセージを修正する

例外タイプでコンテキストを追加できる場合の別のオプションがあります。例外のメッセージを変更してから、再送信できます。

_import subprocess

try:
    final_args = ['lsx', '/home']
    s = subprocess.check_output(final_args)
except OSError as ex:
    ex.strerror += ' for command {}'.format(final_args)
    raise
_

これにより、次のスタックトレースが生成されます。

_Traceback (most recent call last):
  File "/mnt/data/don/workspace/scratch/scratch.py", line 5, in <module>
    s = subprocess.check_output(final_args)
  File "/usr/lib/python2.7/subprocess.py", line 566, in check_output
    process = Popen(stdout=PIPE, *popenargs, **kwargs)
  File "/usr/lib/python2.7/subprocess.py", line 710, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1327, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory for command ['lsx', '/home']
_

check_output()が呼び出された行が表示されていることがわかりますが、例外メッセージにはコマンドラインが含まれています。

18
Don Kirkby

Python 3.x

raise Exception('Failed to process file ' + filePath).with_traceback(e.__traceback__)

or単純に

except Exception:
    raise MyException()

MyExceptionを伝播しますが、処理されない場合はboth例外を出力します。

Python 2.x

raise Exception, 'Failed to process file ' + filePath, e

__context__属性を強制終了することにより、両方の例外の出力を防ぐことができます。ここで、それを使って例外をキャッチして変更するコンテキストマネージャを作成します:( http://docs.python.org/3.1/library/stdtypes.html を参照してください。仕組み)

try: # Wrap the whole program into the block that will kill __context__.

    class Catcher(Exception):
        '''This context manager reraises an exception under a different name.'''

        def __init__(self, name):
            super().__init__('Failed to process code in {!r}'.format(name))

        def __enter__(self):
            return self

        def __exit__(self, exc_type, exc_val, exc_tb):
            if exc_type is not None:
                self.__traceback__ = exc_tb
                raise self

    ...


    with Catcher('class definition'):
        class a:
            def spam(self):
                # not really pass, but you get the idea
                pass

            lut = [1,
                   3,
                   17,
                   [12,34],
                   5,
                   _spam]


        assert a().lut[-1] == a.spam

    ...


except Catcher as e:
    e.__context__ = None
    raise
10
ilya n.

Python 2.xでこれを行うことはできないと思いますが、この機能に似たものはPython 3. From PEP 3134

今日のPython実装では、例外はタイプ、値、トレースバックの3つの部分で構成されます。「sys」モジュールは、3つの並列変数exc_type、exc_valueで現在の例外を公開します。およびexc_traceback、sys.exc_info()関数はこれら3つの部分のTupleを返し、「raise」ステートメントはこれらの3つの部分を受け入れる3つの引数の形式を持ちます。また、「except」ステートメントは、トレースバックではなく、値へのアクセスのみを提供できます。例外に「traceback」属性を追加値を使用すると、すべての例外情報に単一の場所からアクセスできます。

C#との比較:

C#の例外には、別の例外を指す可能性のある読み取り専用の 'InnerException'プロパティが含まれています。そのドキュメント[10]には、「前の例外Yの直接の結果として例外Xがスローされる場合、XのInnerExceptionプロパティにはYへの参照が含まれている必要があります。」このプロパティはVMによって自動的に設定されません;むしろ、すべての例外コンストラクタはオプションの 'innerException'引数を取り、明示的に設定します。 'cause '属性はInnerExceptionと同じ目的を果たしますが、このPEPは、すべての例外のコンストラクターを拡張するのではなく、新しい形式の' raise 'を提案します。C#は、 InnerExceptionチェーン;このPEPはアナログを提案しません。

また、Java、RubyおよびPerl 5もこのタイプのことをサポートしていません。再度引用してください。

他の言語に関しては、JavaおよびRuby=は、 'catch'/'rescue'または 'finally'/'で別の例外が発生した場合、元の例外を破棄します。 Perl 5には組み込みの構造化例外処理がありませんPerl 6の場合、RFC番号88 [9]は、@@という配列に連鎖例外を暗黙的に保持する例外メカニズムを提案しています。

5
ire_and_curses

Python 2と3の間で最大限の互換性を得るには、sixライブラリでraise_fromを使用できます。 https://six.readthedocs.io/ #six.raise_from 。以下に例を示します(わかりやすくするために少し変更しています)。

import six

try:
  ProcessFile(filePath)
except Exception as e:
  six.raise_from(IOError('Failed to process file ' + repr(filePath)), e)
3
LexieHankins

CausedException class を使用して、Python 2.xで例外を連鎖することができます(さらにPython 3新たに発生した例外の原因として、キャッチされた複数の例外を提供したい場合)。

3
Alfe

想定:

  • Python 2(純粋なPython 3の場合はraise ... fromソリューション)
  • エラーメッセージを充実させたいだけです。追加のコンテキストを提供する
  • 完全なスタックトレースが必要

docs https://docs.python.org/3/tutorial/errors.html#raising-exceptions から簡単な解決策を使用できます。

try:
    raise NameError('HiThere')
except NameError:
    print 'An exception flew by!' # print or log, provide details about context
    raise # reraise the original exception, keeping full stack trace

出力:

An exception flew by!
Traceback (most recent call last):
  File "<stdin>", line 2, in ?
NameError: HiThere

重要な部分は、独立した単純化された「レイズ」キーワードのようです。それは、exceptブロックで例外を再発生させます。

2
atreat

おそらく、関連情報を取得して渡すことができますか?私は次のようなものを考えています:

import traceback
import sys
import StringIO

class ApplicationError:
    def __init__(self, value, e):
        s = StringIO.StringIO()
        traceback.print_exc(file=s)
        self.value = (value, s.getvalue())

    def __str__(self):
        return repr(self.value)

try:
    try:
        a = 1/0
    except Exception, e:
        raise ApplicationError("Failed to process file", e)
except Exception, e:
    print e
2
brool