【pytest框架源码分析五】pytest插件的注册流程

前文介绍到pytest整体是运用插件来实现其运行流程的。这里仔细介绍下具体过程。
首先进入main方法

def main(args: list[str] | os.PathLike[str] | None = None,plugins: Sequence[str | _PluggyPlugin] | None = None,
) -> int | ExitCode:"""Perform an in-process test run.:param args:List of command line arguments. If `None` or not given, defaults to readingarguments directly from the process command line (:data:`sys.argv`).:param plugins: List of plugin objects to be auto-registered during initialization.:returns: An exit code."""old_pytest_version = os.environ.get("PYTEST_VERSION")try:os.environ["PYTEST_VERSION"] = __version__try:config = _prepareconfig(args, plugins)except ConftestImportFailure as e:exc_info = ExceptionInfo.from_exception(e.cause)tw = TerminalWriter(sys.stderr)tw.line(f"ImportError while loading conftest '{e.path}'.", red=True)exc_info.traceback = exc_info.traceback.filter(filter_traceback_for_conftest_import_failure)exc_repr = (exc_info.getrepr(style="short", chain=False)if exc_info.tracebackelse exc_info.exconly())formatted_tb = str(exc_repr)for line in formatted_tb.splitlines():tw.line(line.rstrip(), red=True)return ExitCode.USAGE_ERRORelse:try:ret: ExitCode | int = config.hook.pytest_cmdline_main(config=config)try:return ExitCode(ret)except ValueError:return retfinally:config._ensure_unconfigure()except UsageError as e:tw = TerminalWriter(sys.stderr)for msg in e.args:tw.line(f"ERROR: {msg}\n", red=True)return ExitCode.USAGE_ERRORfinally:if old_pytest_version is None:os.environ.pop("PYTEST_VERSION", None)else:os.environ["PYTEST_VERSION"] = old_pytest_version

这个main方法,最重要的有两步,第一步是
config = _prepareconfig(args, plugins)
这一步就是读取配置以及注册插件等动作。这里附下_prepareconfig方法

def _prepareconfig(args: list[str] | os.PathLike[str] | None = None,plugins: Sequence[str | _PluggyPlugin] | None = None,
) -> Config:if args is None:args = sys.argv[1:]elif isinstance(args, os.PathLike):args = [os.fspath(args)]elif not isinstance(args, list):msg = (  # type:ignore[unreachable]"`args` parameter expected to be a list of strings, got: {!r} (type: {})")raise TypeError(msg.format(args, type(args)))config = get_config(args, plugins)pluginmanager = config.pluginmanagertry:if plugins:for plugin in plugins:if isinstance(plugin, str):pluginmanager.consider_pluginarg(plugin)else:pluginmanager.register(plugin)config = pluginmanager.hook.pytest_cmdline_parse(pluginmanager=pluginmanager, args=args)return configexcept BaseException:config._ensure_unconfigure()raise

这里这个方法可以看到,如果有plugin传入,则会注册。但是我们知道有些默认的插件是没有传入的,也注册了。其是通过get_config(args, plugins)这个方法来注册的

def get_config(args: list[str] | None = None,plugins: Sequence[str | _PluggyPlugin] | None = None,
) -> Config:# subsequent calls to main will create a fresh instancepluginmanager = PytestPluginManager()config = Config(pluginmanager,invocation_params=Config.InvocationParams(args=args or (),plugins=plugins,dir=pathlib.Path.cwd(),),)if args is not None:# Handle any "-p no:plugin" args.pluginmanager.consider_preparse(args, exclude_only=True)for spec in default_plugins:pluginmanager.import_plugin(spec)return config

这里初始化pluginmanager时,添加了默认的插件接口类
在这里插入图片描述
可以看到这个文件里都是插件的接口类,并且都是以pytest_开头的。
在这里插入图片描述
注意这里有些方法没有加@hookspec的装饰器,但是也添加进去了,这是因为pytest对其做了一层处理。我们知道add hooksepcs时,主要是判断其有无对应的spec_opts,没有添加@hooksepc的就没有sepc_opts。

在这里插入图片描述
在这里插入图片描述
对于这种没有添加@hookspec的方法,pytest重写了parse_hookspec_opts方法
在这里插入图片描述
这里可以看到,其先取了下对应的接口方法有无opts参数,如果没有,则判断一下方法是否是以pytest_开头的,如果是,则添加opts参数。所以这里添加了hooksepc.py文件中所有的接口类方法。
然后PytestPluginManager类中self.register(self)注册了它自己类中的插件,然后运行到get_config方法中的

    for spec in default_plugins:pluginmanager.import_plugin(spec)

这里将default_plugins中的插件也都注册了,default_plugin如下
在这里插入图片描述
import_plugin方法如下

   def import_plugin(self, modname: str, consider_entry_points: bool = False) -> None:"""Import a plugin with ``modname``.If ``consider_entry_points`` is True, entry point names are alsoconsidered to find a plugin."""# Most often modname refers to builtin modules, e.g. "pytester",# "terminal" or "capture".  Those plugins are registered under their# basename for historic purposes but must be imported with the# _pytest prefix.assert isinstance(modname, str), f"module name as text required, got {modname!r}"if self.is_blocked(modname) or self.get_plugin(modname) is not None:returnimportspec = "_pytest." + modname if modname in builtin_plugins else modnameself.rewrite_hook.mark_rewrite(importspec)if consider_entry_points:loaded = self.load_setuptools_entrypoints("pytest11", name=modname)if loaded:returntry:__import__(importspec)except ImportError as e:raise ImportError(f'Error importing plugin "{modname}": {e.args[0]}').with_traceback(e.__traceback__) from eexcept Skipped as e:self.skipped_plugins.append((modname, e.msg or ""))else:mod = sys.modules[importspec]self.register(mod, modname)

这里主要就是把default_plugins中提到的文件中的插件实现都注册了。(注意这里有些接口实现方法也是未加hookimpl装饰器的,但是也能注册,同上面添加spec的方法 ,pytest重新实现了parse_hookimpl_opts方法,只要是以pytest_开头的方法都可以正常注册)
回到main方法,_prepareconfig这一步就是将配置读取完,将默认插件注册完成。
接下来main方法执行到config.hook.pytest_cmdline_main(config=config),这个方法在hookspec中有接口,实现的方法也有多处。
在这里插入图片描述

对比可以看到这些文件中都有实现,其中mark,setuonly,setupplan中的实现方法都加了@pytest.hookimpl(tryfirst=True),按照之前介绍的原则,后加的先执行,加了tryfirst的先执行,这里的执行顺序为setupplan,setuponly,mark,cacheprovider,…中实现的pytest_cmdline_main方法。

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

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

相关文章

IoTDB日志提示Too many open files

问题 时序数据库 IoTDB 1.3.3 版本 IoTDB 执行查询操作失败,日志打印提示 Too many open files。通过命令查看打开文件数,结果如下: [root0002 DataReceiver]# lsof|grep 28347|wc -l DataNode 55444 [root0002 DataReceiver]# lsof|g…

prometheus 添加alertmanager添加dingtalk机器人告警

1、dingtalk创建机器人,目前我们采用加白名单的方式校验 2、定位到如下图 test结果如下

C 语 言 --- 操 作 符 2

C 语 言 --- 操 作 符 2 移 位 操 作 符定 义原 码 补 码 和 反 码左 移&#xff08;<<&#xff09;右 移&#xff08;>>&#xff09;算 术 右 移逻 辑 右 移 按 位 与、按 位 或、和 按 位 异 或按 位 与按 位 或按 位 异 或 逻 辑 反 操 作负 值 操 作按 位 取 反…

基于Spring Boot的公司资产网站的设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

零碳工厂能源管理系统的核心技术与应用实践

零碳工厂能源管理系统是一种高效的解决方案&#xff0c;旨在优化能源使用并减少碳排放&#xff0c;以帮助工厂实现低碳或零碳的生产目标。以下是该系统的详细构成和功能&#xff1a; 1. 核心组件 传感器和监测设备&#xff1a;用于实时监测工厂内的能源使用情况&#xff0c;包…

美摄接入DeepSeek等大模型,用多模态融合重构视频创作新边界!

今年以来&#xff0c;DeepSeek凭借其强大的深度推理分析能力&#xff0c;在AI领域掀起新的热潮。美摄科技快速响应市场需求&#xff0c;迅速接入以DeepSeek、通义千问、商汤、文心一言为代表的大模型&#xff0c;为企业视频创作生产带来全新体验。 传统视频创作面临着同质化、…

JAVA————十五万字汇总

JAVA语言概述 JAVA语句结构 JAVA面向对象程序设计&#xff08;一&#xff09; JAVA面向对象程序设计&#xff08;二&#xff09; JAVA面向对象程序设计&#xff08;三&#xff09;工具类的实现 JAVA面向对象程序设计&#xff08;四&#xff09;录入异常处理 JAVA图形用户界面设…

力扣热题100(方便自己复习,自用)

力扣热题100 1. 两数之和 - 力扣&#xff08;LeetCode&#xff09; 查找两数之和是不是等于target也就是我们找到一个数之后&#xff0c;用target将其减掉&#xff0c;再寻找应当对应的元素是什么每找到一个数&#xff0c;我们就将其放在集合中&#xff0c;因为集合中可以去重…

【yolo】yolo训练报错,以及解决方案

背景&#xff1a; 刚刚&#xff0c;写了《【yolo】yolo推理报错&#xff0c;以及解决方案》&#xff0c;马上训练就遇到类似的报错。 我对我标注的图像进行了300轮的训练&#xff0c;但是训练完300轮后&#xff0c;报错了。。。 报错信息 300 epochs completed in 0.085 hou…

vscode/cursor中python运行路径设置 模块导入问题

vscode/cursor中python运行路径设置 ## 文件路径设置 问题描述 pycharm的项目用cursor运行&#xff0c;出现目录找不到 后来利用 os.getcwd()&#xff0c;经过打印调试发现是IDE的本身配置问题 pycharm中&#xff0c;os.getcwd()默认打开当前脚本所在目录 vscode/cursor中…

理解线性动力学中的模态叠加法

线性动力学中的模态叠加方法 模态叠加法是线性动力学中一种有价值的工具&#xff0c;可以有效地确定频域或时域中的系统响应。对于某些类型的线性动力学分析&#xff0c;有必要使用此方法&#xff0c;因此了解该过程对于获得准确的结果至关重要。在本博客中&#xff0c;我们将…

报错 - redis - Unit redis.service could not be found.

报错&#xff1a; Unit redis.service could not be found.Could not connect to Redis at 127.0.0.1:6379: Connection refused解决方法&#xff1a; 检查状态、有必要的话 重新安装 Linux 上查看状态 systemctl status redis显示以下内容&#xff0c;代表正常服务 出现下面…

CMS网站模板定制设计与安全评估

内容概要 现代CMS&#xff08;内容管理系统&#xff09;作为网站建设的核心载体&#xff0c;其模板架构与安全防护体系的协同设计已成为企业数字化转型的关键环节。随着网络攻击向量日益复杂化&#xff0c;基于HTTPS协议的端到端加密部署不仅成为基础安全配置&#xff0c;更直…

【React】useEffect、useLayoutEffect底层机制

目录 useEffect不设置依赖设置空数组&#xff0c;无依赖设置多个依赖返回值是一个函数总结useEffect的使用环境useEffect 中发送请求错误示例用.then获取数据在useEffect创建一个函数 总结 useLayoutEffectuseLayoutEffect 和useEffect区别执行时机&#xff1a;浏览器渲染的关系…

深度解析学术论文成果评估(Artifact Evaluation):从历史到现状

深度解析学术论文成果评估(Artifact Evaluation)&#xff1a;从历史到现状 引言 在计算机科学和工程领域的学术研究中&#xff0c;可重复性和可验证性越来越受到重视。随着实验性研究的复杂性不断增加&#xff0c;确保研究成果可以被其他研究者验证和构建变得尤为重要。这一需…

VSCode创建VUE项目(四)增加用户Session管理

将用户信息存储或者更新到Session sessionStorage.setItem("userID",loginform.value.username); sessionStorage.setItem(loginTime, Date.now()); 获取Session信息 const storedUserInfo sessionStorage.getItem(userID); const loginTime sessionStorage.get…

威联通 后台可用命令查看Bash

一、查看所有可用命令的方法 列出所有外部命令&#xff08;二进制文件&#xff09; 外部命令通常存放在系统路径&#xff08;如 /bin, /usr/bin, /sbin, /usr/sbin&#xff09;中&#xff1a; bash ls /bin /usr/bin /sbin /usr/sbin # 直接列出命令目录&#xff08;结果较长&…

游戏MOD伴随盗号风险,仿冒网站借“风灵月影”窃密【火绒企业版V2.0】

游戏MOD&#xff08;即游戏修改器&#xff09;是一种能够对游戏进行修改或增强的程序&#xff0c;因其能够提升游戏体验&#xff0c;在玩家群体中拥有一定的市场。然而&#xff0c;这类程序大多由第三方开发者制作&#xff0c;容易缺乏完善的安全保障机制&#xff0c;这就为不法…

Kubernetes Init 容器:实现 Nginx 和 PHP 对 MySQL 的依赖检查

在设计 Kubernetes Pod 时&#xff0c;如果需要在启动 Nginx 和 PHP 之前等待 MySQL 启动完成&#xff0c;可以通过 初始化容器&#xff08;initC&#xff09; 来实现。初始化容器可以用于检查 MySQL 是否可用&#xff0c;只有在 MySQL 可用后&#xff0c;才会继续启动主容器&a…

SSL/TLS 和 SSH 介绍以及他们的区别

目录 SSL/TLS SSL/TLS工作原理的核心步骤握手阶段&#xff08;Handshake Protocol&#xff09;加密通信阶段&#xff08;Encrypted Communication Phase&#xff09;会话恢复&#xff08;Session Resumption&#xff09; SSH SSH 加密机制的核心步骤 SSH 和 SSL 区别 SSL/TLS …