2012-01-25 4 views
19

私は関数に適用するデコレータを書いています。例外を捕捉し、元の例外メッセージに基づいてカスタム例外を発生させる必要があります。 (これは、sudsがジェネリックWebFault例外をスローし、そのメッセージからWebサービスからスローされた例外を解析し、それをミラーリングするPythonの例外を発生させるからです。)しかし、ラッパーでカスタム例外を発生させると、 stacktraceが元のWebFault例外を発生させた関数を指すようにします。私はこれまでに正しい例外を発生させています(メッセージを動的に解析して例外クラスをインスタンス化します)。 私の質問: WebTrax例外を発生させた元の関数を指すようにスタックトレースを保存するにはどうすればよいですか? Pythonの2.xでPython:例外デコレータ。 stacktraceを保持する方法

from functools import wraps 

def try_except(fn): 
     def wrapped(*args, **kwargs): 
      try: 
       fn(*args, **kwargs) 
      except Exception, e: 
       parser = exceptions.ExceptionParser() 
       raised_exception = parser.get_raised_exception_class_name(e) 
       exception = getattr(exceptions, raised_exception) 
       raise exception(parser.get_message(e)) 
     return wraps(fn)(wrapped) 
+1

あなたは 'traceback'モジュールで見たことがありますか? http://docs.python.org/library/traceback.html – stderr

+0

デコレータでラップするときは、[functools.wrap](https://docs.python.org/2/library/functools.html) –

+0

の可能な複製["内部例外"(トレースバック付き)のPython?](http://stackoverflow.com/questions/1350671/inner-exception-with-traceback-in-python) –

答えて

37

raiseのあまり知られていない機能はそれ以上だけの引数で使用することができることである。raiseの三引数形式は、例外の種類を取り、例外インスタンスとトレースバック。 sys.exc_info()でトレースバックすると、例外タイプ、例外インスタンス、およびトレースバックを(偶然ではない)返します。

(これは、2つの別個の引数として例外タイプと例外インスタンスを扱う理由は、例外クラスの前の日からアーティファクトである。)

だから:これは変更のPython 3では

import sys 

class MyError(Exception): 
    pass 

def try_except(fn): 
    def wrapped(*args, **kwargs): 
     try: 
      return fn(*args, **kwargs) 
     except Exception, e: 
      et, ei, tb = sys.exc_info() 
      raise MyError, MyError(e), tb 
    return wrapped 

def bottom(): 
    1/0 

@try_except 
def middle(): 
    bottom() 

def top(): 
    middle() 

>>> top() 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "tmp.py", line 24, in top 
    middle() 
    File "tmp.py", line 10, in wrapped 
    return fn(*args, **kwargs) 
    File "tmp.py", line 21, in middle 
    bottom() 
    File "tmp.py", line 17, in bottom 
    1/0 
__main__.MyError: integer division or modulo by zero 

少し。そこでは、トレースバックではなく、例外インスタンスに取り付けられており、彼らはwith_tracebackメソッドを持っている。一方のPythonの3で

raise MyError(e).with_traceback(tb) 

も、多くの場合に、より理にかなってを連鎖例外を、持っています。

raise MyError(e) from e 
+0

ありがとう!これは仕事をした。私はデコレータの宣言を、装飾される関数と同じファイルに移動しなければならないという問題がありました。それ以外の場合、sys.exc_info()は返されませんでした(None、None、None)。 – igniteflow

+0

それは...意味がありません。 sys.exc_info()は、呼び出し側がどこに定義されているかは気にしません。現在処理中の例外を返します。別のファイルにあるデコレータが正しいことをしていないように聞こえますが、実際のコードを見ずに言うのは難しいです。 –

4

カスタムデコレータで飾られたテストでこの問題が発生しました。私はユニットテスト出力で印刷元のトレース維持するためにデコレータ本体に構築後に使用

​​
+0

ええ、デコレータの引数は何ですか? –

+0

上記のコードとの違いが 'traceback.format_tb'であることを指摘してください。ありがとう。 – Ehvince

関連する問題