Трассировка исключений скрыта, если она не была немедленно восстановлена
у меня есть кусок кода, похожий на этот:
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
5 ответов:
пустой
raise
вызывает последнее исключение.# need to re-raise err so caller can do its own handling if err: raise
если вы используете
raise something
Python не может знать, еслиsomething
было только что пойманное исключение или новое исключение с новой трассировкой стека. Вот почему есть пробелraise
это сохраняет трассировку стека.
можно изменить и повторно исключение:
если нет выражений,
raise
повторно вызывает последнее исключение, которое был активен в текущей области. Если никакое исключение не активно в текущей областиTypeError
исключение, указывающее, что это ошибка (если работает в режиме ожидания, aQueue.Empty
исключение вместо.)иначе
raise
вычисляет выражения для получения трех объектов, с помощьюNone
как значение пропущенных выражений. Первые два объекта используется для определения типа и значения исключения.если третий объект присутствует и не
None
, это должно быть трассировка объект (см. раздел стандартная иерархия типов), и это заменено вместо текущего местоположения как место, где произошло исключение. Если третий объект присутствует, а не вывод объект илиNone
, aTypeError
исключение.три выражения форма
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]
вы можете получить много информации об исключении через
sys.exc_info()
вместе с traceback модульпопробуйте следующее расширение для вашего кода.
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
хотя ответ @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)
ваша главная функция должна выглядеть так:
def main(): try: func1() except Exception, err: # error processing raise
Это стандартный способ обработки (и ре-рейз) ошибок. вот демонстрация codepad.