核心应用自定义timeout异常调用模块时无法被捕获问题咨询
问题说明
核心应用自定义了timeout异常,预期捕获到该异常时输出提示并调用sys.exit()终止应用,异常定义如下:
class timeout(Exception): def __init__(self, msg): super().__init__(msg)
原有异常捕获逻辑:
try: s3Client.put_object(Bucket = bucket, Key = key, Body = body) except timeout as error: print(error) sys.exit()
- 异常触发方式:
timeout异常由signal模块的Alarm信号触发,示例中调用的是AWS boto3模块的上传接口,可替换为任意第三方模块接口。 - 预期逻辑:
timeout异常抛出后被对应except分支捕获,打印错误后终止应用。 - 实际问题:第三方库未透传
timeout异常,会将其封装为HTTPClientError('An HTTP Client raised an unhandled exception: Integration Timeout')抛出,原有逻辑未捕获该封装后的异常,导致超时处理逻辑完全失效。 - 排除方案:修改第三方模块源码添加异常处理的方案侵入性过强,属于hacky实现,不适用。
- 目标:在核心应用层面实现
timeout异常处理,即使第三方库抛出封装后的其他类型异常,也能正常触发超时逻辑。
实现方案
Python 3的异常机制默认会通过__cause__(raise ... from ...显式关联的异常)和__context__(异常处理过程中抛出的上下文异常)两个属性保留完整异常链,直接遍历异常链判断根因是否为自定义timeout异常即可,完全不需要修改第三方源码。
步骤1:实现异常链检测工具函数
def extract_root_timeout(exc): """遍历异常链,返回最底层的自定义timeout异常实例,不存在则返回None""" visited = set() current = exc while current is not None and id(current) not in visited: visited.add(id(current)) if isinstance(current, timeout): return current current = current.__cause__ or current.__context__ return None
加visited集合是为了避免异常链出现循环引用导致死循环,属于边界情况防护。
步骤2:修改异常捕获逻辑
在原有捕获逻辑基础上增加通用异常捕获,检测到异常链中存在timeout根因时直接走超时处理流程:
import sys try: s3Client.put_object(Bucket = bucket, Key = key, Body = body) except timeout as error: # 异常未被封装时直接走原有逻辑 print(error) sys.exit() except Exception as e: root_timeout_err = extract_root_timeout(e) if root_timeout_err: # 异常被第三方库封装时,提取原始超时错误处理 print(root_timeout_err) sys.exit() # 非超时异常正常抛出,走原有其他错误处理逻辑 raise
该方案兼容所有遵循Python标准异常实现的第三方库,无侵入性,不需要对第三方代码做任何修改。
内容的提问来源于stack exchange,提问作者Peter Jordanson
相关产品推荐
相关产品推荐

