You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Python嵌套异常的回溯信息中提取所有异常类型及消息(无需字符串匹配)

如何从Python嵌套异常的回溯信息中提取所有异常类型及消息(无需字符串匹配)

你说得对,靠字符串匹配处理回溯信息确实容易踩坑——要是异常格式、路径或者行号变了,正则或者字符串截取就会失效。其实Python的异常对象本身就自带链式异常的属性,我们可以直接遍历这些属性来收集所有嵌套异常,完全不用碰那些繁琐的回溯字符串!

核心思路

Python的每个异常对象都有两个关键属性:

  • __cause__:通过raise ... from ...显式关联的直接原因异常
  • __context__:没有显式指定from时,自动关联的上下文异常

我们可以写一个简单的遍历函数,从捕获到的根异常开始,顺着这两个属性层层深入,把所有异常对象都收集起来,再从中提取类型和消息。

代码实现

下面是完整的示例代码,刚好能满足你想要的结构化输出需求:

import requests

def collect_all_exceptions(exc):
    """遍历异常链,收集所有嵌套的异常对象"""
    exception_list = []
    current_exception = exc
    while current_exception is not None:
        exception_list.append(current_exception)
        # 优先处理显式的__cause__,再处理隐式的__context__
        current_exception = current_exception.__cause__ or current_exception.__context__
    return exception_list

try:
    r = requests.get('https://thisdoesntexist.test')
except Exception as e:
    # 获取所有异常对象
    all_exceptions = collect_all_exceptions(e)
    # 转换成结构化的字典列表
    structured_result = [
        {
            "type": f"{type(exc).__module__}.{type(exc).__name__}",
            "message": str(exc),
            "full_message": f"{type(exc).__module__}.{type(exc).__name__}: {str(exc)}"
        }
        for exc in all_exceptions
    ]
    # 打印结果
    for item in structured_result:
        print(item)

代码说明

  1. collect_all_exceptions函数:从传入的根异常开始,循环遍历__cause__和__context__,把每一层的异常对象都加入列表,直到没有下一层异常为止。
  2. 结构化转换:
    • type(exc).__module__获取异常所属的模块名,type(exc).__name__获取异常类名,拼起来就是完整的异常类型(比如socket.gaierror)
    • str(exc)直接获取异常的消息内容
    • full_message字段是把类型和消息拼在一起的完整字符串,和你示例里的格式一致

输出效果

运行这段代码后,你会得到类似这样的结构化结果(顺序从最外层到最内层异常):

{
    "type": "requests.exceptions.ConnectionError",
    "message": "HTTPSConnectionPool(host='thisdoesntexist.test', port=443): Max retries exceeded with url: / (Caused by NameResolutionError(\"<urllib3.connection.HTTPSConnection object at 0x0000025E6C236600>: Failed to resolve 'thisdoesntexist.test' ([Errno 11001] getaddrinfo failed)\"))",
    "full_message": "requests.exceptions.ConnectionError: HTTPSConnectionPool(host='thisdoesntexist.test', port=443): Max retries exceeded with url: / (Caused by NameResolutionError(\"<urllib3.connection.HTTPSConnection object at 0x0000025E6C236600>: Failed to resolve 'thisdoesntexist.test' ([Errno 11001] getaddrinfo failed)\"))"
}
{
    "type": "urllib3.exceptions.MaxRetryError",
    "message": "HTTPSConnectionPool(host='thisdoesntexist.test', port=443): Max retries exceeded with url: / (Caused by NameResolutionError(\"<urllib3.connection.HTTPSConnection object at 0x0000025E6C236600>: Failed to resolve 'thisdoesntexist.test' ([Errno 11001] getaddrinfo failed)\"))",
    "full_message": "urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='thisdoesntexist.test', port=443): Max retries exceeded with url: / (Caused by NameResolutionError(\"<urllib3.connection.HTTPSConnection object at 0x0000025E6C236600>: Failed to resolve 'thisdoesntexist.test' ([Errno 11001] getaddrinfo failed)\"))"
}
{
    "type": "urllib3.exceptions.NameResolutionError",
    "message": "<urllib3.connection.HTTPSConnection object at 0x0000025E6C236600>: Failed to resolve 'thisdoesntexist.test' ([Errno 11001] getaddrinfo failed)",
    "full_message": "urllib3.exceptions.NameResolutionError: <urllib3.connection.HTTPSConnection object at 0x0000025E6C236600>: Failed to resolve 'thisdoesntexist.test' ([Errno 11001] getaddrinfo failed)"
}
{
    "type": "socket.gaierror",
    "message": "[Errno 11001] getaddrinfo failed",
    "full_message": "socket.gaierror: [Errno 11001] getaddrinfo failed"
}

如果需要从最内层到最外层的顺序,只需要把all_exceptions反转一下:

all_exceptions = collect_all_exceptions(e)[::-1]

优势对比

和你之前用traceback.format_exception再解析字符串的方法比,这个方案的好处很明显:

  • 更可靠:直接操作异常对象,完全不受回溯信息格式变化的影响
  • 更灵活:除了类型和消息,还能直接使用异常对象做其他处理(比如获取堆栈帧)
  • 更简洁:代码逻辑清晰,不需要写复杂的正则或字符串处理逻辑

备注:内容来源于stack exchange,提问作者Superbman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 11:22:59