tryfirst/trylast 是协商式优先级:先按 priority 排序,再将 tryfirst 放最前、trylast 放最后;tryfirst 对应 priority=0(非负无穷),冲突时回退到 priority 比较,相同则按注册顺序(不稳定)。

hookimpl 的 tryfirst 和 trylast 怎么生效?
tryfirst=True 和 trylast=True 不是强制排序,而是“协商式优先级”:当多个插件注册了同名 hook,pluggy 会先按 hookimpl 的 priority 数值排序,再把 tryfirst 的放最前、trylast 的放最后——但仅限于它们之间不冲突时。一旦 priority 显式设为高值(比如 100),它就可能压过 tryfirst。
-
tryfirst对应的 priority 实际是0,不是负无穷;trylast对应0,也不是正无穷 - 如果两个插件都设
tryfirst=True,那就退回到比 priority,priority 相同才按注册顺序(非稳定) - 在 hookspec 声明里加
firstresult=True后,tryfirst更关键——第一个返回非 None 的tryfirst实现会直接中断执行
@hookimpl(tryfirst=True)
def pytest_runtest_makereport(item, call):
if call.excinfo:
return ReportWrapper(call.excinfo)
为什么 hook 调用顺序和预期不符?
常见现象是:明明写了 trylast=True,结果自己的逻辑还是被别的插件覆盖或截断。根本原因通常是 hookspec 定义时没开 hookwrapper=True 或 firstresult=True,导致所有实现都串行执行,而你的逻辑在中间就被其他返回值干扰了。
- 插件加载顺序影响注册顺序,但 pluggy 不保证 import 顺序 = 注册顺序,尤其用了
setuptoolsentry points 时 -
pm.register()手动注册的插件,其 hookimpl 优先级只在本次注册中有效,不会覆盖已注册的同名 hook - 使用
pm.hook.my_hook._nonwrappers可以查当前实际参与调用的实现列表,调试时比猜更可靠
hookwrapper=True 和普通 hookimpl 有什么实质区别?
hookwrapper=True 不是“更高优先级”,而是切换了执行模型:它不直接返回值,而是用 yield 切入 hook 执行流,在前后都能拦截——比如在测试开始前改 item,或在所有普通 impl 返回后统一收口日志。
- 普通 hookimpl 必须有
return,且返回值会进入结果列表(除非 hookspec 设了firstresult=True) -
hookwrapper函数体内必须有且仅有一个yield,否则启动时报HookCallError - 多个
hookwrapper之间仍受priority控制,但它们包裹的是“整个 hook 调用过程”,不是单个返回值 - 注意:wrapper 里
yield后的代码,是在所有普通 impl 执行完之后才运行的
@hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result() # 获取普通 impl 的返回结果
if report.failed:
log_failure(report)
如何安全地覆盖第三方插件的 hook 行为?
别试图用 tryfirst=True 硬顶替——尤其当对方用了 hookwrapper 或修改了内部状态时,容易引发静默失败。真正可控的方式是:明确 hookspec 是否允许你介入、是否支持 firstresult、以及有没有公开的配置开关。
立即学习“Python免费学习笔记(深入)”;
- 先看目标 hookspec 的定义:在源码里搜
@hookspec,确认有没有firstresult、hookwrapper、historic等标记 - 如果原 hook 是
firstresult=True,你设tryfirst=True+return非 None,就能短路后续逻辑 - 如果原 hook 是
hookwrapper=True,你的 wrapper 必须小心 yield 后的副作用,避免重复执行或状态错乱 - 最稳妥的覆盖点,其实是 hookspec 允许的参数注入位置(比如
pytest_configure(config)中改config.pluginmanager),而不是硬抢 hook 顺序
pluggy 的 hook 优先级本质是“协作契约”,不是调度器。越想绕过规则强行控制,越容易掉进注册时机、yield 生命周期、结果聚合逻辑这些隐性坑里。










