news 2026/9/25 2:22:22

tenacity Python 重试库完整 API 参考:retry 装饰器、Retrying 控制器与六大策略模块实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
tenacity Python 重试库完整 API 参考:retry 装饰器、Retrying 控制器与六大策略模块实战指南
  • 后端
  • 开发工具

【免费下载链接】tenacity

Retrying library for Python

项目地址:https://gitcode.com/gh_mirrors/te/tenacity
点击查看免费下载

tenacity 是一个为 Python 提供通用重试能力的开源库,其核心价值在于把"何时重试(retry)"、"何时停止(stop)"、"等待多久(wait)"、"重试前后做什么(before/after/before_sleep)"以及"如何睡眠(sleep/nap)"全部解耦为独立可组合的策略对象,并通过@retry装饰器与Retrying/AsyncRetrying/TornadoRetrying控制器统一驱动。本文以仓库文档 doc/source/api.rst 定义的 API 结构为骨架,逐一讲解主 API 与六大策略模块的每个公开符号、参数默认值与底层实现,读者可以据此掌握 tenacity 的全部公开接口,并直接写出可运行的同步、asyncio 与 Tornado 重试代码。

一、API 文档结构总览:一张 API 地图

doc/source/api.rst 把十点重试库的公开接口划分为 8 个区域,每个区域对应一个真实的 Python 模块:

文档区域对应模块提供的策略类型
Retry Main APItenacity/init.pyretry装饰器、Retrying、AsyncRetrying、TornadoRetrying、RetryCallState
After Functionstenacity/after.pyafter关键字参数用
Before Functionstenacity/before.pybefore关键字参数用
Before Sleep Functionstenacity/before_sleep.pybefore_sleep关键字参数用
Nap Functionstenacity/nap.pysleep关键字参数用
Retry Functionstenacity/retry.pyretry关键字参数用
Stop Functionstenacity/stop.pystop关键字参数用
Wait Functionstenacity/wait.pywait关键字参数用

所有内置策略都已在 tenacity/init.py 中集中导入,因此实践中可以直接从tenacity顶层命名空间导入,无需深入子模块。此外 tenacity/_utils.py 定义了time_unit_type = int | float | timedelta与to_seconds()转换函数——所有接受时间类参数的位置(wait 的时长、stop 的延迟上限等)都同时支持秒数(int/float)与datetime.timedelta对象,这是全库统一的约定。

二、Retry Main API:重试主入口

1.tenacity.retry:装饰器入口

retry是文档中最先出现的函数,也是最常用的入口。它同时支持@retry与@retry(...)两种写法,见 tenacity/init.py 的实现:

@retry def foo(): ... # 无参数写法 @retry(stop=stop_after_attempt(7)) def bar(): ... # 带参数写法

其内部会按被装饰函数类型自动分派到三种控制器(tenacity/init.py):

  • 若函数是协程函数(或sleep是协程函数)→ 使用AsyncRetrying;
  • 若函数是 Tornado 协程函数 → 使用TornadoRetrying;
  • 其余普通函数 → 使用Retrying。

retry的所有关键字参数最终都原样传递给对应控制器构造函数,参数表见下文Retrying。

2.tenacity.Retrying:同步重试控制器

Retrying是同步重试的核心控制器,继承自抽象基类BaseRetrying。完整构造参数及默认值见 tenacity/init.py:

参数默认值说明
sleeptenacity.nap.sleep(即time.sleep)每次重试间隔执行的睡眠函数
stopstop_never停止策略,决定何时放弃重试
waitwait_none()等待策略,决定重试前睡多久
retryretry_if_exception_type()重试条件,决定某次结果是否需要重试
beforebefore_nothing每次尝试前执行的回调
afterafter_nothing每次尝试结束后执行的回调
before_sleepNone入睡前执行的回调(如日志)
reraiseFalse放弃重试时是否直接抛出最后一次异常(否则抛RetryError)
retry_error_clsRetryError重试耗尽后抛出的异常类
retry_error_callbackNone重试耗尽时执行的回调,返回其值代替抛异常
nameNone重试对象名称(用于日志与 repr)
enabledTrue设为False时跳过所有重试逻辑直接调用原函数

控制器通过__call__驱动主循环(tenacity/init.py):每次迭代要么执行DoAttempt(调用目标函数并记录结果/异常),要么执行DoSleep(睡眠后进入下一次尝试),直到返回最终结果或抛RetryError。两种典型用法:

from tenacity import Retrying, stop_after_attempt # 方式一:直接调用 result = Retrying(stop=stop_after_attempt(3), reraise=True)(fn, arg1, kw=2) # 方式二:作为迭代器/上下文管理器(单次尝试粒度) for attempt in Retrying(stop=stop_after_attempt(3)): with attempt: result = fn(arg1)

BaseRetrying还提供两个高频辅助能力:

  • wraps(f):把一个函数包装为带重试能力的装饰器,包装结果会附带retry、retry_with、statistics属性(tenacity/init.py);
  • copy(...):在保留其余配置不变的前提下生成参数修改后的新控制器,retry_with即基于它实现(tenacity/init.py)。

3.tenacity.AsyncRetrying:asyncio 重试控制器

AsyncRetrying是Retrying的异步版本(tenacity/asyncio/init.py),参数签名与同步版完全一致,但默认sleep是_portable_async_sleep——它会检测当前运行的是 trio 还是 asyncio 事件循环,并调用trio.sleep或asyncio.sleep(tenacity/asyncio/init.py)。它同样支持异步上下文管理器(__aiter__/__anext__)与直接await调用两种形态:

from tenacity import AsyncRetrying, stop_after_attempt, wait_fixed async def main(): # 直接 await 调用 result = await AsyncRetrying(stop=stop_after_attempt(3), wait=wait_fixed(1))(fn) # 异步迭代器形态 async for attempt in AsyncRetrying(stop=stop_after_attempt(3)): with attempt: result = await fn()

注意:AsyncRetrying.__iter__会抛出TypeError("AsyncRetrying object is not iterable"),防止误用同步迭代(tenacity/asyncio/init.py)。异步侧还提供了独立的异步重试策略模块 tenacity/asyncio/retry.py(retry_if_exception、retry_if_result、retry_any、retry_all,其谓词可以是awaitable)。

4.tenacity.tornadoweb.TornadoRetrying:Tornado 重试控制器

TornadoRetrying面向基于tornado.gen的协程(tenacity/tornadoweb.py),默认sleep为tornado.gen.sleep,其__call__以@gen.coroutine实现,通过yield等待目标函数与睡眠。仅在使用 Tornado 的代码中需要,tenacity在导入时会检测 tornado 是否可用(tenacity/init.py)。

5.tenacity.RetryCallState:单次调用的状态载体

RetryCallState是贯穿所有策略的对象——每个retry/stop/wait/before/after/before_sleep回调都会收到它,因此掌握其字段是编写自定义策略的前提。完整字段见 tenacity/init.py:

字段/方法类型含义
start_timefloat重试开始时间戳(time.monotonic())
retry_objectBaseRetrying当前重试管理器
fn/args/kwargs—被重试的函数及其参数
attempt_numberint当前尝试次数(从 1 开始)
outcomeFuture | None最近一次结果或异常(Future.failed判断是否异常)
outcome_timestampfloat最近一次结果的时间戳
idle_forfloat累计睡眠时间
next_action/upcoming_sleep—下一步动作与即将执行的睡眠时长
get_fn_name()str被重试函数的全限定名(用于日志)
seconds_since_startfloat距首次尝试经过的秒数

自定义策略示例(判断耗时是否超过阈值):

from tenacity import RetryCallState def wait_if_slow(retry_state: RetryCallState) -> float: return 5.0 if retry_state.seconds_since_start and retry_state.seconds_since_start > 2 else 0.0

6.RetryError、TryAgain与运行统计

  • RetryError封装"放弃前最后一次尝试"的Future,通过retry_error_callback或reraise控制抛出方式(tenacity/init.py);
  • TryAgain是一个特殊异常:在except块中主动raise TryAgain可无条件触发下一次重试(tenacity/init.py),且RetryError.reraise()会还原其底层原因异常;
  • statistics属性返回运行期统计字典,典型键为start_time、attempt_number、idle_for、delay_since_first_attempt(由begin()初始化,见 tenacity/init.py)。统计是按线程隔离的(threading.local),多线程共享同一控制器时各自独立。

三、After Functions(after参数)

after回调在每次尝试结束后执行,返回值为空。模块 tenacity/after.py 提供两个内置实现:

  • after_nothing(retry_state):什么都不做(默认值);
  • after_log(logger, log_level, sec_format="%.3g"):以指定日志器与级别记录"本次调用耗时、这是第几次调用",sec_format控制耗时格式化。底层调用retry_state.get_fn_name()获取函数名、retry_state.seconds_since_start获取耗时(tenacity/after.py)。
import logging from tenacity import retry, stop_after_attempt, after_log logger = logging.getLogger(__name__) logging.basicConfig(level=logging.DEBUG) @retry(stop=stop_after_attempt(3), after=after_log(logger, logging.DEBUG)) def fetch(): ...

四、Before Functions(before参数)

before回调在每次尝试前执行,模块 tenacity/before.py 提供:

  • before_nothing(retry_state):什么都不做(默认值);
  • before_log(logger, log_level):记录"即将开始第 N 次调用",内部同样依赖retry_state.get_fn_name()与attempt_number(tenacity/before.py)。

五、Before Sleep Functions(before_sleep参数)

before_sleep在确认需要重试、且睡眠开始之前执行,是记录"为什么重试、多久后重试"的最佳位置。模块 tenacity/before_sleep.py 提供:

  • before_sleep_nothing(retry_state):什么都不做;
  • before_sleep_log(logger, log_level, exc_info=False, sec_format="%.3g"):记录如Retrying fetch in 1.0 seconds as it raised ConnectionError: ...这样的日志;exc_info=True时额外附带完整 traceback(tenacity/before_sleep.py)。注意该回调要求outcome与next_action均已设置,即只应在重试循环内部使用。
@retry(stop=stop_after_attempt(3), before_sleep=before_sleep_log(logger, logging.WARNING, exc_info=True)) def connect(): ...

六、Nap Functions(sleep参数)

sleep决定"如何真正入睡",模块 tenacity/nap.py 提供:

  • sleep(seconds):默认策略,直接调用time.sleep(seconds)。文档注释特别说明它可以被 mock 以便单元测试(tenacity/nap.py);
  • sleep_using_event(event):返回一个等待threading.Event被 set 的睡眠函数,事件一旦被设置会提前结束等待(event.wait(timeout)),适合用于实现可中断/可取消的重试(tenacity/nap.py)。

自定义sleep也很常见,例如写入测试中让nap.sleep可被 mock 后立即返回:

from tenacity import Retrying, stop_after_attempt import tenacity.nap def fake_sleep(seconds): pass # 测试中不真正阻塞 retryer = Retrying(stop=stop_after_attempt(3), sleep=fake_sleep)

七、Retry Functions(retry参数)

retry策略决定某次尝试结果是否值得重试,其输入是RetryCallState,返回bool。模块 tenacity/retry.py 以retry_base为抽象基类(其__call__返回bool),并支持&(AND)与|(OR)运算符组合——retry_all与retry_any(tenacity/retry.py)。

策略签名与默认值行为
retry_never单例永不重试(恒 False)
retry_always单例总是重试(恒 True,需配合 stop 防止死循环)
retry_if_exception(predicate)谓词BaseException -> bool异常满足谓词则重试
retry_if_exception_type(exception_types=Exception)类型或类型元组抛出的异常是指定类型(含子类)则重试,默认捕获所有Exception
retry_if_not_exception_type(exception_types=Exception)同上抛出的异常不是指定类型则重试
retry_unless_exception_type(exception_types=Exception)同上未抛异常,或异常不是指定类型时都重试;直到抛出指定类型才停止
retry_if_exception_cause_type(exception_types=Exception)同上沿__cause__链递归检查异常原因是否为指定类型,且能识别循环链(如raise e from e)
retry_if_result(predicate)谓词result -> bool返回值满足谓词则重试
retry_if_not_result(predicate)谓词result -> bool返回值不满足谓词则重试
retry_if_exception_message(message=None, match=None)二选一异常消息等于message,或用match(字符串或已编译正则)匹配;两者都传会抛TypeError
retry_if_not_exception_message(message=None, match=None)二选一与上相反:消息不匹配时重试
retry_any(*retries)任意个任一子条件为真则重试
retry_all(*retries)任意个全部子条件为真才重试

典型用法:

from tenacity import ( retry, retry_if_exception_type, retry_if_not_exception_type, retry_if_result, retry_unless_exception_type, ) from requests import ConnectionError, Timeout # 只重试特定异常 @retry(retry=retry_if_exception_type((ConnectionError, Timeout))) def call_api(): ... # 组合:连接错误重试,且返回值为 None 也重试 @retry(retry=(retry_if_result(lambda r: r is None) | retry_if_exception_type())) def get_user(): ... # 直到出现特定异常才停止重试(常用于"等到某错误发生") @retry(retry=retry_unless_exception_type(Timeout)) def poll_until_timeout(): ...

retry_if_exception_cause_type用于处理"异常被包装"的场景(如网络库把真实原因放在__cause__里),实现见 tenacity/retry.py。

八、Stop Functions(stop参数)

stop策略决定何时整体放弃重试,输入RetryCallState,返回bool。模块 tenacity/stop.py 提供:

策略签名行为
stop_never单例永不停止(配合retry_always会无限循环,需谨慎)
stop_after_attempt(max_attempt_number)int尝试次数 ≥max_attempt_number时停止
stop_after_delay(max_delay)时间自首次尝试起耗时 ≥max_delay时停止;文档明确提示实际总延迟可能略超上限(因为会先执行完最后一次等待),需要严格控时请用stop_before_delay
stop_before_delay(max_delay)时间在"当前已耗时 + 即将到来的睡眠" ≥max_delay时停止,即保证不超出上限,适合配合随机/指数等待使用
stop_when_event_set(event)threading.Event事件被 set 时停止,可实现外部取消
stop_any(*stops)/stop_all(*stops)任意个任一满足 / 全部满足才停止

stop_base同样支持&(stop_all)与|(stop_any)运算符(tenacity/stop.py)。所有*_delay的max_delay都接受 int/float/timedelta,内部经_utils.to_seconds归一化(tenacity/stop.py)。

from tenacity import retry, stop_after_attempt, stop_after_delay, stop_before_delay @retry(stop=stop_after_attempt(7)) def a(): ... @retry(stop=stop_after_delay(10)) # 最多重试 10 秒(可能略超) def b(): ... @retry(stop=stop_before_delay(10)) # 严格不超过 10 秒 def c(): ... @retry(stop=(stop_after_delay(10) | stop_after_attempt(5))) # 先到先停 def d(): ...

九、Wait Functions(wait参数)

wait策略决定每次重试前等待多久,输入RetryCallState,返回float。模块 tenacity/wait.py 提供:

策略签名与默认值行为
wait_none()—不等待,立即重试(默认值)
wait_fixed(wait)时间每次固定等待wait
wait_random(min=0, max=1)时间在[min, max]间均匀随机
wait_incrementing(start=0, increment=100, max=MAX_WAIT)时间每次递增:start + increment * (attempt-1),并截断到[0, max]
wait_exponential(multiplier=1, max=MAX_WAIT, exp_base=2, min=0)时间指数退避:multiplier * exp_base ** (attempt-1),夹在[min, max]内;无抖动,适合资源暂时不可用、而非多进程争抢的场景
wait_random_exponential(multiplier=1, max=MAX_WAIT, exp_base=2, min=0)时间在[min, 指数上限]区间内均匀随机,实现"Full Jitter"式退避,适合多进程争抢共享资源的场景
wait_exponential_jitter(initial=1, max=MAX_WAIT, exp_base=2, jitter=1, min=0, multiplier=1)时间max(min, min(multiplier * 2**n + uniform(0, jitter), max));initial已弃用(传它且同时传multiplier会抛ValueError),请统一使用multiplier
wait_chain(*strategies)至少一个按尝试次数顺序切换等待策略,全部用尽后沿用最后一个
wait_combine(*strategies)任意个每次等待 = 所有子策略返回值之和
wait_exception(predicate)谓词exception -> float依据异常对象动态决定等待时长,例如读取 HTTPRetry-After响应头

wait_base支持+运算符(等价于wait_combine),并且实现了__radd__,因此多个等待策略可以直接用内置sum()相加(tenacity/wait.py)。

from tenacity import ( retry, wait_fixed, wait_random, wait_exponential, wait_random_exponential, wait_chain, wait_exception, ) @retry(wait=wait_fixed(2)) def a(): ... @retry(wait=wait_random(min=1, max=2)) def b(): ... @retry(wait=wait_exponential(multiplier=1, min=4, max=10)) def c(): ... @retry(wait=wait_fixed(3) + wait_random(0, 2)) # 组合:固定 3s + 随机 0~2s def d(): ... @retry(wait=wait_random_exponential(multiplier=0.5, max=60)) def e(): ... # 前 3 次等 1s,接着 5 次等 2s,之后一直等 5s @retry(wait=wait_chain(*[wait_fixed(1) for _ in range(3)] + [wait_fixed(2) for _ in range(5)] + [wait_fixed(5) for _ in range(4)])) def f(): ... # 依据异常动态等待(示例来自 wait_exception 的 docstring) def http_error(exception): if isinstance(exception, requests.HTTPError) and exception.response.status_code == 429: return float(exception.response.headers.get("Retry-After", "1")) return 60.0 @retry(stop=stop_after_attempt(3), wait=wait_exception(http_error)) def rate_limited_call(): ...

MAX_WAIT定义为sys.maxsize / 2(tenacity/_utils.py),即等待时长默认几乎无上限。wait_exponential在计算溢出时(OverflowError)会直接返回max,不会崩溃(tenacity/wait.py)。

十、组合策略的运算符约定

三类策略分别定义了直观的运算符组合(见 tenacity/retry.py、tenacity/stop.py、tenacity/wait.py):

  • retry策略:A & B→ 全部满足才重试;A | B→ 任一满足即重试;
  • stop策略:A & B→ 全部满足才停止;A | B→ 任一满足即停止;
  • wait策略:A + B→ 每次等待两者之和;sum([wait_fixed(1), wait_random(0, 1)])也可用。

这套运算符使十点重试库的策略可以像搭积木一样组合,README 中大量示例均依赖此约定,仓库测试 tests/test_tenacity.py(同步策略与主控制器)、tests/test_asyncio.py(异步控制器与 trio/asyncio 兼容)、tests/test_tornado.py(Tornado 控制器)可进一步验证各策略的实际行为。

十一、把 API 串联起来:一个完整可运行示例

综合以上全部 API,一个兼顾异常重试、结果校验、指数退避、日志与统计的完整示例:

import logging import time from tenacity import ( retry, retry_if_exception_type, retry_if_result, stop_after_attempt, wait_exponential, before_sleep_log, after_log, ) logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) attempt = 0 @retry( stop=stop_after_attempt(5), wait=wait_exponential(multiplier=1, min=1, max=8), retry=retry_if_exception_type(ConnectionError) | retry_if_result(lambda r: r is None), before_sleep=before_sleep_log(logger, logging.WARNING), after=after_log(logger, logging.INFO), reraise=True, ) def unstable_call(): global attempt attempt += 1 if attempt < 3: raise ConnectionError("temporary failure") return None # 第三次也返回 None,触发结果重试

运行后可以看到:前两次因ConnectionError重试、第三次因返回None重试,每次入睡前打印Retrying ... in X seconds as it raised ...,每次尝试结束打印耗时日志,最终第 5 次成功返回或抛RetryError/原异常。读者可按需替换stop/wait/retry/before/after/before_sleep/sleep中的任何一个为自定义回调(只需符合RetryCallState输入约定),即可无限扩展十点重试库的行为,这正是 doc/source/api.rst 所定义 API 结构的完整价值。

  • 后端
  • 开发工具

【免费下载链接】tenacity

Retrying library for Python

项目地址:https://gitcode.com/gh_mirrors/te/tenacity
点击查看免费下载
上一篇:Spring Data Relational 项目教程
下一篇:SFSafeSymbols架构设计:可扩展符号系统的实现原理

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/25 2:16:37

助记词碰撞TRC20:从BIP39推导到USDT余额扫描

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/25 2:13:27

毫米波雷达无线感知:DETR动作识别毕设源码详解

简介&#xff1a;基于毫米波雷达的无线感知智能化算法设计&#xff0c;完整毕业设计源码与论文文件打包为zip。面向智能家居应用场景&#xff0c;方案提出将特征队列提取与任务输出需求解耦的系统架构&#xff0c;利用人体骨架关键点、锚定框等通用中间特征统一不同任务&#x…

作者头像 李华
网站建设 2026/9/25 2:12:43

2026论文AI率检测成新标尺,Paperxie中文定稿与留学申请双场景实操指南

2026年&#xff0c;论文圈最热的话题早已不是重复率那几个百分点&#xff0c;而是“AI率”——也就是一篇文本被判定为AI生成的概率。我身边越来越多学生被导师叫去谈话&#xff0c;理由不是抄袭&#xff0c;而是“论文里疑似AI的味道太重”。更麻烦的是&#xff0c;同样一篇自…

作者头像 李华