Подтвердить что ты не робот

Исключительная трассировка скрыта, если не восстать сразу

У меня есть код, похожий на этот:

import sys

def func1():
    func2()

def func2():
    raise Exception('test error')

def main():
    err = None

    try:
        func1()
    except:
        err = sys.exc_info()[1]
        pass

    # some extra processing, involving checking err details (if err is not None)

    # need to re-raise err so caller can do its own handling
    if err:
        raise err

if __name__ == '__main__':
    main()

Когда func2 вызывает исключение, я получаю следующую трассировку:

Traceback (most recent call last):
  File "err_test.py", line 25, in <module>
    main()
  File "err_test.py", line 22, in main
    raise err
Exception: test error

Отсюда я не вижу, откуда исходит исключение. Исходная трассировка потеряна.

Как сохранить исходную трассировку и повторно поднять ее? Я хочу увидеть что-то похожее на это:

Traceback (most recent call last):
  File "err_test.py", line 26, in <module>
    main()
  File "err_test.py", line 13, in main
    func1()
  File "err_test.py", line 4, in func1
    func2()
  File "err_test.py", line 7, in func2
    raise Exception('test error')
Exception: test error
4b9b3361

Ответ 1

Пустое raise вызывает последнее исключение.

# need to re-raise err so caller can do its own handling
if err:
    raise

Если вы используете raise something, Python не может знать, было ли исключение something только что пойманным раньше или новое исключение с новой трассировкой стека. Вот почему существует пустой raise, который сохраняет трассировку стека.

Ссылка здесь

Ответ 2

Можно изменить и реконструировать исключение:

Если выражения не присутствуют, raise повторно вызывает последнее исключение, был активен в текущем объеме. Если в текущая область действия, исключение a TypeError, указывающее, что это ошибка (если выполняется под IDLE, возникает исключение Queue.Emptyвместо этого).

В противном случае raise оценивает выражения для получения трех объектов, используя None как значение пропущенных выражений. Первые два объекта используется для определения типа и значения исключения.

Если присутствует третий объект, а не None, он должен быть трассировкой объект (см. раздел Иерархия стандартного типа), и это вместо текущего местоположения вместо того, чтобы исключение. Если третий объект присутствует, а не трассировка объект или None, возникает исключение TypeError.

Трехмерное выражение форма raise полезна для прозрачного воссоздания исключения в except, но raise без выражений должно быть предпочтительным, если исключение, которое должно быть повторно поднято, было последним активным исключением в текущей области.

Итак, если вы хотите изменить исключение и изменить его, вы можете сделать это:

try:
    buggy_code_which_throws_exception()
except Exception as e:
    raise Exception, "The code is buggy: %s" % e, sys.exc_info()[2]

Ответ 3

Вы можете получить много информации об исключении через sys.exc_info() вместе с traceback module

попробуйте следующее расширение для вашего кода.

import sys
import traceback

def func1():
    func2()

def func2():
    raise Exception('test error')

def main():

    try:
        func1()
    except:
        exc_type, exc_value, exc_traceback = sys.exc_info()
        # Do your verification using exc_value and exc_traceback

        print "*** print_exception:"
        traceback.print_exception(exc_type, exc_value, exc_traceback,
                                  limit=3, file=sys.stdout)

if __name__ == '__main__':
    main()

Это будет печатать, аналогично тому, что вы хотели.

*** print_exception:
Traceback (most recent call last):
  File "err_test.py", line 14, in main
    func1()
  File "err_test.py", line 5, in func1
    func2()
  File "err_test.py", line 8, in func2
    raise Exception('test error')
Exception: test error

Ответ 4

Ваша основная функция должна выглядеть так:

def main():
    try:
        func1()
    except Exception, err:
        # error processing
        raise

Это стандартный способ обработки (и повторного поднятия) ошибок. Вот демонстрация кодекса.

Ответ 5

В то время как @Jochen ответ хорошо работает в простом случае, он не способен обрабатывать более сложные случаи, когда вы не пытаетесь перехватывать и реконструировать, но почему-то дано исключение как объект и хотите повторно бросить в совершенно новом контексте (т.е. если вам нужно обработать его в другом процессе).

В этом случае я предлагаю следующее:

  • получить исходный exc_info
  • форматировать исходное сообщение об ошибке со стеком трассировки
  • введите новое исключение с полным сообщением об ошибке (включая трассировку стека).

Прежде чем вы это сделаете, определите новый тип исключения, который позже будет удален...

class ChildTaskException(Exception):
    pass

В коде нарушения...

import sys
import traceback

try:
    # do something dangerous
except:
    error_type, error, tb = sys.exc_info()
    error_lines = traceback.format_exception(error_type, error, tb)
    error_msg = ''.join(error_lines)
    # for example, if you are doing multiprocessing, you might want to send this to another process via a pipe
    connection.send(error_msg)

Rethrow...

# again, a multiprocessing example of receiving that message through a pipe
error_msg = pcon.recv()
raise ChildTaskException(error_msg)