timeouterror 经常捕获不到,因为 socket.timeout、requests.exceptions.timeout 等并非其子类;应按库文档捕获具体异常,或用 except oserror + errno.etimedout 判断,辅以重试装饰器或 pytest fixture 实现可控重试。

为什么 TimeoutError 经常捕获不到?
Python 标准库中很多超时机制(比如 socket.timeout、concurrent.futures.TimeoutError、requests.exceptions.Timeout)**根本不是 TimeoutError 的子类**。直接 except TimeoutError: 会漏掉绝大多数真实超时异常。
实操建议:
- 优先按具体库的文档捕获对应异常,例如
requests必须写except requests.exceptions.Timeout: - 用
except Exception as e:+ 关键字判断兜底(见下一条),但别长期依赖它 - 检查你用的库是否把超时包装成了
OSError(如某些底层 socket 操作),这时可能需要except OSError as e:并判断e.errno == errno.ETIMEDOUT
用装饰器封装重试逻辑最干净
把重试和超时捕获逻辑从测试用例里抽出来,避免每个 test_* 方法都写重复的 try/except/for。
示例(支持指定异常类型 + 最大重试次数):
from functools import wraps
import time
<p>def retry_on_timeout(max_retries=2, delay=1, timeout_exceptions=(requests.exceptions.Timeout, socket.timeout)):
def decorator(func):
@wraps(func)
def wrapper(*args, *<em>kwargs):
for i in range(max_retries + 1):
try:
return func(</em>args, **kwargs)
except timeout_exceptions as e:
if i == max_retries:
raise e
time.sleep(delay)
return None
return wrapper
return decorator</p><h1>使用</h1><p>@retry_on_timeout(max_retries=1, delay=0.5)
def test_api_response():
resp = requests.get("<a href="https://www.php.cn/link/c19fa3728a347ac2a373dbb5c44ba1c2">https://www.php.cn/link/c19fa3728a347ac2a373dbb5c44ba1c2</a>", timeout=2)
assert resp.status_code == 200
</p>
注意:max_retries=1 表示最多执行 2 次(首次 + 1 次重试),别数错。
Pytest 中用 fixture 实现更灵活的重试控制
如果项目已用 pytest,用 fixture 比装饰器更容易统一管理重试策略,还能结合 pytest.mark.parametrize 或命令行参数动态开关。
实操要点:
- 在
conftest.py里定义 fixture,用request.node.get_closest_marker("retry")读取用例级标记 - 不要在 fixture 里直接
raise,而是用pytest.skip()或返回特殊状态,让外层断言决定失败 - 记录重试次数到
request.node.user_properties,方便后续生成报告 - 避免对非网络操作(如断言失败)也重试——重试只应针对可恢复的瞬态故障
重试不是万能的:哪些情况必须停
自动重试容易掩盖真正的问题,比如服务端持续降级、测试环境配置错误、或数据状态不一致。
必须加保护机制:
- 重试前检查前置条件是否满足(例如数据库连接是否还活着、目标 URL 是否可解析)
- 限制总耗时(比如单个用例从开始到最终失败不能超过 30 秒),防止无限卡住
- 对同一用例连续失败超过 N 次(比如 3 天内每天失败),自动禁用并通知负责人——这大概率不是偶发,是真问题
- 日志里明确打出 “Retry #1/2” 和原始异常 traceback,否则排查时根本分不清是第几次失败
最麻烦的其实是重试后通过了,但实际业务状态没恢复(比如订单创建接口重试成功,却生成了两笔重复订单)。这类问题得靠幂等性设计兜底,不是靠重试能解决的。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











