Повторно вызвать исключение с другим типом и сообщением, сохраняя существующую информацию


Я пишу модуль и хочу иметь единую иерархию исключений для исключений, которые он может поднять (например, наследование от FooError абстрактный класс для всех foo конкретные исключения модуля). Это позволяет пользователям модуля перехватывать эти конкретные исключения и обрабатывать их четко, если это необходимо. Но многие исключения, вызванные из модуля, возникают из-за какого-то другого исключения; например, сбой в какой-то задаче из-за ошибки ОС на a файл.

мне нужно "обернуть" исключение поймано таким образом, что оно имеет другой тип и сообщение, так что информация доступна дальше по иерархии распространения, независимо от того, что ловит исключение. Но я не хочу потерять существующий тип, сообщение и трассировку стека; это все полезная информация для тех, кто пытается отладить проблему. Обработчик исключений верхнего уровня не годится, так как я пытаюсь украсить исключение, прежде чем оно продвинется дальше стек распространения и обработчик верхнего уровня слишком поздно.

это частично решается путем получения моего модуля fooконкретные типы исключений из существующего типа (например,class FooPermissionError(OSError, FooError)), но это не делает его легче обернуть существующий экземпляр исключения в новый тип, ни изменить сообщение.

в Python!--13-->PEP 3134 "цепочка исключений и встроенные трассировки" обсуждает изменение, принятое в Python 3.0 для объектов исключения" цепочка", чтобы указывает, что при обработке существующего исключения возникло новое исключение.

то, что я пытаюсь сделать, связано: мне нужно, чтобы он также работал в более ранних версиях Python, и мне это нужно не для цепочки, а только для полиморфизма. Как правильно это сделать?

4 84

4 ответа:

Python 3 ввел исключения-цепочки (как описано в PEP 3134). Это позволяет при создании исключения ссылаться на существующее исключение как на "причину":

try:
    frobnicate()
except KeyError as exc:
    raise ValueError("Bad grape") from exc

пойманное исключение, таким образом, становится частью (является "причиной") нового исключения и доступно для любого кода, который ловит новое исключение.


In Python 2, похоже, этот вариант использования не имеет хорошего ответа (как описано на Иэн Bicking и Нэд Батчелдер). Лентяй.

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

import sys

def failure():
    try: 1/0
    except ZeroDivisionError, e:
        type, value, traceback = sys.exc_info()
        raise ValueError, ("You did something wrong!", type, value), traceback

конечно, это действительно не так уж полезно. Если бы это было так, нам бы не нужна была эта бодрость духа. Я бы не рекомендовал это делать.

вы можете создать свой собственный тип исключения, что выходит какое исключение вы поймали.

class NewException(CaughtException):
    def __init__(self, caught):
        self.caught = caught

try:
    ...
except CaughtException as e:
    ...
    raise NewException(e)

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

редактировать: я нашел этот анализ способы бросить свое собственное исключение и сохранить исходное исключение. Никаких красивых решений.

самое прямое решение для ваших нужд должно быть следующим:

try:
     upload(file_id)
except Exception as upload_error:
     error_msg = "Your upload failed! File: " + file_id
     raise RuntimeError(error_msg, upload_error)

таким образом, вы можете позже распечатать свое сообщение и конкретную ошибку, вызванную функцией загрузки