背景:

Pytest 是一个功能强大的 Python 测试框架,它使用了一个名为 "pluggy" 的插件系统来扩展其功能。在 Pytest 的源码中,pluggy 模块负责实现插件管理和扩展机制。

核心类介绍:

PluginManager 类:PluginManager 是 pluggy 模块提供的一个类,用于管理插件的加载、注册和调用。它负责协调插件之间的交互,并控制钩子函数的执行顺序。

HookspecMarker 类:HookspecMarker 是 pluggy 模块提供的一个装饰器类,用于定义钩子函数规范。通过使用 HookspecMarker 装饰器,我们可以标识出一个函数作为钩子函数规范,以便在后续的插件中进行实现。

HookimplMarker 类:HookimplMarker 是 pluggy 模块提供的一个装饰器类,用于定义钩子函数的具体实现。通过使用 HookimplMarker 装饰器,我们可以将一个函数标识为钩子函数的具体实现,并将其注册到插件管理器中。

核心逻辑代码:

pm = PluginManager("pluggy_demo_1")
pm.add_hookspecs(HookSpec)
pm.register(HookImpl1())
pm.register(HookImpl2())
print(pm.hook.calculate(a=1, b=2))

首先,我们创建一个插件管理器 pm,并指定项目名称为 "pluggy_demo_1"。然后,我们使用 add_hookspecs() 方法将钩子函数规范 HookSpec 添加到插件管理器中。这样,插件管理器就知道了我们定义的钩子函数规范。

接下来,我们注册两个插件 HookImpl1() 和 HookImpl2() 到插件管理器中,通过调用 register() 方法。这样,插件管理器就知道了需要执行这两个插件的钩子函数。

最后,我们通过 pm.hook.calculate() 调用了钩子函数 calculatepm.hook 是插件管理器提供的一个特殊属性,它允许我们访问所有已注册的钩子函数。通过调用 pm.hook.calculate(),插件管理器会触发所有已注册的 calculate 钩子函数,并将参数 a=1 和 b=2 传递给它们。

在插件中,我们可以根据具体需求实现 calculate 钩子函数的逻辑。可以有多个插件实现了该钩子函数,每个插件的实现可以根据自己的逻辑进行计算,并返回计算结果。

总结起来,这段代码中的 pm.hook.calculate 调用了已注册的 calculate 钩子函数,并触发了相应的插件逻辑。pm.hook 是插件管理器提供的特殊属性,用于访问已注册的钩子函数。通过调用该属性上的钩子函数,可以实现插件的扩展和自定义行为。

常用的 Pytest 钩子函数:

  • pytest_configure(config): 在 Pytest 运行之前,用于配置和初始化测试运行环境。
  • pytest_collection_modifyitems(config, items): 在测试收集过程中修改测试项(test items)的钩子函数。
  • pytest_runtest_protocol(item, nextitem): 在执行单个测试项之前和之后,以及在测试项之间进行干预的钩子函数。
  • pytest_pyfunc_call(pyfuncitem): 在执行测试函数之前和之后进行干预的钩子函数。
  • pytest_terminal_summary(terminalreporter): 在测试运行完成后,用于生成测试结果摘要的钩子函数。

自定义钩子函数步骤

步骤1:定义钩子函数接口(Hook specification)

  1. 创建一个 Python 模块,例如 myhooks.py
  2. 在该模块中,使用 @pytest.hookspec 装饰器来标记你的钩子函数接口。例如:
# myhooks.py
import pytest @pytest.hookspec
def my_custom_hook(arg1, arg2):
"""Documentation for the custom hook."""
pass

在这个例子中,my_custom_hook 是你的自定义钩子函数接口。你可以根据需要定义参数和返回值,以及提供相应的文档说明。

步骤2:实现钩子函数(Hook implementation)

  1. 创建另一个 Python 模块,例如 myplugin.py
  2. 在该模块中,使用 @pytest.hookimpl 装饰器来标记你的钩子函数实现。例如:
# myplugin.py
import pytest @pytest.hookimpl
def my_custom_hook(arg1, arg2):
"""Implementation of the custom hook."""
# 执行自定义的逻辑
print(f"Running custom hook with arguments: {arg1}, {arg2}")
return 42

在这个例子中,my_custom_hook 是你的自定义钩子函数实现。你可以在函数体内编写你的自定义逻辑,并返回相应的结果。

步骤3:注册自定义插件

  1. 创建一个用于注册插件的 Python 模块,例如 conftest.py。这个文件通常放置在测试目录的根目录或者 tests 目录下。
  2. 在 conftest.py 中,使用 pytest_configure 钩子函数来注册你的插件。例如:
# conftest.py
def pytest_configure(config):
config.pluginmanager.register(myplugin)

在这个例子中,myplugin 是你在 myplugin.py 中定义的插件。通过调用 register 方法,你可以将插件注册到 Pytest 的插件管理器中。

步骤4:使用自定义钩子函数

  1. 在测试代码中,你可以通过使用 pytestconfig 对象来访问自定义的钩子函数。
  2. 调用自定义钩子函数,并传入相应的参数。例如:
# test_myhooks.py
def test_with_custom_hook(pytestconfig):
hook_result = pytestconfig.hook.my_custom_hook(arg1=1, arg2=2)
# 处理钩子函数返回的结果
assert hook_result == 42

在这个例子中,我们通过 pytestconfig.hook 访问了自定义的钩子函数,并传入了参数 arg1 和 arg2。你可以根据你的钩子函数逻辑来处理返回的结果。

需要注意的是,为了让 Pytest 能够识别和加载自定义的钩子函数,确保将 myhooks.pymyplugin.py 和 conftest.py 放置在测试的搜索路径下,例如项目根目录或者 tests 目录下。

  

  

  

  

Pytest 源码解读 [1] - [pluggy] 核心设计理念浅读的更多相关文章

  1. SDWebImage源码解读之SDWebImageDownloaderOperation

    第七篇 前言 本篇文章主要讲解下载操作的相关知识,SDWebImageDownloaderOperation的主要任务是把一张图片从服务器下载到内存中.下载数据并不难,如何对下载这一系列的任务进行设计 ...

  2. SDWebImage源码解读_之SDWebImageDecoder

    第四篇 前言 首先,我们要弄明白一个问题? 为什么要对UIImage进行解码呢?难道不能直接使用吗? 其实不解码也是可以使用的,假如说我们通过imageNamed:来加载image,系统默认会在主线程 ...

  3. AFNetworking 3.0 源码解读 总结(干货)(上)

    养成记笔记的习惯,对于一个软件工程师来说,我觉得很重要.记得在知乎上看到过一个问题,说是人类最大的缺点是什么?我个人觉得记忆算是一个缺点.它就像时间一样,会自己消散. 前言 终于写完了 AFNetwo ...

  4. AFNetworking 3.0 源码解读(十一)之 UIButton/UIProgressView/UIWebView + AFNetworking

    AFNetworking的源码解读马上就结束了,这一篇应该算是倒数第二篇,下一篇会是对AFNetworking中的技术点进行总结. 前言 上一篇我们总结了 UIActivityIndicatorVie ...

  5. AFNetworking 3.0 源码解读(十)之 UIActivityIndicatorView/UIRefreshControl/UIImageView + AFNetworking

    我们应该看到过很多类似这样的例子:某个控件拥有加载网络图片的能力.但这究竟是怎么做到的呢?看完这篇文章就明白了. 前言 这篇我们会介绍 AFNetworking 中的3个UIKit中的分类.UIAct ...

  6. AFNetworking 3.0 源码解读(八)之 AFImageDownloader

    AFImageDownloader 这个类对写DownloadManager有很大的借鉴意义.在平时的开发中,当我们使用UIImageView加载一个网络上的图片时,其原理就是把图片下载下来,然后再赋 ...

  7. AFNetworking 3.0 源码解读(七)之 AFAutoPurgingImageCache

    这篇我们就要介绍AFAutoPurgingImageCache这个类了.这个类给了我们临时管理图片内存的能力. 前言 假如说我们要写一个通用的网络框架,除了必备的请求数据的方法外,必须提供一个下载器来 ...

  8. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager

    做ios开发,AFNetworking 这个网络框架肯定都非常熟悉,也许我们平时只使用了它的部分功能,而且我们对它的实现原理并不是很清楚,就好像总是有一团迷雾在眼前一样. 接下来我们就非常详细的来读一 ...

  9. AFNetworking 3.0 源码解读(三)之 AFURLRequestSerialization

    这篇就讲到了跟请求相关的类了 关于AFNetworking 3.0 源码解读 的文章篇幅都会很长,因为不仅仅要把代码进行详细的的解释,还会大概讲解和代码相关的知识点. 上半篇: URI编码的知识 关于 ...

  10. AFNetworking 3.0 源码解读(五)之 AFURLSessionManager

    本篇是AFNetworking 3.0 源码解读的第五篇了. AFNetworking 3.0 源码解读(一)之 AFNetworkReachabilityManager AFNetworking 3 ...

随机推荐

  1. 我“采访”了 ChatGPT

    我"采访"了 ChatGPT 大家好,我是准备认真码字的**老章:. 最近ChatGPT火的爆表,看了很多相关文章,特别焦虑,唯恐自己的进步赶不上 AI 的发展. 还有就是我非常看 ...

  2. 一文聊透 IP 地址的那些事

    IP 地址,是一个大家都耳熟能详的名词.以生活举例,IP 在互联网中的作用就像是寄件时的收件人地址和寄件人地址,收件人地址让信件可以被正确送达,寄件人地址则让收到信的人可以回信. IP 地址作为每一个 ...

  3. 【每日一题】33. 简单瞎搞题 (滚动数组 + bitset 优化DP)

    补题链接:Here 这个问题的难点在于如何统计出所有和可能出现的情况,并且不能重复. 很容易想到用桶去存储每一个数,即某个和能够组合出来则为1,否则为0 不妨令 \(dp[i][j]\) 表示为第 \ ...

  4. ava进阶(39)--守护线程与定时器

    文档目录: 一.守护线程 二.定时器 ---------------------------------------分割线:正文------------------------------------ ...

  5. java进阶(18)--Enum枚举

    一.枚举基本概念 1.引用数据类型 2.每一个值可看作一个常量 3.方法返回结果>2时建议使用枚举,=2建议使用boolean   二.举例说明 1.程序1,方法返回为数字

  6. C++ std::initializer_list 实现原理勘误

    今天正在看侯捷<C++ 新标准 C++11-14>的视频,里面讲到 std::initializer_list 的实现原理,并且把源码贴出来. /// initializer_list t ...

  7. Elasticsearch 索引与文档的常用操作总结二:复杂条件查询

    本文为博主原创,未经允许不得转载: 1.  查询所有:match_all GET /es_db/_doc/_search { "query":{ "match_all&q ...

  8. JS - this 操作 dom , 添加样式

    <!DOCTYPE html> <html lang="en"> <head>     <meta charset="UTF-8 ...

  9. MySQL复习——20211027

    MYSQL MySQL创建数据库 我们可以在登录MySQL服务后,使用create命令创建数据库,语法如下: CREATE DATABASE 数据库名; 使用root用户登录,root用户拥有最高权限 ...

  10. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.12.20)

    一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...