Предложение from
оператора raise
делает именно это. Он оборачивает исходное исключение в атрибут __cause__
исключения обтекания. Это называется цепочка исключений и является приложением шаблона проектирования декоратора . Тогда параметр exc_info
функций регистрации позволяет зарегистрировать исходное исключение:
import logging
try:
try:
raise IndexError("foo")
except IndexError as e:
raise KeyError from e
except KeyError as e:
logging.error("An exception occurred:", exc_info=e.__cause__)
Вариант использования
Этот механизм цепочки исключений может пригодиться в некоторых ситуациях. Например, когда пользователь должен реализовать абстрактный метод интерфейса, используя другие вспомогательные методы интерфейса, которые имеют реализации по умолчанию. Если эти вспомогательные методы могут вызывать исключения, вполне вероятно, что некоторые из этих исключений перекрываются (относятся к одному типу). Таким образом, вызывающий объект абстрактного метода не сможет определить, какой вспомогательный метод вызвал исключение. Обтекание исходных исключений в экземплярах отдельных классов исключений может решить эту проблему.
В этом примере кода у нас есть интерфейс BaseServer
для обработки запросов и пользовательский класс Server
, реализующий абстрактный метод _handle
с помощью вспомогательных методов _parse
, _format
и _send
. Мы используем механизм цепочки исключений для регистрации различных сообщений в методе _callback
(который делегирует методу _handle
) в соответствии с точкой отказа в реализации метода _handle
: разбор запроса, обработка запроса, форматирование ответа или ответ отправка.
Код Девелоппера:
import abc
import logging
class BaseServer(abc.ABCMeta):
def serve(self):
pass # serving code (should register the _callback method)
def _callback(self, request):
try:
logging.info("Received request: %s", request)
self._handle(request)
except ParsingError as e:
logging.error("Parsing failed:", exc_info=e.__cause__)
except FormattingError as e:
logging.error("Formatting failed:", exc_info=e.__cause__)
except SendingError as e:
logging.error("Sending failed:", exc_info=e.__cause__)
except Exception:
logging.exception("Processing failed:")
@abc.abstractmethod
def _handle(self, request):
raise NotImplementedError
@classmethod
def _parse(cls, request):
try:
pass # parsing code
except Exception as e:
raise ParsingError from e
@classmethod
def _format(cls, response):
try:
pass # formatting code
except Exception as e:
raise FormattingError from e
@classmethod
def _send(cls, response):
try:
pass # sending code
except Exception as e:
raise SendingError from e
class ParsingError(Exception):
pass
class FormattingError(Exception):
pass
class SendingError(Exception):
pass
Код пользователя:
class Server(BaseServer):
def _handle(self, request):
request = self._parse(request)
response = request # processing code
response = self._format(response)
self._send(response)