Python测试框架pytest核心库pluggy详解

news2024/11/17 17:42:48

代码案例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

import pluggy

# HookspecMarker 和 HookimplMarker 实质上是一个装饰器带参数的装饰器类,作用是给函数增加额外的属性设置

hookspec = pluggy.HookspecMarker("myproject")

hookimpl = pluggy.HookimplMarker("myproject")

'''

HookspeckMarker:

    传入firstresult=True时,获取第一个plugin执行结果后就停止继续执行 @hookspec(firstresult=True)

    historic - 表示这个 hook 是需要保存call history 的,当有新的 plugin 注册的时候,需要回放历史

hookimpl:

    当传入tryfirst=True时,表示这个类的hook函数会优先执行,其他的仍然按照后进先出的顺序执行

    当传入trylast=True,表示当前插件的hook函数会尽可能晚的执行,其他的仍然按照后进先出的顺序执行

    当传入hookwrapper=True时,需要在这个plugin中实现一个yield,plugin先执行yield之前的代码,

        然后去执行其他的pluggin,然后再回来执行yield之后的代码,同时通过yield可以获取到其他插件执行的结果

'''

# 定义自己的Spec,这里可以理解为定义接口类

class MySpec:

    # hookspec 是一个装饰类中的方法的装饰器,为此方法增额外的属性设置,这里myhook可以理解为定义了一个接口

    # 会给当前方法添加属性  键为 {self.project_name + "_spec"} 值是装饰器传入的参数

    @hookspec

    def myhook(self, arg1, arg2):

        pass

# 定义了一个插件

class Plugin_1:

    # 插件中实现了上面定义的接口,同样这个实现接口的方法用 hookimpl装饰器装饰,功能是返回两个参数的和

    @hookimpl

    def myhook(self, arg1, arg2):

        print("inside Plugin_1.myhook()")

        return arg1 + arg2

# 定义第二个插件

class Plugin_2:

    # 插件中实现了上面定义的接口,同样这个实现接口的方法用 hookimpl装饰器装饰,功能是返回两个参数的差

    @hookimpl(hookwrapper=True)

    def myhook(self, arg1, arg2):

        out = yield

        print("inside Plugin_2.myhook()")

        return arg1 - arg2

# 实例化一个插件管理的对象,注意这里的名称要与文件开头定义装饰器的时候的名称一致

pm = pluggy.PluginManager("myproject")

# 将自定义的接口类加到钩子定义中去

pm.add_hookspecs(MySpec)

# 注册定义的两个插件

pm.register(Plugin_1())

pm.register(Plugin_2())

# 通过插件管理对象的钩子调用方法,这时候两个插件中的这个方法都会执行,而且遵循后注册先执行即LIFO的原则,两个插件的结果讲义列表的形式返回

results = pm.hook.myhook(arg1=1, arg2=2)

print(results)

实例化:

  • 初始化一些参数,如_name2plugin:存放后续注册 plugin

添加到钩子定义中 (add_hookspecs)

  • 将定义的类已参数的方式传递进去 (module_or_class)
  • 遍历类中全部的方法
  • 判断: getattr(method, self.project_name + "_spec", None),判断类方法中是否有当前定义 myproject+spec 的属性,
    • 如果有则返回装饰器所得到的参数,没有则返回 None,其实就是判断有没有被@hookspec装饰,因为装饰了会设置上 myproject+spec 这个属性以及相对应的值
  • 如果有被装饰: 判断一下 self.hook 中是否以及存在了这个 spec
  • 如果不存在: 创建一个_HookCaller(spec 名字,_hookexec(本质就是一个执行 hook 的方法),传递进来的 spec 对象,第三步获得的参数,也就是通过装饰器 set 到方法中的一些参数) 对象
    • init: 判断 spec 对象是否为空,如果不为空:
      • ​ 先判断参数是否存在,如果存在,创建一个 HookSpec 给 self.spec,传递参数为 当前 spec 对象,当前 spec 名字,参数,self.function 就是对应的那个被装饰的方法,最后判断一下这个 spec 需不需要保存历史,如果需要,初始化一个列表
  • 将上面初始化的对象,通过 setattr 的方式存在到 self.hook 中,名字就是被装饰方法的名字,值是刚刚创建的对象
  • 最后会在 names 的列表中把这个添加的方法的名字添加进去,判断一个 names 是否为空,如果为空,则抛出异常
  • 添加 add_hookspecs 的步骤全部完成

注册插件 register

注册插件 (register): 传递实现插件的实体类对象

  • 判断是否传递插件名字,如果没传,就获取对象的name属性,如果还没有就直接用 id() 生产一个随机字符串做当前对象在插件中的名字

  • 判断名字是否存在,或者是否已被注册: self._name2plugin 和 self._plugin2hookcaller,前者是用 plugin_name 做 key,后者是用 plugin object 做 key,判断是否已经注册过重复的 plugin

  • self._name2plugin[plugin_name(插件名字)] = plugin(传递的实体类对象)
    self._plugin2hookcallers[plugin(传递的实体类对象)] = hookcallers = [],其实就是初始化一下 self._plugin2hookcallers[plugin],因为列表的引用传递,所有直接修改 hookcallers 也可以作用在 self 中

  • 遍历实体类对象的方法列表,判断是否被 impl 装饰:

    • a.先获取到方法对象
    • b.判断对象是否是内置函数、函数、方法或者方法描述符,如果不是直接返回
    • c.获取该方法的属性 hook 对象创建时传递的名字 (myproject) + "_impl" ,没有则返回 None
    • d.判断获取到的值是不是 None 并且不是一个字典,则将获取到的 res 赋值为 None
    • e. 最后返回 res,其实就是 hookimpl 装饰器,如果你不给值就给一堆默认值
  • 先判断参数列表是否为空: 如果不为空,进行设置默认值 (其实正常是不会出现没有值的情况),然后从实体类对象中获取到该方法的对象

    • 在创建一个新的对象 (HookImpl):init(self,传递进来的实体类对象,hook 名字 (第一步获取或者 id 生成),method 对象,第四步返回的参数字典),并且将参数列表跟新到 self.dict
  • 判断 self.hook 中是否以及注册了当前插件 (就是 add_hookspecs 注册的 spec 中是否有当前方法)

    • 如果没有注册,会直接注册一个,这样注册 specmodule_or_class 参数会为空,意为着不会有额外的一些参数 eg:tryfirst
  • hook.has_spec() 判断注册 spec 的 spec 属性不为空

    • self._verify_hook(hook(spec 对象), hookimpl(插件对象)) a.先判断当前对象中是否有 (_call_history 属性),历史 和是否 需要使用 yield b. 判断 hookimpl 和 hook.spec 的参数列表是否相等,如果不相等报错
  • hook._maybe_apply_history(hookimpl)
    a.判断是否有_call_history 这个属性

  • hook._add_hookimpl(hookimpl):
    a.判断是否为 hookwrapper 为 True,添加到不同的 wrappers 中
    b.判断是否有 trylast tryfirst 属性,将 hookimpl 存放到对应位置
    c.将 hook 添加到 hookcallers 中

  • 遍历结束后,返回 plugin_name(第一步产生)

运行插件 pm.hook.myhook

运行插件 pm.hook.myhook(arg1=1, arg2=2):本质就是调用对象的call方法

  • 先判断是否有顺序参数,如果有直接报错
  • 在判断是否有_call_history 这个属性
  • 判断实际传入参数,是否和插件需要参数一样
  • self._hookexec(self(hook 对象), self.get_hookimpls()(全部的已经注册的插件), kwargs(传入的参数))

self._inner_hookexec(hook(hook 对象), methods(插件), kwargs(参数))

1

2

3

4

5

6

# 实际调用,也就是hook.multicall的方法

self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(

            methods,

            kwargs,

            firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,

        )

  • _multicall(hook_impls(插件), caller_kwargs(参数), firstresult=False(@hookspec传入,默认 False))

1. 先将 hook_impls 变成一个可迭代对象 (reversed(hook_impls))

2. 先把顺序参数的参数列表,拿到 (列表推导式)

3. 判断需不需要将其他插件执行的结果传递进去

- 需要
- 先从 hook_impl 中拿出对应的方法并且传递参数,执行关键字 yield 前面部分
- 然后 next()
- 最后将这个方法添加到 teardowns 列表中去

- 不需要
- 先从 hook_impl 中拿出对于的方法并且传递参数
- 判断执行后的返回值是不是为空,不为空则添加到 results 列表中
- 最后判断是否有 firstresult 属性,如果有直接结束循环

4. 最后执行 (finally 中代码)

- 如果 firstresult 为 true,那么直接返回第一个插件返回的结果即可
- 执行 teardowns 列表中的需要最后执行的插件
- 通过迭代器的 send 方法,将上几个插件的结果传递进去

5. 返回 result 对象 :会判断是否有报错 如果没有直接返回结果列表,如果有报错会抛出异常

​现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1338737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SCA在 得物 DevSecOps 平台上应用

SCA在 得物 DevSecOps 平台上应用。 ############################# 免责声明:工具本身并无好坏,希望大家以遵守《网络安全法》相关法律为前提来使用该工具,支持研究学习,切勿用于非法犯罪活动,对于恶意使用该工具造成的损失,和本人及开发者无关。 ###################…

CAD objectArx 在操作mfc时出现“不支持尝试执行的操作“

问题原因: ARX中对话框通常继承自CAcUiDialog,CAcUiDialog 构造函数有个参数 HINSTANCE hInstance,默认为 NULL,指定了对话框资源所在DLL进程。如果没有指定该参数,在创建对话框(DoModal或Create&#xff…

超维空间S2无人机使用说明书——42、使用3维雷达进行室内定位——实物篇

引言:为了提高无人机室内定位的稳定性和多种定位方案,S系列无人机推出了基于三维雷达激光点云数据的定位方案。该方案的提出是为了改善视觉定位容易受到光照等影响导致的位置漂移。 简介 定位采用fast-lio定位算法,通过修改源码直接发布定位…

MySQL数据库——多表查询

1. 概述 MySQL中的多表查询是指在一个查询中同时使用两个或多个表,通过某种关系将它们连接起来,以检索所需的数据。多表查询在数据库操作中非常常见,尤其是在处理复杂的数据关系时。 简单讲就是:多表查询是指从多个表中检索数据…

HPM6750开发笔记《第一个helloworld例程》

HPM_SDK的使用: HPM_SDK界面如下图 此处选择所支持的5款evk大家根据自己的板子选 此处选择想看的例程工程 此处可选择生成工程的类型 其中debug工程是在纯RAM中运行的,板子掉电后代码会被删除,用来测试比较合适 其中挂flash的工程有两种其中…

【Java基础系列】equals方法使用与总结

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

加速布局!美格智能获国内某自主大厂智能座舱项目模组定点

近日,销售前线又传来重大好消息,美格智能座舱模组正式获得国内某自主大厂前装智能座舱项目定点。此次项目由主机厂直接定点模组,基于美格智能座舱模组SLM925来打造平台化智能座舱解决方案,同时此方案也将会应用于该汽车品牌及旗下…

【JavaWeb学习笔记】15 - jQuery

项目代码 https://github.com/yinhai1114/JavaWeb_LearningCode/tree/main/jquery 目录 零、官方文档 一、jQuery基本介绍 1.基本介绍 2.原理图 二、JQuery入门使用 1.下载JQuery 2.jQuery快速入门 三、jQuery对象 1.什么是jQuery对象? 2.DOM对象转换成jQuery对象 …

数据结构与算法(C语言版)P11——查找

1、查找的基本概念 从着一章节开始学习查找, 查找时属于数据的运算里面的知识。 数据的元素包括:查找、排序、插入、删除、修改等。 问题一:那里查找? 首先要清楚,是在哪里进行查找操作?是在线性表中找…

【Redis】七、Redis主从复制(重点)

文章目录 1、概念1.1、主从复制的作用主要包括1.2、一般来说,要将Redis运用于工程项目中,只使用一台Redis是万万不能的(宕机),原因如下 2、环境配置2.1、复制拷贝3个配置文件,然后修改对应的信息拷贝文件改…

2024,智能汽车走出独立曲线

文|刘俊宏 新能源汽车正在带领中国车市走向新的高峰。 在2023年,新能源汽车的增速显著超越整体车市。据中汽协预测,2023年,汽车总销量有望突破3000万辆,同比增长11.7%。其中,乘用车预计达到2600万辆&…

《软件需求分析报告》

第1章 序言 第2章 引言 2.1 项目概述 2.2 编写目的 2.3 文档约定 2.4 预期读者及阅读建议 第3章 技术要求 3.1 软件开发要求 第4章 项目建设内容 第5章 系统安全需求 5.1 物理设计安全 5.2 系统安全设计 5.3 网络安全设计 5.4 应用安全设计 5.5 对用户安全管理 …

SpringBoot3 应用分析

SpringBoot3-快速入门 1、简介 1. 前置知识 Java17Spring、SpringMVC、MyBatisMaven、IDEA 2. 环境要求 环境&工具版本(or later)SpringBoot3.0.5IDEA2021.2.1Java17Maven3.5Tomcat10.0Servlet5.0GraalVM Community22.3Native Build Tools0.9.1…

免费IDEA插件,Apipost出品!

IDEA插件市场中的API调试插件不是收费(Fast Request )就是不好用(apidoc、apidocx等等)今天给大家介绍一款国产的API调试插件:Apipost-Helper,完全免费且好看好用! 这款插件由Apipost团队开发的…

12.26ARM作业

三个按键中断&#xff0c;控制对应灯亮灭 main.c #include "key_it.h"void delay(int ms){int i,j;for(i0;i<ms;i){for(j0;j<2000;j);}}int main(){all_led_init();key1_it_config();key2_it_config();key3_it_config();while(1){printf("do main...\n&…

【每日一题】LeetCode160.相交链表

个人主页&#xff1a;白日依山璟 专栏&#xff1a;Java|数据结构与算法|每日一题 文章目录 1. 题目描述示例1&#xff1a;示例2&#xff1a;提示&#xff1a; 2. 思路3. 代码 1. 题目描述 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的…

【PTA】L1-32 Left-pad (C++)

题目链接&#xff1a;L1-032 Left-pad 题目要求&#xff1a; 根据新浪微博上的消息&#xff0c;有一位开发者不满NPM&#xff08;Node Package Manager&#xff09;的做法&#xff0c;收回了自己的开源代码&#xff0c;其中包括一个叫left-pad的模块&#xff0c;就是这个模块…

Mysql实时数据同步工具Alibaba Canal 使用

目录 Mysql实时数据同步工具Alibaba Canal 使用Canal是什么&#xff1f;工作原理重要版本更新说明 环境准备安装Canalwindow Java : Canal Client 集成依赖编码 工作流程其他学习canal资料 个人主页: 【⭐️个人主页】 需要您的【&#x1f496; 点赞关注】支持 &#x1f4af; M…

7.7、kali linux环境下搭建DVWA

目录 一、资料下载准备工作 1.1、DVWA源代码下载 二、开启Apache、mysql服务 2.1、下载Apache2文件 2.2、开启Apache2服务 方法一&#xff1a;开启Apache2服务&#xff08;手动&#xff09; 方法二&#xff1a;开启Apache2服务&#xff08;系统自启动&#xff09; 2.3、…

Linux进行模型微调前的环境准备

在Linux机器上对模型进行微调前&#xff0c;首先需要准备环境&#xff0c;即安装相关的软件。因为linux是一个无界面操作系统&#xff0c;软件安装完成后&#xff0c;还需要有便捷的交互方式编写脚本&#xff0c;调试脚本。此篇博客将专门介绍如何快速安装所需依赖软件&#xf…