DEBUG.WIKI
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
雨云;给项目一台云服务器
晚安云;香港 CN2 ¥15/月

Tenacity before_sleep_log 报 LoggerProtocol 类型不兼容

标准 logging.Logger 传给 before_sleep_log 后被 mypy 拒绝,需核对 Protocol 的 log 签名。正式9.2.1已缩小关键字要求;旧版只对确认的 arg-type 做局部处理。

升级 Tenacity 后,logging.getLogger() 得到的标准 Logger 传给 before_sleep_log(),mypy 却报:

Argument 1 to "before_sleep_log" has incompatible type "Logger"; expected "LoggerProtocol" [arg-type]

9.1.3 起的类型检查出现过这条错误,9.1.4 也有相同报告。先看完整诊断是否指出 Logger.log 与 LoggerProtocol.log 的签名冲突;这不等于实际记录日志时抛出了异常。

检查的是调用契约,不是有没有 log 方法

下面是独立的类型检查示例,没有发起重试,也不主动输出日志:

import logging
from tenacity.before_sleep import before_sleep_log

logger = logging.getLogger(__name__)
before_sleep = before_sleep_log(logger, logging.INFO)

Tenacity 9.1.4 的 Protocol 声明允许任意关键字参数 **kwargs。标准 Logger 虽然支持 exc_info、extra 等特定关键字,却不承诺接受任意名称的关键字参数。结构化类型检查会比较整份方法契约,不能因为两者都叫 log 就认定兼容。

before_sleep_log 在实际回调中按位置传入日志级别与已经构造好的消息。改日志的 Formatter、handler 或输出级别,不能消除这里的静态签名冲突。

当前正式接口已缩小 Protocol 的要求

正式 Tenacity 9.2.1 的 LoggerProtocol.log 已不再要求 **kwargs,其签名是 log(self, level: int, msg: str, *args: Any)。 该版本要求 Python 3.10 及以上,旧 Python 项目应先核对运行环境。可以在项目的 Python 与依赖支持范围内采用这份正式接口,再用原项目的 mypy 配置检查同一调用点。不要只更新编辑器插件,而让 CI 继续加载旧环境中的 Tenacity。

如果暂时必须保留旧依赖,并且已确认传入的是标准 Logger、错误仅为这一项 arg-type,可以在该调用行做局部豁免:

before_sleep = before_sleep_log(logger, logging.INFO)  # type: ignore[arg-type]

该行接在上面的导入与 logger 定义之后。它只让 mypy 忽略这一行的参数类型错误,不修改运行时对象,也不保证其他自定义 Logger 满足要求。若项目使用别的检查器,应按其诊断处理,不能把 mypy 注释当通用修复。

不要给整个模块加 ignore-errors,或把所有日志对象都标成 Any。升级后检查这条局部注释是否已多余,恢复正常类型检查;同时保留原来的重试停止条件、日志级别与 handler 配置。

验收分开看两件事:静态检查是否接受实际安装版本下的调用;在允许的开发环境触发已有重试流程后,日志是否仍按原配置出现。类型检查通过不会自动配置日志输出,日志有输出也不能证明静态契约已经正确。