Kural dışı duruma neden olan istisna açıklaması ve yığın izlemesi alın, tümü dize olarak


423

Python'da yığın izlemesi ve istisnalar hakkında birçok yazı gördüm. Ama ihtiyacım olanı bulamadım.

Bir istisna oluşturabilir Python 2.7 kodu bir yığın var. Yakalamak ve bir dizeye tam açıklamasını ve hataya neden olan yığın izlemesini (yalnızca konsolda görmek için kullandığımız) atamak istiyorum . GUI bir metin kutusuna yazdırmak için bu dize gerekir.

Bunun gibi bir şey:

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

Sorun şudur: fonksiyon complete_exception_descriptionnedir?

Yanıtlar:


615

Bkz tracebackmodülü, özel format_exc()bir işlev. İşte .

import traceback

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

2
Bu yalnızca son hatayla mı çalışır? Hatayı diğer kod parçalarına aktarmaya başlarsanız ne olur? Bir log_error(err)işlev yazıyorum .
AnnanFay

Yakalanan ve işlenen hata ile çalışır.
tür

74

Tam yığın izini aldığımızı göstermek için oldukça karmaşık bir yığın izlemesi oluşturalım:

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

def do_something_that_might_error():
    raise_error()

Yığın izinin tamamını kaydetme

En iyi uygulama, modülünüz için bir günlükçünün ayarlanmasıdır. Modülün adını bilecek ve seviyeleri değiştirebilecektir (işleyiciler gibi diğer özellikler arasında)

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

Ve hatayı almak için bu kaydediciyi kullanabiliriz:

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

Hangi günlükler:

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!

Ve böylece bir hatayla karşılaştığımız zamanki çıktıyı alıyoruz:

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

Sadece dizeyi almak

Dizeyi gerçekten istiyorsanız traceback.format_exc, dizeyi buraya günlüğe kaydettiğini göstermek için işlevi kullanın:

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

Hangi günlükler:

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!

1
python 3 kullanırken bu en iyi yöntem midir (örneğin aşağıdaki cevaplardan bazılarına kıyasla)?
Yunti

1
@Yunti Bu API'nin Python 2 ve 3'te tutarlı olduğuna inanıyorum
Aaron Hall

Bu yanıtın biçimlendirilmesi meta: meta.stackoverflow.com/questions/386477/… adresinde tartışıldı .
Lundin

Aşağıdakilere bir düzenleme gönderdim ancak anonim olarak gösterilen şekilde oturum except Exception as e: logger.exception("<<clearly and distinctly describe what failed here>>", exc_info=e)
açmadım

@arntg Yardım etmeye çalıştığınızı takdir ediyorum, ancak bu düzenleme zararlı bir değişiklik olacaktır. Kullanmaya çalıştığınız API'ları tam olarak anlamak için lütfen gelecekte çok daha dikkatli olun. Bu durumda, exc_infobuna karşın bağımsız değişken bir "durum tuple" bekler errorbir örneği olan Exceptionbir nesne (veya alt) ve değişikliğe gerek yoktur erroriçin e.
Aaron Hall

39

Python 3 ile, aşağıdaki kod bir Exceptionnesneyi tam olarak elde edilecek şekilde biçimlendirir 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__)))

Avantajı, sadece Exceptionnesneye ihtiyaç duyulmasıdır (kaydedilen __traceback__öznitelik sayesinde ) ve bu nedenle daha fazla işlem için başka bir işleve argüman olarak daha kolay aktarılabilir.


1
İyi OO stili olmayan ve global değişkeni kullanan sys.exc_info () 'dan daha iyidir.
WeiChing 清 煒 清

Bu, burada yaptığınız gibi özel durum nesnesinden izlemeyi nasıl alacağınızı sorar: stackoverflow.com/questions/11414894/…
Ciro Santilli 13:09'da

Kullanmadan daha basit bir Python3 yolu vardır .__traceback__ve typebkz. 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

Biçimlendirmek için modüldeki bilgileri ve işlevleri toplamak için sys.exc_info () yöntemini kullanırsınız traceback. Buraya o biçimlendirmek için bazı örneklerdir.

İstisna dizesinin tamamı şuradadır:

>>> 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']

9

Python-3 kullananlar için

Kullanma tracebackmodülü ve exception.__traceback__aşağıdaki şekilde bir yığın izlemesi özü:

  • kullanarak geçerli yığın izini yakalatraceback.extract_stack()
  • Son üç öğeyi kaldır (bunlar, yığıntaki beni hata ayıklama işlevime götüren girdiler olduğu için)
  • ekleme __traceback__durum nesneden kullanılaraktraceback.extract_tb()
  • kullanarak her şeyi biçimlendir 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)

Basit bir gösteri:

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

def bar():
    return foo()

Aradığımızda aşağıdaki çıktıyı alırız 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

Okunamayan karmaşık bir koda benziyor. In Python 3.5+ daha zarif ve basit bir yolu vardır: stackoverflow.com/a/58764987/5717886
don_vanchos

6

Ayrıca , yerel değişken değerleri, kaynak kodu bağlamı, işlev parametreleri vb.Dahil olmak üzere gerçekten iyi, güzel biçimlendirilmiş bazı özel durum bilgileri almak için yerleşik Python modülünü ( cgitb) kullanmayı düşünebilirsiniz .

Örneğin bu kod için ...

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)

bu istisna çıktısını alıyoruz ...

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

İstisna ele alınmadığında aynı bilgileri vermek isterseniz, böyle bir şey yapabilirsiniz. Yapın import tracebackve sonra:

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

Python 3.7 kullanıyorum.


3

İçin Python 3.5+ :

Böylece, diğer herhangi bir istisna gibi yığın izini istisnanızdan alabilirsiniz. Bunun için kullanın traceback.TracebackException(sadece existisnanızla değiştirin ):

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

Genişletilmiş bir örnek ve bunu yapmak için diğer özellikler:

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

Çıktı şu şekilde olacaktır:

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

1

2 sentim:

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

1

Amacınız istisna ve yığın izleme mesajının python hata verdiğinde olduğu gibi görünmesini sağlamaksa, aşağıdaki iki python 2 + 3'te de çalışır:

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

Son format_stacktrace()çağrıyı yığından kaldırarak ve geri kalanını birleştirerek çalışır. Çalıştırıldığında, yukarıdaki örnek aşağıdaki çıktıyı verir:

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.

0

Aşağıdaki yardımcı sınıfı tanımladım:

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

Hangi daha sonra böyle kullanabilirsiniz:

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

Ve daha sonra bu şekilde tüketebilir:

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

(Geçmiş: Maalesef Promises ile birlikte kullanıldığımdan dolayı Exceptionhayal kırıklığına uğradım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.