Abrufbeschreibung und Stapelverfolgung, die eine Ausnahme verursacht haben, als Zeichenfolge abrufen

423

Ich habe viele Beiträge über Stack-Trace und Ausnahmen in Python gesehen. Aber ich habe nicht gefunden, was ich brauche.

Ich habe einen Teil des Python 2.7-Codes, der möglicherweise eine Ausnahme auslöst. Ich möchte es abfangen und einem String seine vollständige Beschreibung und den Stack-Trace zuweisen , der den Fehler verursacht hat (einfach alles, was wir verwenden, um auf der Konsole zu sehen). Ich benötige diese Zeichenfolge, um sie in ein Textfeld in der GUI zu drucken.

Etwas wie das:

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

Das Problem ist: Was ist die Funktion complete_exception_description?

bläulich
quelle

Antworten:

613

Siehe das tracebackModul, speziell die format_exc()Funktion. Hier .

import traceback

try:
    raise ValueError
except ValueError:
    tb = traceback.format_exc()
else:
    tb = "No error"
finally:
    print tb
irgendwie
quelle
2
Funktioniert das nur mit dem letzten Fehler? Was passiert, wenn Sie den Fehler an andere Codebits weitergeben? Ich schreibe eine log_error(err)Funktion.
AnnanFay
Es funktioniert mit dem Fehler, der abgefangen und behandelt wurde.
Kindall
73

Lassen Sie uns einen anständig komplizierten Stacktrace erstellen, um zu demonstrieren, dass wir den vollständigen Stacktrace erhalten:

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

def do_something_that_might_error():
    raise_error()

Protokollierung des vollständigen Stacktraces

Eine bewährte Methode besteht darin, einen Logger für Ihr Modul einzurichten. Es kennt den Namen des Moduls und kann Ebenen ändern (unter anderem Attribute wie Handler).

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

Und wir können diesen Logger verwenden, um den Fehler zu erhalten:

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

Welche Protokolle:

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!

Und so erhalten wir die gleiche Ausgabe wie bei einem Fehler:

>>> 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!

Nur die Schnur bekommen

Wenn Sie wirklich nur die Zeichenfolge möchten, verwenden Sie traceback.format_excstattdessen die Funktion, um die Protokollierung der Zeichenfolge hier zu demonstrieren:

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

Welche Protokolle:

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!
Aaron Hall
quelle
1
Ist dies die beste Methode bei der Verwendung von Python 3 (im Vergleich zu einigen der folgenden Antworten)?
Yunti
1
@ Yunti Ich glaube, diese API war in Python 2 und 3 konsistent.
Aaron Hall
Die Formatierung dieser Antwort wurde auf meta: meta.stackoverflow.com/questions/386477/… diskutiert .
Lundin
Ich habe eine Bearbeitung an Folgendes gesendet, war aber nicht angemeldet und wurde als anonym except Exception as e: logger.exception("<<clearly and distinctly describe what failed here>>", exc_info=e)
angezeigt
@arntg Ich weiß es zu schätzen, dass Sie versuchen zu helfen, aber diese Bearbeitung wäre eine schädliche Änderung. Seien Sie in Zukunft viel vorsichtiger, um die APIs, die Sie verwenden möchten, vollständig zu verstehen. In diesem Fall wird das exc_infoerwartet Argument einen „exception Tupel“ , während die erroreine Instanz der IS ExceptionObjekt (oder Unterklasse), und es gibt keine Notwendigkeit zur Veränderung errorzu e.
Aaron Hall
39

Mit Python 3 formatiert der folgende Code ein ExceptionObjekt genau so, wie es mit erhalten würde traceback.format_exc():

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__)))

Der Vorteil besteht darin, dass nur das ExceptionObjekt benötigt wird (dank des aufgezeichneten __traceback__Attributs) und daher leichter als Argument zur weiteren Verarbeitung an eine andere Funktion übergeben werden kann.

Erwin Mayer
quelle
1
Es ist besser als sys.exc_info (), das keinen guten OO-Stil hat und eine globale Variable verwendet.
WeiChing
Hier wird speziell gefragt, wie der Traceback vom Ausnahmeobjekt abgerufen werden soll,
Ciro Santilli 郝海东 冠状 病 六四 事件 事件 18.
Es gibt einen einfacheren Python3-Weg ohne .__traceback__und type, siehe stackoverflow.com/a/58764987/5717886
don_vanchos
34
>>> 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

Mit sys.exc_info () sammeln Sie die Informationen und die Funktionen im tracebackModul, um sie zu formatieren. Hier sind einige Beispiele für die Formatierung.

Die gesamte Ausnahmezeichenfolge lautet:

>>> 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']
aeter
quelle
9

Für diejenigen, die Python-3 verwenden

Mit tracebackModul und exception.__traceback__kann man den Stack-Trace wie folgt extrahieren:

  • Holen Sie sich den aktuellen Stack-Trace mittraceback.extract_stack()
  • Entfernen Sie die letzten drei Elemente (da dies Einträge im Stapel sind, die mich zu meiner Debug-Funktion gebracht haben).
  • Hängen Sie das __traceback__aus dem Ausnahmeobjekt mit antraceback.extract_tb()
  • Formatieren Sie das Ganze mit 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)

Eine einfache Demonstration:

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

def bar():
    return foo()

Wir erhalten die folgende Ausgabe, wenn wir anrufen 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
Mike N.
quelle
Es sieht aus wie ein unlesbarer, komplizierter Code. In Python 3.5+ gibt es einen eleganteren und einfacheren Weg: stackoverflow.com/a/58764987/5717886
don_vanchos
6

Sie können auch das integrierte Python-Modul cgitb verwenden , um einige wirklich gute, gut formatierte Ausnahmeinformationen zu erhalten, einschließlich lokaler Variablenwerte, Quellcodekontext, Funktionsparameter usw.

Zum Beispiel für diesen Code ...

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)

wir bekommen diese Ausnahmeausgabe ...

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
Samaspin
quelle
5

Wenn Sie dieselben Informationen erhalten möchten, wenn eine Ausnahme nicht behandelt wird, können Sie Folgendes tun. Tun import tracebackund dann:

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

Ich benutze Python 3.7.

SamuelN
quelle
3

Für Python 3.5+ :

Sie können also die Stapelverfolgung von Ihrer Ausnahme wie von jeder anderen Ausnahme abrufen. Verwenden Sie traceback.TracebackExceptiones (ersetzen exSie es einfach durch Ihre Ausnahme):

print("".join(traceback.TracebackException.from_exception(ex).format())

Ein erweitertes Beispiel und andere Funktionen, um dies zu tun:

import traceback

try:
    1/0
except Exception as ex:
    print("".join(traceback.TracebackException.from_exception(ex).format()) == traceback.format_exc() == "".join(traceback.format_exception(type(ex), ex, ex.__traceback__))) # This is True !!
    print("".join(traceback.TracebackException.from_exception(ex).format()))

Die Ausgabe wird ungefähr so ​​aussehen:

True
Traceback (most recent call last):
  File "untidsfsdfsdftled.py", line 29, in <module>
    1/0
ZeroDivisionError: division by zero
don_vanchos
quelle
1

meine 2 Cent:

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

Wenn Ihr Ziel darin besteht, die Ausnahme- und Stacktrace-Nachricht genau so aussehen zu lassen, wie wenn Python einen Fehler auslöst, funktioniert Folgendes in Python 2 + 3:

import sys, traceback


def format_stacktrace():
    parts = ["Traceback (most recent call last):\n"]
    parts.extend(traceback.format_stack(limit=25)[:-2])
    parts.extend(traceback.format_exception(*sys.exc_info())[1:])
    return "".join(parts)

# EXAMPLE BELOW...

def a():
    b()


def b():
    c()


def c():
    d()


def d():
    assert False, "Noooh don't do it."


print("THIS IS THE FORMATTED STRING")
print("============================\n")

try:
    a()
except:
    stacktrace = format_stacktrace()
    print(stacktrace)

print("THIS IS HOW PYTHON DOES IT")
print("==========================\n")
a()

Es funktioniert, indem der letzte format_stacktrace()Aufruf vom Stapel entfernt und der Rest verbunden wird. Im obigen Beispiel wird beim Ausführen die folgende Ausgabe ausgegeben:

THIS IS THE FORMATTED STRING
============================

Traceback (most recent call last):
  File "test.py", line 31, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.

THIS IS HOW PYTHON DOES IT
==========================

Traceback (most recent call last):
  File "test.py", line 38, in <module>
    a()
  File "test.py", line 12, in a
    b()
  File "test.py", line 16, in b
    c()
  File "test.py", line 20, in c
    d()
  File "test.py", line 24, in d
    assert False, "Noooh don't do it."
AssertionError: Noooh don't do it.
Rune Kaagaard
quelle
0

Ich habe folgende Hilfsklasse definiert:

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

Was ich später so verwenden kann:

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

Und später kann es so konsumieren:

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

(Hintergrund: Ich war frustraded, weil ich Promises zusammen mit Exceptions verwendet habe, was leider Ausnahmen, die an einer Stelle ausgelöst wurden, an einen on_rejected-Handler an einer anderen Stelle weitergibt, und daher ist es schwierig, den Traceback vom ursprünglichen Ort zu erhalten.)

qbolec
quelle