web-dev-qa-db-ja.com

例外の原因となった例外の説明とスタックトレースをすべて文字列として取得します。

私はPythonでスタックトレースと例外について多くの記事を見ました。しかし、私が必要なものが見つかりませんでした。

私は例外を発生させる可能性のあるPython 2.7コードの塊を持っています。それをキャッチして、エラーの原因となったstringその完全な説明とスタックトレース(単にコンソールで表示するために使用するすべてのもの)に割り当てます。 GUIのテキストボックスに表示するには、この文字列が必要です。

このようなもの:

try:
    method_that_can_raise_an_exception(params)
except Exception as e:
    print_to_textbox(complete_exception_description(e))

問題は次のとおりです。関数complete_exception_descriptionとは何ですか?

341
bluish

tracebackモジュール、特にformat_exc()関数を参照してください。 ここ

import traceback

try:
    raise ValueError
except ValueError:
    tb = traceback.format_exc()
else:
    tb = "No error"
finally:
    print tb
503
kindall

例外の原因となった例外の説明とスタックトレースをすべて文字列として取得します。

完全なスタックトレースを取得することを実証するために、かなり複雑なスタックトレースを作成するには、次の手順に従います。

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

フルスタックトレースを記録する

ベストプラクティスは、あなたのモジュール用にロガーをセットアップすることです。それはモジュールの名前を知っていて、レベルを変えることができるでしょう(他の属性の中でも、例えばハンドラーのように)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

そして、このロガーを使ってエラーを取得することができます。

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

どのログ:

ERROR:__main__:something bad happened!
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

そのため、エラーが発生したときと同じ出力が得られます。

>>> do_something_that_might_error()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!

文字列だけを取得する

本当に文字列がほしい場合は、代わりにtraceback.format_exc関数を使用して、ここに文字列を記録する方法を示します。

import traceback
try:
    do_something_that_might_error()
except Exception as error:
    just_the_string = traceback.format_exc()
    logger.debug(just_the_string)

どのログ:

DEBUG:__main__:Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
  File "<stdin>", line 2, in do_something_that_might_error
  File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
43
Aaron Hall
>>> import sys
>>> import traceback
>>> try:
...   5 / 0
... except ZeroDivisionError as e:
...   type_, value_, traceback_ = sys.exc_info()
>>> traceback.format_tb(traceback_)
['  File "<stdin>", line 2, in <module>\n']
>>> value_
ZeroDivisionError('integer division or modulo by zero',)
>>> type_
<type 'exceptions.ZeroDivisionError'>
>>>
>>> 5 / 0
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero

フォーマットするためにtracebackname__モジュール内の情報と関数を収集するには、sys.exc_info()を使用します。 ここ フォーマットの例です。

例外ストリング全体は次の場所にあります。

>>> ex = traceback.format_exception(type_, value_, traceback_)
>>> ex
['Traceback (most recent call last):\n', '  File "<stdin>", line 2, in <module>\n', 'ZeroDivisionError: integer division or modulo by zero\n']
33
aeter

Python 3では、次のコードはtraceback.format_exc()を使用して得られるものとまったく同じようにExceptionオブジェクトをフォーマットします。

import traceback

try: 
    method_that_can_raise_an_exception(params)
except Exception as ex:
    print(''.join(traceback.format_exception(etype=type(ex), value=ex, tb=ex.__traceback__)))

利点はExceptionオブジェクトだけが必要であり(記録された__traceback__属性のおかげで)、したがってさらなる処理のために別の関数への引数としてより簡単に渡すことができるということです。

31
Erwin Mayer

Python-を使っている人のために

tracebackモジュールとexception.__traceback__を使用すると、次のようにスタックトレースを抽出できます。

  • traceback.extract_stack()を使用してcurrentスタックトレースを取得します
  • 最後の3つの要素を削除します(これらはスタック内のエントリなので、デバッグ機能に移ります)。
  • traceback.extract_tb()を使用して例外オブジェクトから__traceback__を追加します
  • traceback.format_list()を使って全体をフォーマットする
import traceback
def exception_to_string(excp):
   stack = traceback.extract_stack()[:-3] + traceback.extract_tb(excp.__traceback__)  # add limit=?? 
   pretty = traceback.format_list(stack)
   return ''.join(pretty) + '\n  {} {}'.format(excp.__class__,excp)

簡単なデモンストレーション:

def foo():
    try:
        something_invalid()
    except Exception as e:
        print(exception_to_string(e))

def bar():
    return foo()

bar()を呼び出すと、次のような出力が得られます。

  File "./test.py", line 57, in <module>
    bar()
  File "./test.py", line 55, in bar
    return foo()
  File "./test.py", line 50, in foo
    something_invalid()

  <class 'NameError'> name 'something_invalid' is not defined
7
Mike N

また、組み込みのPythonモジュール cgitb を使用して、以下のような本当に優れた形式の例外情報を取得することもできます。ローカル変数値、ソースコードコンテキスト、関数パラメータなど.

例えばこのコードでは...

import cgitb
cgitb.enable(format='text')

def func2(a, divisor):
    return a / divisor

def func1(a, b):
    c = b - 5
    return func2(a, c)

func1(1, 5)

この例外出力を受け取ります...

ZeroDivisionError
Python 3.4.2: C:\tools\python\python.exe
Tue Sep 22 15:29:33 2015

A problem occurred in a Python script.  Here is the sequence of
function calls leading up to the error, in the order they occurred.

 c:\TEMP\cgittest2.py in <module>()
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
func1 = <function func1>

 c:\TEMP\cgittest2.py in func1(a=1, b=5)
    7 def func1(a, b):
    8   c = b - 5
    9   return func2(a, c)
   10
   11 func1(1, 5)
global func2 = <function func2>
a = 1
c = 0

 c:\TEMP\cgittest2.py in func2(a=1, divisor=0)
    3
    4 def func2(a, divisor):
    5   return a / divisor
    6
    7 def func1(a, b):
a = 1
divisor = 0
ZeroDivisionError: division by zero
    __cause__ = None
    __class__ = <class 'ZeroDivisionError'>
    __context__ = None
    __delattr__ = <method-wrapper '__delattr__' of ZeroDivisionError object>
    __dict__ = {}
    __dir__ = <built-in method __dir__ of ZeroDivisionError object>
    __doc__ = 'Second argument to a division or modulo operation was zero.'
    __eq__ = <method-wrapper '__eq__' of ZeroDivisionError object>
    __format__ = <built-in method __format__ of ZeroDivisionError object>
    __ge__ = <method-wrapper '__ge__' of ZeroDivisionError object>
    __getattribute__ = <method-wrapper '__getattribute__' of ZeroDivisionError object>
    __gt__ = <method-wrapper '__gt__' of ZeroDivisionError object>
    __hash__ = <method-wrapper '__hash__' of ZeroDivisionError object>
    __init__ = <method-wrapper '__init__' of ZeroDivisionError object>
    __le__ = <method-wrapper '__le__' of ZeroDivisionError object>
    __lt__ = <method-wrapper '__lt__' of ZeroDivisionError object>
    __ne__ = <method-wrapper '__ne__' of ZeroDivisionError object>
    __new__ = <built-in method __new__ of type object>
    __reduce__ = <built-in method __reduce__ of ZeroDivisionError object>
    __reduce_ex__ = <built-in method __reduce_ex__ of ZeroDivisionError object>
    __repr__ = <method-wrapper '__repr__' of ZeroDivisionError object>
    __setattr__ = <method-wrapper '__setattr__' of ZeroDivisionError object>
    __setstate__ = <built-in method __setstate__ of ZeroDivisionError object>
    __sizeof__ = <built-in method __sizeof__ of ZeroDivisionError object>
    __str__ = <method-wrapper '__str__' of ZeroDivisionError object>
    __subclasshook__ = <built-in method __subclasshook__ of type object>
    __suppress_context__ = False
    __traceback__ = <traceback object>
    args = ('division by zero',)
    with_traceback = <built-in method with_traceback of ZeroDivisionError object>

The above is a description of an error in a Python program.  Here is
the original traceback:

Traceback (most recent call last):
  File "cgittest2.py", line 11, in <module>
    func1(1, 5)
  File "cgittest2.py", line 9, in func1
    return func2(a, c)
  File "cgittest2.py", line 5, in func2
    return a / divisor
ZeroDivisionError: division by zero
5
samaspin

私の2セント:

import sys, traceback
try: 
  ...
except Exception, e:
  T, V, TB = sys.exc_info()
  print ''.join(traceback.format_exception(T,V,TB))
1
user 1155692

例外が処理されないときに同じ情報を取得したい場合は、次のようにします。次にtracebackをインポートします。

try:
    ...
except Exception as e:
    print(traceback.print_tb(e.__traceback__))

私はpython 3.7を使っています

1
SamuelN

以下のヘルパークラスを定義しました。

import traceback
class TracedExeptions(object):
    def __init__(self):
        pass
    def __enter__(self):
        pass

    def __exit__(self, etype, value, tb):
      if value :
        if not hasattr(value, 'traceString'):
          value.traceString = "\n".join(traceback.format_exception(etype, value, tb))
        return False
      return True

私は後でこのように使うことができます:

with TracedExeptions():
  #some-code-which-might-throw-any-exception

そして、後でこれを消費することができます。

def log_err(ex):
  if hasattr(ex, 'traceString'):
    print("ERROR:{}".format(ex.traceString));
  else:
    print("ERROR:{}".format(ex));

(背景:PromisesをExceptionsと一緒に使用していたために不満がありました。残念ながら、ある場所で発生した例外が別の場所のon_rejectedハンドラに渡されるため、元の場所からトレースバックを取得するのが困難です。)

0
qbolec