【问题标题】:Wrapping an exception in another one and log the original one将异常包装在另一个中并记录原始异常
【发布时间】:2019-06-14 05:44:21
【问题描述】:

如何将一个异常包装在另一个异常中并在 Python 中记录原始异常的回溯?

【问题讨论】:

  • 这个问题不清楚。 “将一个异常包装在另一个异常中”是什么意思?
  • @JordanSinger 异常是一个类,所以它意味着将一个异常实例附加到另一个异常实例的属性上。
  • @JordanSinger 这是处理 f.e. 异常时的常见概念。将 ValueError 包装在“特定的” IntParsingError 中,然后将其重新抛出,包括对用户的原始异常。很好的自我回答

标签: python exception logging wrapper


【解决方案1】:

raise 语句的 from 子句正是这样做的。它将原始异常包装在包装异常的__cause__ 属性中。这称为exception chaining,它是decorator design pattern 的一个应用程序。然后记录函数的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 接口和一个使用_parse_format_send 辅助方法实现_handle 抽象方法的用户Server 类。我们使用异常链机制在_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)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-10-26
    • 2017-02-04
    • 1970-01-01
    • 2019-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多