首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

pytest的内置插件盘点19. assertion

本文系《pytest源码剖析》系列内容

19. assertion

插件路径:_pytest.assertion

实现的 hook

调用的 hook

pytest_assertion_pass

pytest_assertrepr_compare

pytest_assertion_pass.get_hookimpls

定义的 fixture

插件功能

创建命令行参数--assert, 指定是否重写断言

创建 ini 配置enable_assertion_pass_hook,指定是否启用该钩子

创建便捷函数register_assert_rewrite,对指定模块进行断言重写

创建便捷函数install_importhook, 安装导入钩子

代码片段

略(太多了,而且很跳)

简评

在函数 install_importhook 中看到一个特殊的操作:

sys.meta_path.insert(0, hook)

头一次见陌生得很,chatGPT 之:

sys.meta_path 是一个包含导入钩子(import hooks)的列表。导入钩子是一组对象,它们定义了如何在导入时查找和加载模块。它们可以用于自定义模块导入的行为。导入时,系统会按顺序检查 sys.meta_path 中的每个导入钩子,以确定模块的位置和加载方式。

简单来说,可以自定义 import 的行为

...

 pyconfig插件

在加载第三方插件之前,会尝试解析命令行参数----assert

根据参数,决定是否调用函数 install_importhook

如果调用成功,则利用函数返回值(导入钩子)对所有 pytest 第三方插件进行断言重写

...

函数 install_importhook 做了什么?

首先,根据 config 实例化AssertionRewritingHook

hook = rewrite.AssertionRewritingHook(config)

然后,将 hook 插入到sys.meta_path, 实现对 import 的自定义

sys.meta_path.insert(0, hook)

最后,并将其保存在 config.stash 和 pluginmanager.rewrite_hook

...

类 AssertionRewritingHook 做了什么,为什么能够进行断言重写?

这个类单独定义了一个文件,1k + 行,看得头昏脑涨

简单来说:

通过导入钩子的机制,拦截了 import 语句,

通过 ast 分析被导入模块的代码内容,

将代码中assert关键字重写为多个语句:

得到断言条件的结果:top_condition

得到断言条件的取反结果:negation

将变量名替换为具体的变量值

如果成功且enable_assertion_pass_hook为真,则调用改 hook

如果失败拼接各项内容,生成详细的断言提示

同时重写了其他关键字:

Name:用于表示标识符(变量名、函数名等)

BoolOp:用于表示布尔操作,如 and、or。

UnaryOp:用于表示一元操作符,如 not、-(负号)

BinOp:用于表示二元操作符,如 +、-、*、/ 等

Call:用于表示函数调用。

Starred:用于表示 * 运算符

Attribute:用于表示对象属性的访问

Compare:用于表示比较操作,如<或==

将修改结果保存为 pyc 文件,供 python 执行

...

插件中的几个 pytest 钩子,反而没那么复杂

pytest_collection:把 session 保存到导入钩子中

pytest_sessionfinish:把 session 从导入钩子中移除

pytest_runtest_protocol:

调用pytest_assertrepr_compare

判断 ini 配置 ,调用pytest_assertion_pass

pytest_assertrepr_compare:调用util.assertrepr_compare

...

总的来说,这个插件做了一件事情:

将 python 代码中assert用另外一种方式执行,称之为重写

这是改变 python 底层的复杂操作,所以它使用了一些复杂的技术:

sys.meta_path

ast

同时,为了断言是可配置式的,做了大量的工作进行判断、兼容

最后,为了断言细节是可自定义的,提供了 2 个 hook:

pytest_assertrepr_compare

pytest_assertion_pass

...

由于插件本身太过硬核,写的可能不是很清晰

如果有机会,我录一个视频,再详细的梳理一下

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OjvXzD3DIytmiO2HOiN1SvHQ0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券