捕获Python中的嵌套异常

捕获Python中的嵌套异常,python,error-handling,nested,Python,Error Handling,Nested,我有一组Python脚本,它们以嵌套方式调用函数。对于这些函数中的每一个,我都有一个try,except语句来捕获每个异常并打印它们。我想发送一封电子邮件警报,其中包含执行过程中遇到的完整异常序列。例如: import sys def SendAlert(ErrorMessage): try: #send email alert with error message #[...] except: print(str(sys.exc

我有一组Python脚本,它们以嵌套方式调用函数。对于这些函数中的每一个,我都有一个try,except语句来捕获每个异常并打印它们。我想发送一封电子邮件警报,其中包含执行过程中遇到的完整异常序列。例如:

import sys

def SendAlert(ErrorMessage):
    try:
        #send email alert with error message
        #[...]
    except:
        print(str(sys.exc_info()))
        return(sys.exc_info())

def ParentFunction():
    try:
        #call ChildFunction
        ChildResult = ChildFunction()

        #do stuff with ChildResult
        #[...]
        return ParentResult
    except:
        ErrorMessage = str(sys.exc_info())
        print(ErrorMessage)
        SendAlert(ErrorMessage)

def ChildFunction():
    try:
        #do stuff
        #[...]
        return ChildResult
    except:
        print(str(sys.exc_info()))
        return(sys.exc_info())

#main
if __name__ == '__main__':
    Result = ParentFunction()
如果
ChildFunction
是嵌套最多的函数,则上述代码的行为如下:

  • ChildFunction
    遇到异常,它将打印并返回 发送到
    ParentFunction
  • ParentFunction
    将失败,因为
    ChildResult
    包含错误消息且不是有效值
  • ParentFunction
    将触发异常并在电子邮件警报中发送自己的错误消息
除了来自
ParentFunction
的错误消息外,我希望电子邮件警报包含来自
ChildFunction
的错误消息。请注意,我希望避免在
ParentFunction
的except语句中将
ChildResult
变量传递给
SendAlert
函数,因为在现实生活中,我的程序有很多嵌套函数,这意味着将每个函数的结果变量传递给except语句

你将如何做到这一点?是否有办法访问由整个程序触发的完整错误序列


谢谢

您不需要返回通过
sys.exc_info
获得的异常:我们可以重新引发它

try:
    # do stuff
# FIXME: we should avoid catching too broad exception
except Exception as err:
    # do stuff with exception
    raise err
因此,您的示例可能如下所示

def SendAlert(ErrorMessage):
    try:
        # send email alert with error message
        # [...]
        pass
    # what kind of exceptions may occur while sending email?
    except Exception as err:
        print(err)
        raise err


def ParentFunction():
    try:
        # call ChildFunction
        ChildResult = ChildFunction()

        ParentResult = ChildResult
        # do stuff with ChildResult
        # [...]
        return ParentResult
    # FIXME: we should avoid catching too broad exception
    except Exception as err:
        ErrorMessage = str(err)
        # why do we need to print again?
        print(ErrorMessage)
        SendAlert(ErrorMessage)


def ChildFunction():
    try:
        ChildResult = 0
        # do stuff
        # [...]

        # let's raise `ZeroDivisionError`

        ChildResult /= 0

        return ChildResult
    # FIXME: we should avoid catching too broad exception
    except Exception as err:
        print(err)
        raise err


# main
if __name__ == '__main__':
    Result = ParentFunction()
进一步改进 对于打印完整的错误回溯,我们可以使用如下模块

import logging

logging.basicConfig(level=logging.DEBUG)

logger = logging.getLogger(__name__)


def SendAlert(ErrorMessage):
    try:
        # send email alert with error message
        # [...]
        pass
    # what kind of exceptions may occur while sending email?
    except Exception as err:
        logger.exception('Error while sending email')
        # we're not receiving values from this function
        raise err


def ParentFunction():
    try:
        # call ChildFunction
        ChildResult = ChildFunction()

        ParentResult = ChildResult
        # do stuff with ChildResult
        # [...]
        return ParentResult
    # FIXME: we should avoid catching too broad exception
    except Exception as err:
        # this will log full error traceback
        # including `ChildFunction`
        logger.exception('Error in ParentFunction')
        ErrorMessage = str(err)
        SendAlert(ErrorMessage)


def ChildFunction():
    ChildResult = 0
    # do stuff
    # [...]

    # e. g. let's raise `ZeroDivisionError`
    ChildResult /= 0

    return ChildResult


# main
if __name__ == '__main__':
    Result = ParentFunction()
这只是冰山一角,
日志记录
非常棒,你绝对应该使用它

进一步阅读
  • ,
  • 用于通过电子邮件发送错误

您还可以创建一个
自定义异常
,该异常可以接收描述性错误消息并返回它

下面是一个简单的示例,您可以修改它并将其实现到代码中,直到它满足您的需要:

class MyCustomError(Exception):
    def __init__(self, err):
        Exception.__init__(self)
        self.error = err
    def __str__(self):
        return "%r" % self.error

a = 1
try:
    if a != 0:
        raise MyCustomError("This is an Error!")
except MyCustomError as err:
    print(err)
输出:

'This is an Error!'

重新引发异常是什么意思?为什么不使用
try。。。除了异常作为错误
?谢谢Chiheb,这也是Azat在下面推荐的。我会尝试的。感谢进一步的改进,我刚刚测试了上面的脚本。有一个问题,由于脚本正在使用完整的异常序列在
ParentFunction
中重新引发并记录异常,也许我不需要在
ChildFunction
中也记录异常?否则,日志会变得非常垃圾,有很多冗余,因此可读性较差。你怎么看?@Alexis.Rolland:ofc你不应该记录两次相同的异常,所以请随意删除
ChildFunction
中的日志记录好吧,如果这听起来是个愚蠢的问题,很抱歉,但是如果我随意删除了try。。。除了ChildFunction中的子句?ParentFunction是否仍然能够进行日志记录?(现在我在手机上无法测试)我的意思是记录完整的异常序列当然…:)谢谢Chiheb,我对类有点陌生,你能详细说明一下它在这里的作用和好处吗?通过这种自定义异常(类),你可以轻松地跟踪代码中的错误,也可以在类中添加其他重复代码。例如,当出现异常时,您希望将错误记录到一个文件中,然后继续处理其余代码。或者,当出现异常时,您可能希望在执行其余代码之前调用方法/函数。没有限制。。。此外,这是一个将异常分类为组的简便技巧。等…有关更多信息,请参阅。