私のバックグラウンドは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]
これにはメッセージと両方のトレースバックが含まれますが、トレースバックのどこでどの例外が発生したかは表示されません。
簡単だ; raiseの3番目の引数としてトレースバックを渡します。
import sys
class MyException(Exception): pass
try:
raise TypeError("test")
except TypeError, e:
raise MyException(), None, sys.exc_info()[2]
ある例外をキャッチし、別の例外を再発生させる場合は、常にこれを実行してください。
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")
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()
が呼び出された行が表示されていることがわかりますが、例外メッセージにはコマンドラインが含まれています。
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
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]は、@@という配列に連鎖例外を暗黙的に保持する例外メカニズムを提案しています。
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)
CausedException class を使用して、Python 2.xで例外を連鎖することができます(さらに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ブロックで例外を再発生させます。
おそらく、関連情報を取得して渡すことができますか?私は次のようなものを考えています:
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