Ре-рейз исключение Python и сохранить трассировку стека
Я пытаюсь поймать исключение в потоке и вновь поднять его в главном потоке:
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. Как я должен изменить main поток так, что выход читает:
Traceback (most recent call last):
File "test.py", line 7, in <module>
3 ответа:
в Python 2 вам нужно использовать все три аргумента, чтобы поднять:
raise failingThread.exc_info[0], failingThread.exc_info[1], failingThread.exc_info[2]
передача объекта трассировки в качестве третьего аргумента сохраняет стек.
С
help('raise')
:если третий объект присутствует и не
None
, это должно быть трассировка объект (см. раздел стандартная иерархия типа), и это заменено вместо текущего местоположения как место, где произошло исключение. Если третий объект настоящее и не объект трассировки илиNone
, aTypeError
исключение. Этот три-форма выраженияraise
полезно повторно вызвать исключение прозрачно в предложении except, ноraise
без выражений следует предпочесть, если исключение, которое должно быть повторно поднято, было наиболее недавно активное исключение в текущей области.в данном конкретном случае вы не можете использовать версию без выражения.
для Python 3 (согласно комментарии):
raise failingThread.exc_info[1].with_traceback(failingThread.exc_info[2])
или вы можете просто цепочку исключений с помощью
raise ... from ...
но это вызывает цепное исключение с исходным контекстом, прикрепленным в причина атрибут и это может быть или не быть то, что вы хотите.
этот фрагмент кода работает как в Python 2 & 3:
1 try: ----> 2 raise KeyError('Default key error message') 3 except KeyError as e: 4 e.args = ('Custom message when get re-raised',) #The comma is not a typo, it's there to indicate that we're replacing the tuple that e.args pointing to with another tuple that contain the custom message. 5 raise