测试开发技术网站
博客
设计
设计
开发
Python
测试
unittest
运维
Linux基础应用
CI/CD
CI/CD
数据库
数据库
云计算
云计算
云原生
云原生
爬虫
爬虫
数据分析
数据分析
人工智能
人工智能
登录
注册
Pluggy源码解读----PluginManager类实例化
收藏本文
作者:redrose2100 类别: 日期:2022-12-10 04:51:07 阅读:781 次 消耗积分:0 分
[TOC] ![](https://redrose2100.oss-cn-hangzhou.aliyuncs.com/img/7cd47362-951c-11ee-986a-0242ac110004.png) 在解析PlugginManager类之前,首先再来看下一下pluggy应用实例代码,如下所示,通过前面的源码解析,至此已经存在两个实例hookspec和hookimpl,此外MySpec类中的myhook方法新增了一个myproject_spce的属性,属性值是一个字典,字典包含三个key,分表是firstresult,historic,warn_on_impl,而Plugin_1、Plugin_2、Plugin_3类中的myhook方法同样也都增加了一个myproject_impl属性,属性值也是一个字典,字典包含hookwrapper、optionalhook、tryfirst、trylast、specname五个key。其中Plugin_2中myhook的myproject_impl属性中的hookwrapper的值为True。这就是在类中使用装饰器的时候通过装饰器传入参数的方式设置的。 ```python import pluggy hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") class MySpec: @hookspec def myhook(self, arg1, arg2): pass class Plugin_1: @hookimpl def myhook(self, arg1, arg2): print("in Plugin_1.myhook()") return arg1 + arg2 class Plugin_2: @hookimpl(hookwrapper=True) def myhook(self, arg1, arg2): print("in Plugin_2.myhook() before yield...") output=yield result=output.get_result() print("in Plugin_2.myhook() after yield...") print(result) class Plugin_3: @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_3.myhook()") return arg1 - arg2+10 pm = pluggy.PluginManager("myproject") pm.add_hookspecs(MySpec) pm.register(Plugin_1()) pm.register(Plugin_2()) pm.register(Plugin_3()) results = pm.hook.myhook(arg1=1, arg2=2) print("after all run ...") print(results) ``` 然后就开始对PluginManager类进行了实例化,实例化的对象为pm,因为PluginManager类中的代码比较多,而实例化的时候只会调用类中的\_\_init\_\_方法,因此,这里仅列出PluginManager类中\_\_init\_\_方法的源码,如下所示,这里面首先对project\_name属性赋值,比如这里赋值为myproject,然后初始化了几个变量,\_name2plugin从名称可以推断出来是插件的名字和插件对象的映射关系,\_plugin2hookcallers可以大概推断出来是插件和hookcaller的对应关系,\_plugin_distinfo目前推断不出来具体功能,只可能猜测是插件的某方面的信息,这里先不管,只知道这也是一个列表,用于后续存储插件的某方面的信息。hook则是\_HookRelay类的一个实例,\_inner_hookexec则是一个函数,即是\_multicall函数的别名,通过此文件的import部分,可以找到此函数在\_\_callers.py文件中定义。trace通过名称也可以知道这个是一个帮准打印调用追踪栈的功能,从pluggy模块的主要功能来说,可以暂时不管此辅助功能。因此需要关注的主要就是hook和\_inner_hookexec两个属性,其他属性基本都是初始化操作,待后续使用时再具体分析。 ```python class PluginManager: """Core :py:class:`.PluginManager` class which manages registration of plugin objects and 1:N hook calling. You can register new hooks by calling :py:meth:`add_hookspecs(module_or_class) <.PluginManager.add_hookspecs>`. You can register plugin objects (which contain hooks) by calling :py:meth:`register(plugin) <.PluginManager.register>`. The :py:class:`.PluginManager` is initialized with a prefix that is searched for in the names of the dict of registered plugin objects. For debugging purposes you can call :py:meth:`.PluginManager.enable_tracing` which will subsequently send debug information to the trace helper. """ def __init__(self, project_name): self.project_name = project_name self._name2plugin = {} self._plugin2hookcallers = {} self._plugin_distinfo = [] self.trace = _tracing.TagTracer().get("pluginmanage") self.hook = _HookRelay() self._inner_hookexec = _multicall ``` 通过import部分可以查看到\_HookRelay类是在\_hook.py中定义的,而此类的定义如下,即是一个空类,因此可以知道此类应该只是为了组织存储数据的,需要后续动态的增加设置属性。 ```python class _HookRelay: """hook holder object for performing 1:N hook calls where N is the number of registered plugins. """ ``` \_inner_hookexec则指定的是\_callers.py文件中的\_multicall函数,\_multicall函数的定义如下,这里暂时先不去详细的解释此函数的功能,但是粗滤的看一下可以猜测出,这个函数将是pluggy模块中调用函数执行的核心中的核心。这里暂时放在这,待后续调用时再继续详细解析。 ```python def _multicall(hook_name, hook_impls, caller_kwargs, firstresult): """Execute a call into multiple python functions/methods and return the result(s). ``caller_kwargs`` comes from _HookCaller.__call__(). """ __tracebackhide__ = True results = [] excinfo = None try: # run impl and wrapper setup functions in a loop teardowns = [] try: for hook_impl in reversed(hook_impls): try: args = [caller_kwargs[argname] for argname in hook_impl.argnames] except KeyError: for argname in hook_impl.argnames: if argname not in caller_kwargs: raise HookCallError( f"hook call must provide argument {argname!r}" ) if hook_impl.hookwrapper: try: gen = hook_impl.function(*args) next(gen) # first yield teardowns.append(gen) except StopIteration: _raise_wrapfail(gen, "did not yield") else: res = hook_impl.function(*args) if res is not None: results.append(res) if firstresult: # halt further impl calls break except BaseException: excinfo = sys.exc_info() finally: if firstresult: # first result hooks return a single value outcome = _Result(results[0] if results else None, excinfo) else: outcome = _Result(results, excinfo) # run all wrapper post-yield blocks for gen in reversed(teardowns): try: gen.send(outcome) _raise_wrapfail(gen, "has second yield") except StopIteration: pass return outcome.get_result() ```
始终坚持开源开放共享精神,同时感谢您的充电鼓励和支持!
版权所有,转载本站文章请注明出处:redrose2100, http://blog.redrose2100.com/article/491
上一篇:
Pluggy源码解读----HookspecMarker类和HookimplMarker类源码分析
下一篇:
Pluggy源码解读----add_hookspecs增加自定义的接口类
搜索
个人成就
出版书籍
《Pytest企业级应用实战》
测试开发技术全栈公众号
测试开发技术全栈公众号
DevOps技术交流微信群
加微信邀请进群
常用网站链接
开源软件洞察
云原生技术栈全景图
Python语言官方文档
Golang官方文档
Docker官方文档
Jenkins中文用户手册
Scrapy官方文档
VUE官方文档
Harbor官方文档
openQA官方文档
云原生开源社区
开源中国
Kubernetes中文文档
Markdown语法官方教程
Kubernetes中文社区
Kubersphere官方文档
BootStrap中文网站
JavaScript中文网
NumPy官方文档
Pandas官方文档
GitLink确实开源网站
数据库排名网站
编程语言排名网站
SEO综合查询网站
数学加减法练习自动生成网站
Kickstart Generator
文章分类
最新文章
最多阅读
特别推荐
×
Close
登录
注册
找回密码
登录邮箱:
登录密码:
图片验证码:
注册邮箱:
注册密码:
邮箱验证码:
发送邮件
注册邮箱:
新的密码:
邮箱验证码:
发送邮件