Обернуть исключение в другое и записать оригинал - PullRequest
0 голосов
/ 20 января 2019

Как обернуть исключение в другое исключение и записать в журнал трассировку исходного исключения в Python?

1 Ответ

0 голосов
/ 20 января 2019

Предложение 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)
...