0

Переизбрасывание исключения в Python с сохранением трассировки стека

30

Я пытаюсь поймать исключение в потоке и повторно вызвать его в основном потоке:

import threading
import sys

class FailingThread(threading.Thread):
    def run(self):
        try:
            raise ValueError('x')
        except ValueError:
            self.exc_info = sys.exc_info()

failingThread = FailingThread()
failingThread.start()
failingThread.join()

print(failingThread.exc_info)
raise failingThread.exc_info[1]

В общем, этот код работает и выводит следующий результат:

(<type 'exceptions.ValueError'>, ValueError('x',), <traceback object at 0x1004cc320>)
Traceback (most recent call last):
  File "test.py", line 16, in <module>
    raise failingThread.exc_info[1]

Однако, источником исключения указывается строка 16, где произошло повторное возбуждение. Оригинальное исключение исходит из строки 7. Как мне нужно изменить основной поток, чтобы вывод выглядел следующим образом:

Traceback (most recent call last):
  File "test.py", line 7, in <module>

2 ответ(ов)

0

В Python 2 для поднятия исключения с использованием всех трех аргументов вы можете использовать следующий синтаксис:

raise failingThread.exc_info[0], failingThread.exc_info[1], failingThread.exc_info[2]

При передаче объекта трассировки в качестве третьего аргумента сохраняется стек вызовов.

Согласно документации help('raise'):

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

В данном случае вы не можете использовать вариант без выражений.

Для Python 3 (согласно комментариям):

raise failingThread.exc_info[1].with_traceback(failingThread.exc_info[2])

Либо вы можете просто связать исключения, используя raise ... from ..., но это вызовет цепочку исключений с оригинальным контекстом, прикрепленным в атрибуте cause, и это может быть не тем, что вам нужно.

0

Этот фрагмент кода работает как в Python 2, так и в Python 3:

      1 try:
----> 2     raise KeyError('Сообщение об ошибке по умолчанию')
      3 except KeyError as e:
      4     e.args = ('Пользовательское сообщение при повторном поднятии',) # Запятая не является опечаткой, она используется для указания на то, что мы заменяем кортеж, на который указывает e.args, на другой кортеж с пользовательским сообщением.
      5     raise

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

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь