Рефакторинг обработки ошибок и получить правильную трассировку стека в Python
Я получил много кода, как это:
try:
# do a lot of stuff
except StuffError as e:
log.exception(e):
send_mail_to_admin()
raise e
Для СУХОГО я хотел бы преобразовать это в:
def post_mortem(log, e):
log.exception(e):
send_mail_to_admin()
# some other stuff
raise e
Затем сделайте:
try:
# do a lot of stuff
except StuffError as e:
post_mortem(log, e)
Проблема в том, что теперь я не получаю правильную трассировку стека, так как исключение возникает из другого файла.
Как я могу получить ту же трассировку стека, которую я имел бы с первым кодом?
2 ответа
.. Вы хотели это СУХОЙ? Попробуйте это тогда:)
class ReportExceptions(object):
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
print("Sending mail to admin about {0!r}".format(exc_value))
И используйте это так:
with ReportExceptions():
# your code here..
С помощью диспетчера контекста вам не нужно заботиться о повторном возбуждении исключений, сохранении трассировок или других вещей: __exit__()
Метод будет выполнен несмотря ни на что, передав вам необходимую информацию. Если вы ничего не делаете с этим (например. return True
) исключение просто продолжит свой путь..
Примечание: вы можете создать экземпляр диспетчера контекста только один раз, например:
class ReportExceptions(object):
def __init__(self, email):
self.email = email
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, tb):
if exc_type is not None:
print("Sending mail to {0!r} about {1!r}"
"".format(self.email, exc_value))
report_exceptions = ReportExceptions('foo@bar.com')
затем:
with report_exceptions:
# ..your code here..
К вашему сведению: повышение исключения с помощью пользовательской трассировки
В случае, если вам действительно нужно повторно вызвать исключение, вы можете сохранить трассировку и сохранить ее на потом.
try:
100 / 0
except ZeroDivisionError, e:
exc_info = sys.exc_info()
...позже...
raise exc_info[0], exc_info[1], exc_info[2]
(Синтаксис на самом деле raise expression, expression, expression
Я не могу придумать, как лучше использовать кортеж напрямую...)
Осматривать кадры вдоль пути
Вы можете получить доступ tb
атрибуты для проверки выполнения фреймов вдоль пути, например, местные жители из "самой внешней" точки tb.tb_frame.f_locals
внутренняя рамка находится на tb.tb_next.tb_frame
, так далее...
(Вы также можете использовать inspect
модуль, см. /questions/46211547/inspecttrace-vs-traceback/46211555#46211555)
Пройти exc_info()
информация также, как один из параметров, подобных этому
post_mortem(log, e, sys.exc_info()[2])
И в post_mortem
def post_mortem(log, e, traceBack):
traceback.print_tb(traceBack)
Чтобы получить всю трассировку стека, вы можете сделать так, как показано в этом примере
import traceback, sys
def post_mortem(log, e, tb):
print "".join(traceback.format_list(traceback.extract_stack()[:-2]) + [traceback.format_tb(tb)[0]])
def throwError():
try:
raise NameError("I don't like your name")
except NameError as e:
post_mortem("", e, sys.exc_info()[2])
def callThrowError():
throwError()
callThrowError()
Выход
File "/home/thefourtheye/Desktop/Test.py", line 15, in <module>
callThrowError()
File "/home/thefourtheye/Desktop/Test.py", line 13, in callThrowError
throwError()
File "/home/thefourtheye/Desktop/Test.py", line 8, in throwError
raise NameError("I don't like your name")