08/30: Про исключения в питоне
Уважаемые товарищи python-девелоперы,
Eсли кто не знает, или не помнит, или не задумывался об этом.
Если вы хотите обернуть внутреннюю ошибку в новое исключение (“сделать reraise”), например, в таком коде:
try:
do_something()
except Exception, e:
raise e
То внутренние фреймы стека у вас забудутся, первым фреймом будет строчка с raise e
Если вы не хотите терять внутренние фреймы, пишите так:
import sys
try:
do_something()
except Exception, e:
raise Exception, "MyException", sys.exc_info()[2]
так:
import sys
try:
do_something()
except Exception, e:
raise Exception("MyException"), None, sys.exc_info()[2]
или так:
try:
do_something()
except Exception, e:
raise
Обернуть исключение как в Java через throw MyException(e) увы нельзя, т.е. внутреннее сообщение потеряется, останется только внешнее. Но вы можете запихать дополнительное сообщение в отдельное поле Exception-обёртки, и написать ваш красивый код, который покажет стек.
Comments
Меня всегда удивляло в этих Python & Ruby отсутствие таких маленьких и совершенно элементарных вещей… Для выхода из этого положения придумываются какие-то хакерские или дисциплинарные подходы. Казалось бы, ведь запихнуть одно исключение в другое — для этого же никакой особой поддержки со стороны VM не нужно. Только на уровне стандартной библиотеки во всех стандартных исключениях нужен правильный конструктор…
import sys — сожрался при форматировании кода
спасибо, пофиксал
это стандартные проблемы эволюции. языки начинались как скриптовые. поскольку разработка открытая, видимо, до сих пор никто не зарепортил проблему в багтрекер питона. и никто соответственно не пофиксал. хотя, вообще, я не знаю, как обстоят с этим дела в последних версиях питона.
А меня всегда удивляло в этих монструозных C/C++, Java и C# отсутствие таких маленьких и совершенно элементарных вещей, как статический инициализатор словаря и анонимных функций (lambda).
оказывается, пропозал написан 4 года назад, и заимплеменчено всё уже год назад в последнем питоне :)
raise без аргументов делает re-raise последнего исключения с сохранением стека. В любом другом случае создается новое исключение и логично, что у него есть свой собственный стек.
Мне кажется, что чаще всего это применяется для логгинга исключений и соотв. более близкий к реальности код с сохранением стека может выглядеть так:
нелогично то, что нет стандартного конструктора нового исключения, которому можно скормить старое исключение и получить вывод следующего вида:
Вот это познавательно:
А то что если ты хочешь бросить тоже самое исключение что и было, надо делать “raise”, а не “raise e” написано в документации.
По карйней мере у меня не разу не возникло желания наступить на такие грабли.
Из-за sys.exc_info()[2] и был написан данный пост. И из-за того, что, чтобы понять, чем отличаются raise и raise e, нужно читать документацию :)
да, кстати, наткнулся тут на PEP-3134
в питоне 3 есть raise EXCEPTION from CAUSE