Pluggy源码解读----PluginManager类实例化

【原文链接】Pluggy源码解读----PluginManager类实例化

在解析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。这就是在类中使用装饰器的时候通过装饰器传入参数的方式设置的。

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两个属性,其他属性基本都是初始化操作,待后续使用时再具体分析。

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中定义的,而此类的定义如下,即是一个空类,因此可以知道此类应该只是为了组织存储数据的,需要后续动态的增加设置属性。

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模块中调用函数执行的核心中的核心。这里暂时放在这,待后续调用时再继续详细解析。

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()
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

redrose2100

您的鼓励是我最大的创作动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值