pytest默认不校验类型注解,需手动集成typeguard并调用install()启用运行时检查,仅@typechecked装饰的函数生效,不支持pyproject.toml配置或PEP 695高级语法。

pytest 默认完全忽略类型注解,必须显式集成 typeguard
写再多 def process(x: int) -> str:,pytest 本身也不会做任何运行时类型校验。它只执行测试逻辑,不解析 __annotations__。想让类型注解在测试中“活起来”,唯一可靠路径是搭配 typeguard,且必须手动启用 —— 没有自动模式,也没有全局开关。
@typechecked 装饰器是核心控制点
typeguard 不拦截所有函数调用,只对加了 @typechecked 的目标生效。这是设计使然,避免误伤第三方库或性能敏感路径。
- 对单个函数装饰最安全:
from typeguard import typechecked @typechecked def parse_user_id(raw: str) -> int: return int(raw) - 对整个模块启用(适合 DTO 层):
from typeguard import typechecked import sys typechecked(sys.modules[__name__])
- 不要在测试函数上直接加
@typechecked—— 它会校验测试函数的参数(比如def test_parse_user_id(parse_user_id)),而非被测函数内部逻辑
pyproject.toml 中 typeguard 的最小必要配置
仅靠装饰器还不够,得让 typeguard 在 pytest 启动时就准备好运行时钩子。关键不是改 pytest 配置,而是确保 typeguard 的导入钩子已安装。
- 在项目根目录
conftest.py开头添加:from typeguard.importhook import install install()
- 如果使用
TYPECHECK=1环境变量方式(不推荐),需额外加typeguard.importhook.install(),否则钩子不生效 - 避免在
pyproject.toml里配[tool.typeguard]—— 当前版本无此段落支持,纯属无效配置
容易被忽略的兼容性断点:Python 3.12 + typeguard + mypy 的协同边界
typeguard 只校验运行时值是否满足注解声明的类型契约,它不理解 Required、NotRequired 或 class[T] 这类 PEP 695 语法。这些高级泛型需靠 mypy/pyright 静态检查,typeguard 会直接跳过或报 TypeError: unsupported operand type。
立即学习“Python免费学习笔记(深入)”;
- 若用了
TypedDict带Required,typeguard 不校验字段是否存在,只校验传入的字典值类型是否匹配键声明 -
type Vector = list[float]这种新type语句,typeguard 能识别为list,但不会展开别名做深层校验(如检查每个元素是否真为float) - 真正需要运行时强校验的场景(如 API 入口),建议用
pydantic v2替代 —— 它原生支持 3.12 语法,且校验粒度更细
typeguard,并在 conftest.py 里调一次 install();其余全靠 @typechecked 显式标记。别指望配置文件自动接管,也别期待它能覆盖静态检查器的全部能力。


















