当前位置: 首页 > news >正文

【pytest框架源码分析二】pluggy源码分析之add_hookspecs和register

这里我们看一下_manager.py里的类和方法,最主要的是PluginManager类,类的初始化函数如下:

class PluginManager:"""Core class which manages registration of plugin objects and 1:N hookcalling.You can register new hooks by calling :meth:`add_hookspecs(module_or_class)<PluginManager.add_hookspecs>`.You can register plugin objects (which contain hook implementations) bycalling :meth:`register(plugin) <PluginManager.register>`.For debugging purposes you can call :meth:`PluginManager.enable_tracing`which will subsequently send debug information to the trace helper.:param project_name:The short project name. Prefer snake case. Make sure it's unique!"""def __init__(self, project_name: str) -> None:#: The project name.self.project_name: Final = project_nameself._name2plugin: Final[dict[str, _Plugin]] = {}self._plugin_distinfo: Final[list[tuple[_Plugin, DistFacade]]] = []#: The "hook relay", used to call a hook on all registered plugins.#: See :ref:`calling`.self.hook: Final = HookRelay()#: The tracing entry point. See :ref:`tracing`.self.trace: Final[_tracing.TagTracerSub] = _tracing.TagTracer().get("pluginmanage")self._inner_hookexec = _multicall

简单介绍下几个参数:
project_name: 项目名,同一个项目使用一个project name即可
_name2plugin:字典类型,用于存放插件名和插件的对应关系
_plugin_distinfo:存放了plugin及其distributions
hook:hook_relay实例,用于调用plugin
trace:与主流程无关,暂时用不上
_inner_hookexec :调用的_multicall函数,其在_call.py里,被注册的插件执行及结果返回逻辑都在这里面,我们后面具体看下。
接下来我们看下add_hookspecs和register方法,这两个是添加plugin的主要方法。前面我们介绍了hook中的HookspecMarker和HookimplMarker这两个装饰器,其实对hook来说,spec可以看作接口,impl是接口的实现,对应到add_hookspecs是添加hook的接口,register是添加实现的方法。这里我们简单举个例子:

import pluggyspec_test = pluggy.HookspecMarker('test11')
impl_test = pluggy.HookimplMarker('test11')# 定义spec类,相当于接口类,无具体内容
class Spec:@spec_testdef pluggy_test(self, arg1):print(f'this is spec test and arg is {arg1}')pass# 定义impl类,相当于实现类,具体内容在这里定义
class Impl1:@impl_testdef pluggy_test(self, arg1):print(f'this is test1 and arg is {arg1}')return arg1class Impl2:@impl_testdef pluggy_test(self, arg1):print(f'this is test2 and arg is {arg1}')return -1 * arg1pm = pluggy.PluginManager('test11')
pm.add_hookspecs(Spec)
pm.register(Impl1())
pm.register(Impl2())
res = pm.hook.pluggy_test(arg1=1)
print(res)

返回为
在这里插入图片描述
这里可以看出初始化pm后,先add_hookspecs,再register具体的实现,调用时,未打印sepc类中的内容,只打印了impl类中的内容,并且后注册的先执行。这就是pluggin的注册执行的大概流程,接下来看下具体的代码。

    def add_hookspecs(self, module_or_class: _Namespace) -> None:"""Add new hook specifications defined in the given ``module_or_class``.Functions are recognized as hook specifications if they have beendecorated with a matching :class:`HookspecMarker`."""names = []for name in dir(module_or_class):spec_opts = self.parse_hookspec_opts(module_or_class, name)if spec_opts is not None:hc: HookCaller | None = getattr(self.hook, name, None)if hc is None:hc = HookCaller(name, self._hookexec, module_or_class, spec_opts)setattr(self.hook, name, hc)else:# Plugins registered this hook without knowing the spec.hc.set_specification(module_or_class, spec_opts)for hookfunction in hc.get_hookimpls():self._verify_hook(hc, hookfunction)names.append(name)if not names:raise ValueError(f"did not find any {self.project_name!r} hooks in {module_or_class!r}")

add_hookspecs方法输入参数为module_or_class,即为module或者class,上面我举的例子是一个class,也可以是一个模块,如pytest就是入参了一个_pytest.hookspec,hookspec为pytest的一个py文件,用于添加spec。
在这里插入图片描述
接下来是具体的方法,定义了一个names的list,然后dir(module_or_class) 查看我们入参模块或者类的所有属性和方法(dir()内置函数,可查看所有属性和方法,注意这里是属性名和方法名,是str类型的,不是直接的方法),然后进入parse_hookspec_opts(module_or_class, name)方法

    def parse_hookspec_opts(self, module_or_class: _Namespace, name: str) -> HookspecOpts | None:"""Try to obtain a hook specification from an item with the given namein the given module or class which is being searched for hook specs.:returns:The parsed hookspec options for defining a hook, or None to skip thegiven item.This method can be overridden by ``PluginManager`` subclasses tocustomize how hook specifications are picked up. By default, returns theoptions for items decorated with :class:`HookspecMarker`."""method = getattr(module_or_class, name)opts: HookspecOpts | None = getattr(method, self.project_name + "_spec", None)return opts

这个方法首先根据name名获取了对应的方法,然后根据获取了方法的project_name + "_spec"的属性,HookspecOpts在_hook中有定义,这里我们前面讲过在HookspecMarker的__call__方法中有设置这个属性,所有加了HookspecMarker注解的方法都自动加了这个属性。
在这里插入图片描述
有这个属性的方法会返回对应的HookspecOpts,没的则返回None。add_hookspecs方法接着判断spec_opts如果不是None,去hook中根据名称获取对应的HookCaller,如果没有就返回None,一般我们添加时都是没有的,会返回None,这是再判断hc也是None,我们就给hook里添加一个。

hc = HookCaller(name, self._hookexec, module_or_class, spec_opts)
setattr(self.hook, name, hc)

如果hc不是None,这里其实就是plugin已经注册了,但是spec不是我们当前添加的这个spec。则进一步处理,进入set_specification(module_or_class, spec_opts)方法。

    def set_specification(self,specmodule_or_class: _Namespace,spec_opts: HookspecOpts,) -> None:if self.spec is not None:raise ValueError(f"Hook {self.spec.name!r} is already registered "f"within namespace {self.spec.namespace}")self.spec = HookSpec(specmodule_or_class, self.name, spec_opts)if spec_opts.get("historic"):self._call_history = []

如果spec不是None,则抛错(刚初始化的HookCaller其spec都是None,一般设置过才不是None),如果是None,则设置对应的spec。这边还涉及到historic参数,我们后面再捋捋。回到add_hookspecs方法,下一步就是校验_verify_hook,这个方法register时再一起看。最后把所有符合要求的name放到names里(即加了spec装饰器的方法),最后判断下names是不是为空,为空则抛错。
接下来看下register方法

    def register(self, plugin: _Plugin, name: str | None = None) -> str | None:"""Register a plugin and return its name.:param name:The name under which to register the plugin. If not specified, aname is generated using :func:`get_canonical_name`.:returns:The plugin name. If the name is blocked from registering, returns``None``.If the plugin is already registered, raises a :exc:`ValueError`."""plugin_name = name or self.get_canonical_name(plugin)if plugin_name in self._name2plugin:if self._name2plugin.get(plugin_name, -1) is None:return None  # blocked plugin, return None to indicate no registrationraise ValueError("Plugin name already registered: %s=%s\n%s"% (plugin_name, plugin, self._name2plugin))if plugin in self._name2plugin.values():raise ValueError("Plugin already registered under a different name: %s=%s\n%s"% (plugin_name, plugin, self._name2plugin))# XXX if an error happens we should make sure no state has been# changed at point of returnself._name2plugin[plugin_name] = plugin# register matching hook implementations of the pluginfor name in dir(plugin):hookimpl_opts = self.parse_hookimpl_opts(plugin, name)if hookimpl_opts is not None:normalize_hookimpl_opts(hookimpl_opts)method: _HookImplFunction[object] = getattr(plugin, name)hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)name = hookimpl_opts.get("specname") or namehook: HookCaller | None = getattr(self.hook, name, None)if hook is None:hook = HookCaller(name, self._hookexec)setattr(self.hook, name, hook)elif hook.has_spec():self._verify_hook(hook, hookimpl)hook._maybe_apply_history(hookimpl)hook._add_hookimpl(hookimpl)return plugin_name

方法入参为一个plugin对象和name,如果有name传入,则plugin_name取name,否则根据plugin取name

    def get_canonical_name(self, plugin: _Plugin) -> str:"""Return a canonical name for a plugin object.Note that a plugin may be registered under a different namespecified by the caller of :meth:`register(plugin, name) <register>`.To obtain the name of a registered plugin use :meth:`get_name(plugin)<get_name>` instead."""name: str | None = getattr(plugin, "__name__", None)return name or str(id(plugin))

get_canonical_name中直接取了plugin的__name__,如果这个__name__不存在则取其对象的id(id是python中判断身份的唯一标识,任何对象都会有自己的id,判断两个对象是否为同一个就是通过id这个内置函数判断的)。接下来判断plugin_name是否在_name2plugin中,如果在且其值为None时,说明被block了,直接返回,不为None时则抛错:插件已注册。
第二个判断plugin是否在_name2plugin中,由于上面已经判断过其name是否在_name2plugin这个dict中,走到这里说明其name这个key不在,如果plugin这个value在,说明plugin使用其他name注册了,抛出报错。
接下来self._name2plugin[plugin_name] = plugin往_name2plugin存ket-value。
最后遍历plugin中的所有方法,根据方法名在 self.parse_hookimpl_opts(plugin, name)获取对应的opts参数,

    def parse_hookimpl_opts(self, plugin: _Plugin, name: str) -> HookimplOpts | None:"""Try to obtain a hook implementation from an item with the given namein the given plugin which is being searched for hook impls.:returns:The parsed hookimpl options, or None to skip the given item.This method can be overridden by ``PluginManager`` subclasses tocustomize how hook implementation are picked up. By default, returns theoptions for items decorated with :class:`HookimplMarker`."""method: object = getattr(plugin, name)if not inspect.isroutine(method):return Nonetry:res: HookimplOpts | None = getattr(method, self.project_name + "_impl", None)except Exception:res = {}  # type: ignore[assignment]if res is not None and not isinstance(res, dict):# false positiveres = None  # type:ignore[unreachable]return res

这个方法和上面parse_hookspec_opts方法类似,也是先获取方法,然后获取方法中的self.project_name + "_impl"属性,如果没有则返回None,加了HookimplMarker装饰器的都会有这个属性。所以hookspec_opts不是None时,进入下一步,normalize_hookimpl_opts设置hookspec_opts的默认值。

def normalize_hookimpl_opts(opts: HookimplOpts) -> None:opts.setdefault("tryfirst", False)opts.setdefault("trylast", False)opts.setdefault("wrapper", False)opts.setdefault("hookwrapper", False)opts.setdefault("optionalhook", False)opts.setdefault("specname", None)

接下来去plugin根据name获取对应的方法,找到方法后,实例化HookImpl对象,name取hookimpl_opts.get(“specname”) or name,使用装饰器时,我们可以添加这个specname参数(添加则取它,不添则不取),HookCaller则根据name到self.hook中取。
如果hook为空(即未根据name获取到HookCaller),则重新实例化一个HookCaller对象,并且添加到self.hook中去,如果hook存在,则进入_verify_hook方法

    def _verify_hook(self, hook: HookCaller, hookimpl: HookImpl) -> None:if hook.is_historic() and (hookimpl.hookwrapper or hookimpl.wrapper):raise PluginValidationError(hookimpl.plugin,"Plugin %r\nhook %r\nhistoric incompatible with yield/wrapper/hookwrapper"% (hookimpl.plugin_name, hook.name),)assert hook.spec is not Noneif hook.spec.warn_on_impl:_warn_for_function(hook.spec.warn_on_impl, hookimpl.function)# positional arg checkingnotinspec = set(hookimpl.argnames) - set(hook.spec.argnames)if notinspec:raise PluginValidationError(hookimpl.plugin,"Plugin %r for hook %r\nhookimpl definition: %s\n""Argument(s) %s are declared in the hookimpl but ""can not be found in the hookspec"% (hookimpl.plugin_name,hook.name,_formatdef(hookimpl.function),notinspec,),)if hook.spec.warn_on_impl_args:for hookimpl_argname in hookimpl.argnames:argname_warning = hook.spec.warn_on_impl_args.get(hookimpl_argname)if argname_warning is not None:_warn_for_function(argname_warning, hookimpl.function)if (hookimpl.wrapper or hookimpl.hookwrapper) and not inspect.isgeneratorfunction(hookimpl.function):raise PluginValidationError(hookimpl.plugin,"Plugin %r for hook %r\nhookimpl definition: %s\n""Declared as wrapper=True or hookwrapper=True ""but function is not a generator function"% (hookimpl.plugin_name, hook.name, _formatdef(hookimpl.function)),)if hookimpl.wrapper and hookimpl.hookwrapper:raise PluginValidationError(hookimpl.plugin,"Plugin %r for hook %r\nhookimpl definition: %s\n""The wrapper=True and hookwrapper=True options are mutually exclusive"% (hookimpl.plugin_name, hook.name, _formatdef(hookimpl.function)),)

这个方法有如下几个判断:
1.首先判断了hook的is_historic和wrapper参数,这些参数不能同时为true。
2.然后判断了下hook.spec不能为None,这个在register方法中已经判断过,可能是担心其他地方又使用到,重新判断了下。
3.warn_on_impl如果为true,则warn一下。
4.接下来判断了下hookimpl中的参数是否比spec中的多,如果多会报错。
5.再下面还是关于warn的,这个和上面那个warn类似,只是这个是针对特定参数的。
6.判断hookwrapper参数为true的时候,其实现的方法是不是生成器方法,即方法中是不是yield返回的
7.最后判断了下hookwrapper和wrapper是不是同时为true,如果是,则报错。hookwrapper和wrapper是新旧版本的不同名称,只要用一个参数即可
然后进入_maybe_apply_history方法

    def _maybe_apply_history(self, method: HookImpl) -> None:"""Apply call history to a new hookimpl if it is marked as historic."""if self.is_historic():assert self._call_history is not Nonefor kwargs, result_callback in self._call_history:res = self._hookexec(self.name, [method], kwargs, False)if res and result_callback is not None:# XXX: remember firstresult isn't compat with historicassert isinstance(res, list)result_callback(res[0])

开始判断了下is_historic是否为true,是的话确认下_call_history 是否为None(一般is_historic为true时,_call_history 会被初始为[]),然后遍历了下_call_history,在res and result_callback都不是None的情况下,result_callback(res[0])。historic这部分后面一起串起来看。
这时主方法到了_add_hookimpl这里。

    def _add_hookimpl(self, hookimpl: HookImpl) -> None:"""Add an implementation to the callback chain."""for i, method in enumerate(self._hookimpls):if method.hookwrapper or method.wrapper:splitpoint = ibreakelse:splitpoint = len(self._hookimpls)if hookimpl.hookwrapper or hookimpl.wrapper:start, end = splitpoint, len(self._hookimpls)else:start, end = 0, splitpointif hookimpl.trylast:self._hookimpls.insert(start, hookimpl)elif hookimpl.tryfirst:self._hookimpls.insert(end, hookimpl)else:# find last non-tryfirst methodi = end - 1while i >= start and self._hookimpls[i].tryfirst:i -= 1self._hookimpls.insert(i + 1, hookimpl)

1.这个方法开始先是查询有多少wrapper为true的impl方法,在wrapper和非wrapper方法数组中找到分界点。这个数组中wrapper和非wrapper方法是分在前后两部分的,互不交叉。
2.然后判断新增的impl的wrapper是否为true,如果是true,则插入到分界点到数组末尾这段,如果不是true,则插到0到分界点这个位置。
3.在判断有无设置trylast或者tryfirst,如果有trylast则放到数组开头,如果是tryfirst,则放到数组末尾;如果都没有,则放到当前段tryfirst那一段的前一个。(越靠前的后执行,越靠后的先执行)
到这里就register完成了。

相关文章:

【pytest框架源码分析二】pluggy源码分析之add_hookspecs和register

这里我们看一下_manager.py里的类和方法&#xff0c;最主要的是PluginManager类&#xff0c;类的初始化函数如下&#xff1a; class PluginManager:"""Core class which manages registration of plugin objects and 1:N hookcalling.You can register new hoo…...

【leetcode hot 100 53】最大子数组和

解法一&#xff1a;&#xff08;动态规划&#xff09;我们用 f(i) 代表以第 i 个数结尾的「连续子数组的最大和」&#xff0c;那么很显然我们要求的答案就是&#xff1a;max{f(i)}&#xff0c;f(i)max{f(i−1)nums[i],nums[i]} class Solution {public int maxSubArray(int[] …...

Sqlserver安全篇之_启用TLS即配置SQL Server 数据库引擎以加密连接

官方文档 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/configure-sql-server-encryption?viewsql-server-ver16 https://learn.microsoft.com/zh-cn/sql/database-engine/configure-windows/manage-certificates?viewsql-server-ver15&pre…...

009---基于Verilog HDL的单比特信号边沿检测

文章目录 摘要一、边沿检测二、时序逻辑实现2.1 rtl2.2 tb 三、组合逻辑实现3.1 rtl3.2 tb 摘要 文章为学习记录。采用时序逻辑和组合逻辑实现边沿检测的核心逻辑。组合逻辑实现的上升沿和下降沿的脉冲比时序逻辑实现的上升沿和下降沿的脉冲提前一拍。 一、边沿检测 边沿检测…...

istio的核心概念简介

Istio 是一个开源的服务网格&#xff08;Service Mesh&#xff09;平台&#xff0c;旨在帮助管理、连接、保护和观察分布式微服务架构中的服务。它最初由 Google、IBM 和 Lyft 合作开发&#xff0c;广泛应用于 Kubernetes 环境。Istio 的核心目标是通过提供统一的流量管理、安全…...

如何在Apple不再支持的MacOS上安装Homebrew

手头有一台2012年产的Macbook Pro&#xff0c;系统版本停留在了10.15.7&#xff08;2020年9月24日发布的&#xff09;。MacOS 11及后续的版本都无法安装到这台老旧的电脑上。想通过pkg安装Homebrew&#xff0c;发现Homebrew releases里最新的pkg安装包不支持MacOS 10.15.7&…...

@update 的常见用法 Vue.js

在 Vue.js 中&#xff0c;update 是一个事件监听器&#xff0c;通常用于监听自定义组件或某些 Vue 原生组件&#xff08;如 <input> 或自定义组件&#xff09;的更新事件。它并不是 Vue 的核心 API&#xff0c;而是一种约定俗成的命名方式&#xff0c;用于处理组件内部状…...

C#开发——日期操作类DateTime

在C#中&#xff0c;日期和时间的操作主要通过 System.DateTime 类来实现。 DateTime 提供了丰富的属性和法&#xff0c;用于处理日期和时间的创建、格式化、比较和计算等操作。以下是一些常用的日期函数和特性&#xff1a; 一、创建日期和时间 1、直接指定日期和时间&…...

大语言模型学习--LangChain

LangChain基本概念 ReAct学习资料 https://zhuanlan.zhihu.com/p/660951271 LangChain官网地址 Introduction | &#x1f99c;️&#x1f517; LangChain LangChain是一个基于语言模型开发应用程序的框架。它可以实现以下应用程序&#xff1a; 数据感知&#xff1a;将语言模型…...

Oracle数据库安全防护体系构建与核心技术解析

引言&#xff1a;从某跨国集团数据泄露事件看Oracle防护困局 2025年1月&#xff0c;某跨国零售企业Oracle数据库遭APT组织"暗夜猎手"攻击&#xff0c;攻击者通过三重渗透路径实现数据窃取&#xff1a; 存储层突破&#xff1a;利用Oracle TDE密钥管理漏洞获取wallet…...

iOS UICollectionViewCell 点击事件自动化埋点

iOS 中经常要进行埋点&#xff0c;我们这里支持 UICollectionViewCell. 进行自动化埋点&#xff0c;思路&#xff1a; 通过hook UICollectionViewCell 的setSelected:方法&#xff0c; 则新的方法中执行埋点逻辑&#xff0c;并调用原来的方法 直接上代码 implementation UICol…...

软件工程---基于构件的软件工程

基于构件的软件工程&#xff08;CBSE&#xff09;是一种软件开发方法&#xff0c;通过重用现有的软件构件来构建系统&#xff0c;从而提高开发效率和软件质量。这种方法强调软件系统的模块化设计和构建复用&#xff0c;使得软件开发过程更加高效和灵活。 企业软件开发&#xf…...

Redis--单线程模型

目录 一、引言 二、Redis单线程模型 三、原因 四、为什么redis是单线程模型&#xff0c;但他的速度这么快&#xff1f; 五、总结 一、引言 本篇文章就Redis为什么是单线程模型做简单介绍。 二、Redis单线程模型 redis只使用一个线程&#xff0c;处理所有的命令请求&#x…...

NodeJS服务器 + Vue3框架 从搭建服务器 定义接口 到请求数据页面展示

NodeJS服务器 Vue3框架全栈开发 后端项目初始化项目安装express创建服务器server.js启动服务验证服务是否启动成功 前端项目新建vue3项目安装axios启动前端项目启动时报错问题解决 vue页面使用axios调用node接口完整代码页面效果图跨域问题解决 本篇文章主要介绍使用Node.js和…...

3.1、密码学基础

目录 密码学概念与法律密码安全分析密码体制分类 - 私钥密码/对称密码体制密码体制分类 - 公钥密码/非对称密码体制密码体制分类 - 混合密码体制 密码学概念与法律 密码学主要是由密码编码以及密码分析两个部分组成&#xff0c;密码编码就是加密&#xff0c;密码分析就是把我们…...

iOS接入Flutter项目

首先要把iOS项目和flutter项目统一目录下&#xff0c;而且需要注意的是flutter是module。 第一步&#xff1a;Flutter相关内容的创建 module创建命令&#xff1a; flutter create --templatemodule my_flutter&#xff0c;之后再执行 flutter pub get flutter build ios …...

点云配准技术的演进与前沿探索:从传统算法到深度学习融合(4)

4、点云配准面临的挑战与应对策略 4.1 点云配准面临的主要挑战 在点云配准的实际应用中&#xff0c;尽管已经取得了显著的研究成果&#xff0c;但仍然面临着诸多复杂而严峻的挑战&#xff0c;这些挑战严重制约了点云配准技术在更多领域的广泛应用和深入发展。 在自动驾驶场景…...

SEKI —— 基于大型语言模型的自进化与知识启发式神经架构搜索

01、项目概述 我们引入了一种基于新型大型语言模型&#xff08; LLM &#xff09;的神经架构搜索&#xff08; NAS &#xff09;方法&#xff0c;名为 SEKI 。SEKI 受到现代 LLM 中思维链&#xff08; CoT &#xff09;范式的启发&#xff0c;分为两个关键阶段运行&#xff1a…...

Nat Mach Intell | AI分子对接算法评测

《Nature Machine Intelligence》发表重磅评测&#xff0c;系统评估AI与物理方法在虚拟筛选&#xff08;VS&#xff09;中的表现&#xff0c;突破药物发现效率瓶颈。 核心评测体系&#xff1a;三大数据集 研究团队构建了三个新型测试集&#xff1a; TrueDecoy&#xff1a;含14…...

01. HarmonyOS应用开发实践与技术解析

文章目录 前言项目概述HarmonyOS应用架构项目结构Ability生命周期 ArkTS语言特性装饰器状态管理 UI组件与布局基础组件响应式布局样式与主题 页面路由与参数传递页面跳转参数接收 数据绑定与循环渲染数据接口定义循环渲染 条件渲染组件生命周期最佳实践与性能优化组件复用响应式…...

001-码云操作

码云操作 一、配置公钥1.官网地址1.进入 git bash2.查看生成的公钥3.设置到 Gitee4.测试 二、初始化一个项目1.新建仓库 一、配置公钥 方便后续提交代码不用填写密码 1.官网地址 官网地址&#xff1a;https://gitee.com/Git码云教程&#xff1a;https://gitee.com/help/arti…...

10.LED点阵实验

LED点阵是一种由发光二极管排列而成的显示器件&#xff0c;在生活里的各种电器中很常见&#xff0c;像汽车报站器和广告屏等地方都会用到它。 平时用得比较多的是 88 点阵。多个 88 点阵能组合成不同大小的 LED 点阵显示屏&#xff0c;比如 4 个 88 点阵可以拼成一个 1616 点阵…...

cesium+vue3自定义HTML实体弹窗、加高德路网、防实体漂浮、让用户画圆、鹰眼

一、基础使用&#xff1a;Cesium.js基础使用&#xff08;vue&#xff09;-CSDN博客 1、基础路径 为 Cesium 库设置一个全局变量 CESIUM_BASE_URL&#xff0c;用于指定 Cesium 的资源文件&#xff08;如 WebGL shaders、纹理、字体等&#xff09;的 示例场景&#xff1a;假设你…...

Spring(二)容器-注册

目录 一 定义组件Bean (1)添加组件 (2)获取组件 二 配置类Configuration (1)配置类的作用 三 MVC分层注解 - Controller&#xff1a; - Service&#xff1a; - Repository&#xff1a; 四 批量扫描ComponentScan (1) 默认扫描当前包及其子包 (2) 指定扫描包路径 …...

Java-实现PDF合同模板填写内容并导出PDF文件

可用于公司用户合同导出pdf文件 效果图 一、导入所需要jar包 <!--生成PDF--><dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.11</version></dependency><dependency&…...

C# 数据转换

1. 文本框读取byte&#xff0c;ushort格式数据 byte addr; if (byte.TryParse(textBoxAddr.Text, out addr) true) {}2. 字节数组 (byte[]) 转换为 ASCII 字符串 byte[] bytes { 72, 101, 108, 108, 111 }; // "Hello" 的 ASCII 码 string s0 Encoding.ASCII.Ge…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_list_init

ngx_list_init 定义在 src\core\ngx_list.h static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size) {list->part.elts ngx_palloc(pool, n * size);if (list->part.elts NULL) {return NGX_ERROR;}list->par…...

鸿蒙NEXT开发-元服务和服务卡片的开发

注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&#xff0c;博主会一直更新鸿蒙next相关知识 目录 1. 元服务基本概念 1.1 基本介绍 1.2 元…...

Docker安装Redpandata-console控制台

介绍 Redpanda控制台&#xff0c;这是一个功能强大的Web UI&#xff0c;用于管理和监控您的Redpanda 集群。探索实际示例和场景&#xff0c;以帮助您了解如何利用 Redpanda 控制台实现不同的用例&#xff0c;包括数据可观察性、Redpanda 管理、访问控制和连接。 可对Redpanda…...

DeepSeek Agent 企业应用

DeepSeek Agent 技术是基于深度求索(DeepSeek)大模型构建的智能代理系统,其核心技术架构与应用优势可从以下几个方面进行深度解析: 一、核心技术架构 混合专家模型(MoE)与架构优化 DeepSeek 的模型家族(如DeepSeekMoE、DeepSeek-V3)采用 混合专家系统(MoE) ,通过动…...

vcredist_x64 资源文件分享

vcredist_x64 是 Microsoft Visual C Redistributable 的 64 位版本&#xff0c;用于在 64 位 Windows 系统上运行使用 Visual C 开发的应用程序。它包含了运行这些应用程序所需的运行时组件。 vcredist_x64 资源工具网盘下载链接&#xff1a;https://pan.quark.cn/s/ef56f838f…...

解决redis lettuce连接池经常出现连接拒绝(Connection refused)问题

一.软件环境 windows10、11系统、springboot2.x、redis 6 7 linux&#xff08;centos&#xff09;系统没有出现这问题&#xff0c;如果你是linux系统碰到的&#xff0c;本文也有一定大参考价值。 根本思路就是&#xff1a;tcp/ip连接的保活(keepalive)。 二.问题描述 在spr…...

Mac远程桌面软件哪个好用?

远程桌面软件能帮助我们快速的远程控制另一台电脑&#xff0c;从而提供远程帮助&#xff0c;或者进行远程办公。那么&#xff0c;对macOS系统有什么好用的Mac远程桌面软件呢&#xff1f; 远程看看是一款操作简单、界面简洁的远程桌面软件&#xff0c;支持跨平台操作&#xff0…...

一篇吃透模型:all-MiniLM-L6-v2

MiniLM 是什么&#xff1f; MiniLM 是微软研究院开发的一种轻量级的语言模型&#xff0c;旨在以较小的参数量和计算成本实现与大型语言模型&#xff08;如 BERT&#xff09;相当的性能。它是基于 Transformer 架构的预训练模型&#xff0c;通过深度自注意力蒸馏&#xff08;De…...

深入学习Linux内存管理-缺页异常

1.什么是缺页异常 在Linux虚拟内存管理中,缺页异常(Page Fault) 是CPU在访问虚拟地址时发现对应物理页未就绪时触发的中断。根据触发原因,缺页异常分为两类: 次要缺页(Minor Fault):物理页已存在(如缓存或共享内存),只需建立映射。 主要缺页(Major Fault):需要…...

互推机制在开源AI智能名片2+1链动模式S2B2C商城小程序源码推广中的应用探索

摘要&#xff1a; 在数字化营销时代&#xff0c;开源AI智能名片21链动模式S2B2C商城小程序源码作为一种创新的技术解决方案&#xff0c;正逐步成为企业数字化转型的重要工具。然而&#xff0c;面对激烈的市场竞争&#xff0c;如何高效推广这一前沿技术产品&#xff0c;成为开发…...

SparkSQL全之RDD、DF、DS ,UDF、架构、资源划分、sql执行计划、调优......

1 SparkSQL概述 1.1 sparksql简介 Shark是专门针对于spark的构建大规模数据仓库系统的一个框架Shark与Hive兼容、同时也依赖于Spark版本Hivesql底层把sql解析成了mapreduce程序&#xff0c;Shark是把sql语句解析成了Spark任务随着性能优化的上限&#xff0c;以及集成SQL的一些…...

神经网络 - 激活函数(Maxout 单元)

一、Maxout 单元 Maxout 单元是一种特殊的激活函数&#xff0c;用于神经网络中&#xff0c;其主要思想是通过多个线性变换的最大值来作为神经元的输出&#xff0c;从而提高模型的表达能力和鲁棒性。 1. 数学定义 假设输入为 x&#xff0c;Maxout 单元会计算 k 个线性变换&am…...

【软考-架构】1.3、磁盘-输入输出技术-总线

GitHub地址&#xff1a;https://github.com/tyronczt/system_architect ✨资料&文章更新✨ 文章目录 存储系统&#x1f4af;考试真题输入输出技术&#x1f4af;考试真题第一题第二题 存储系统 寻道时间是指磁头移动到磁道所需的时间&#xff1b; 等待时间为等待读写的扇区…...

专业便捷PDF软件,即开即用

PDF文件因其小巧的体积、便捷的分享与存储方式&#xff0c;以及卓越的安全性&#xff0c;已成为学习、企业及各类机构中不可或缺的文件格式。无论是在学术研究、课程资料、商业报告还是合同文件中&#xff0c;PDF都能有效保持原有的格式和布局&#xff0c;确保内容在不同设备和…...

TEE可信执行环境的安全业务保护方案

基于硬件隔离技术&#xff0c;安全业务可在**TEE&#xff08;可信执行环境&#xff09;**中运行&#xff0c;以保障安全资产的完整性。TEE具备以下核心特点&#xff1a; 跨平台兼容&#xff1a;已集成于多款芯片平台&#xff08;MTK、NXP、RK、Samsung、TI、JLQ、Spreadtrum等…...

smolagents学习笔记系列(番外二)Agent+Ollama分析本地图像与文件

这篇文章是在 smolagents 官方教程结束后的番外篇二&#xff0c;实现了如何使用 smolagents 库 Ollama 调用本地模型对图像与文件进行分析。 【注意】&#xff1a;这篇文章需要你在本地部署Ollama的视觉语言模型&#xff0c;如果你的架构方案是纯线上模式&#xff0c;则可以跳…...

Unity TMP_InputField 多行输入时的高度适应

实现 设定输入框的行类型为多行新建行。 inputField.lineType TMP_InputField.LineType.MultiLineNewline; 进行输入时调整输入框高度。 height inputField.textComponent.preferredHeight offset; if (height < minHeight) {rect.SetSizeWithCurrentAnchors(RectTran…...

学习第九天-栈

栈的定义&#xff1a;栈是一种线性表数据结构&#xff0c;仅允许在表的一端&#xff08;栈顶&#xff09;进行插入&#xff08;入栈&#xff09;和删除&#xff08;出栈&#xff09;操作。没有数据元素时为「空栈」&#xff0c;遵循「后进先出&#xff08;LIFO&#xff09;」原…...

ArcGIS Pro应用指南:如何为栅格图精确添加坐标信息

一、引言 在地理信息系统中&#xff0c;栅格图是一种重要的数据类型。 然而&#xff0c;有时我们从网络上获取的栅格图并不包含坐标信息&#xff0c;这使得它们难以与其他带有坐标信息的数据进行集成和分析。 为了解决这一问题&#xff0c;我们需要对栅格图进行地理配准&…...

[前端] redux-thunk

Ps:这里是react18之后的使用方法 一、场景描述 为什么要使用redux-thunk&#xff1f; Thunk-“a piece of code that does some delayed word”。 官方给的解释是redux reducer必须是不包含副作用&#xff0c;但是真正的应用需要有副作用的逻辑。这个副作用包括异步逻辑、产生…...

委托者模式(掌握设计模式的核心之一)

目录 问题&#xff1a; 举例&#xff1a; 总结&#xff1a;核心就是利用Java中的多态来完成注入。 问题&#xff1a; 今天刷面经&#xff0c;刷到装饰者模式&#xff0c;又进阶的发现委托者模式&#xff0c;发现还是不理解&#xff0c;特此记录。 举例&#xff1a; ​老板​…...

爱吃蟠桃的孙悟空

爱吃蟠桃的孙悟空 真题目录: 点击去查看 E 卷 100分题型 题目描述 孙悟空爱吃蟠桃,有一天趁着蟠桃园守卫不在来偷吃。已知蟠桃园有 N 棵桃树,每颗树上都有桃子,守卫将在 H 小时后回来。 孙悟空可以决定他吃蟠桃的速度K(个/小时),每个小时选一颗桃树,并从树上吃掉 K 个…...

低功耗抄表方案-支持Modbus、DL/T645 及 DL/T698 协议‌电表

这是我测试的一款无线红外抄表器&#xff0c;该无线红外抄表器的功能特点及应用场景可归纳如下&#xff1a; 一、核心功能特性 1.多协议兼容与数据采集 可以采集Modbus、DL/T645 及 DL/T698 协议‌电表的各项数据&#xff0c;兼容国网电表、光伏逆变器等设备&#xff0c;包括…...

【含文档+PPT+源码】基于SpringBoot电脑DIY装机教程网站的设计与实现

项目介绍 本课程演示的是一款 基于SpringBoot电脑DIY装机教程网站的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本…...