Pluggy源码解读----Pluggy模块的应用

【原文链接】Pluggy源码解读----Pluggy模块的应用

pluggy 模块简单理解就是一个插件系统,pytest自动化测试框架的核心就是pluggy,简单点理解pytest就是基于pluggy将一系列的插件组装在一起的一个测试框架,因此这里首先介绍pluggy模块的使用方法。
首先安装pluggy模块,执行如下命令。

pip install pluggy

然后编写如下一段实例代码,这里面主要有三个类,其中MySpec类可以简单理解为接口类,而Plugin_1和Plugin_2可以理解为实现类,也可以理解为插件,即对同一个接口可以有多个实现,在主函数中pm可以理解为一个插件管理器,pm.add_hookspecs则是为插件系统增加接口,pm.register则是插件注册的过程,当插件注册后,pm.hook.myhook则会执行myhook函数,当有多个插件注册而且有多个插件中都实现了接口函数后,则每个插件的接口函数都会执行,而且按照先进后出的顺序即后注册先执行的顺序将所有注册了的插件总的该方法执行一遍。

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("inside Plugin_1.myhook()")
        return arg1 + arg2

class Plugin_2:
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_2.myhook()")
        return arg1 - arg2

if __name__=="__main__":
    pm = pluggy.PluginManager("myproject")
    pm.add_hookspecs(MySpec)
    pm.register(Plugin_1())
    pm.register(Plugin_2())
    results = pm.hook.myhook(arg1=1, arg2=2)
    print(results)

执行结果如下,可以看到这里先执行了Plugin_2中的myhook方法,然后执行Plugin_1中的myhook方法,,并且按照此顺序将每个插件中的方法返回值放在列表中返回。

(demo-HCIhX0Hq) E:\demo>python demo.py
inside Plugin_2.myhook()
inside Plugin_1.myhook()
[-1, 3]

(demo-HCIhX0Hq) E:\demo>

下面再看一个更为复杂一点的实例,如下代码,这里可以理解为MySpec类定义了四个接口函数,分别是myhook1、myhook2、myhook3、myhook4,然后定义了两个插件类,插件Pluggin_1实现了myhook1、myhook2、myhook3,而插件Pluggin_2实现myhook2、myhook3、myhook4。

import pluggy
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")

class MySpec:
    @hookspec
    def myhook1(self, arg1, arg2):
        pass
    @hookspec
    def myhook2(self, arg1, arg2):
        pass
    @hookspec
    def myhook3(self, arg1, arg2):
        pass

    @hookspec
    def myhook4(self, arg1, arg2):
        pass

class Plugin_1:
    @hookimpl
    def myhook1(self, arg1, arg2):
        print("inside Plugin_1.myhook1()")
        return arg1 + arg2
    @hookimpl
    def myhook2(self, arg1, arg2):
        print("inside Plugin_1.myhook2()")
        return arg1 + arg2 +1
    @hookimpl
    def myhook3(self, arg1, arg2):
        print("inside Plugin_1.myhook4()")
        return arg1 + arg2 + 2

class Plugin_2:

    @hookimpl
    def myhook2(self, arg1, arg2):
        print("inside Plugin_2.myhook2()")
        return arg1 - arg2
    @hookimpl
    def myhook3(self, arg1, arg2):
        print("inside Plugin_2.myhook3()")
        return arg1 - arg2 - 1

    @hookimpl
    def myhook4(self, arg1, arg2):
        print("inside Plugin_2.myhook4()")
        return arg1 - arg2 - 2

if __name__=="__main__":
    pm = pluggy.PluginManager("myproject")
    pm.add_hookspecs(MySpec)
    pm.register(Plugin_1())
    pm.register(Plugin_2())
    results = pm.hook.myhook1(arg1=1, arg2=2)
    print(results)
    results = pm.hook.myhook2(arg1=1, arg2=2)
    print(results)
    results = pm.hook.myhook3(arg1=1, arg2=2)
    print(results)
    results = pm.hook.myhook4(arg1=1, arg2=2)
    print(results)

执行结果如下,因为myhook1只有一个插件实现,因此返回值只有一个,myhook2和myhook3分别有两个插件实现,因此返回值有两个,并且按照先进后出的顺序存入列表,同样myhook4也只有一个插件定义,因此也只有一个返回值。

(demo-HCIhX0Hq) E:\demo>python demo.py
inside Plugin_1.myhook1()
[3]
inside Plugin_2.myhook2()
inside Plugin_1.myhook2()
[-1, 4]
inside Plugin_2.myhook3()
inside Plugin_1.myhook4()
[-2, 5]
inside Plugin_2.myhook4()
[-3]

(demo-HCIhX0Hq) E:\demo>

在定义接口的时候还可以通过firstresult=True指定只要有一个结果正确返回了就结束。,如下代码,在定义myhook的时候通过装饰器传参指定了firstresult=True,这样在执行的时候,注册了Pluggin_1和Pluggin_2两个插件,当然按照后进先出的顺序,首先执行Pluggin_2插件中的,此时能够正确返回,那么就结束了,不会再去执行Plugging_1中的myhook了。当然这是通过代码分析出来的,下面通过执行结果验证。

import pluggy

hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")

class MySpec:
    @hookspec(firstresult=True)
    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
    def myhook(self, arg1, arg2):
        print("in Plugin_2.myhook()")
        return arg1 - arg2

pm = pluggy.PluginManager("myproject")
pm.add_hookspecs(MySpec)
pm.register(Plugin_1())
pm.register(Plugin_2())
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)

执行结果如下,可以看到这里只执行了Plugin_2类中的myhook方法,Plugin_1类中的

(demo-HCIhX0Hq) E:\demo>python demo.py
in Plugin_2.myhook()
-1

此外还可以通过tryfirst或trylast指定当前类最先执行或者最后执行。比如如下代码,定会了三个插件类,在第二个类中使用tryfirst指定当前最先执行。

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(tryfirst=True)
    def myhook(self, arg1, arg2):
        print("in Plugin_2.myhook()")
        return arg1 - arg2

class Plugin_3:
    @hookimpl
    def myhook(self, arg1, arg2):
        print("in 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(results)

执行结果如下,可以看出此时首先执行了Plugin_2类。

(demo-HCIhX0Hq) E:\demo>python demo.py
in Plugin_2.myhook()
in Plugin_3.myhook()
in Plugin_1.myhook()
[-1, 9, 3]

(demo-HCIhX0Hq) E:\demo>

同样可以通过trylast指定当前类最后执行,比如这里把Plugin_2中的tryfirst修改为trylast,再次执行结果如下:

(demo-HCIhX0Hq) E:\demo>python demo.py
in Plugin_3.myhook()
in Plugin_1.myhook()
in Plugin_2.myhook()
[9, 3, -1]

(demo-HCIhX0Hq) E:\demo>

当传入hookwrapper=True时,需要在这个plugin中实现一个yield,plugin先执行yield之前的代码,然后去执行其他的pluggin,然后再回来执行yield之后的代码,同时通过yield可以获取到其他插件执行的结果。如下所示,在Plugin_2中使用了yield,在yield执行完成后可以获取到所有插件的执行结果,比如这里output,同时当所有yield周的代码执行完成之后,仍然是可以获取到所有插件的返回值列表的。

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)

执行结果如下,可以看到在Plugin_2中yield语句之后以及在所有代码执行完成后,都可以获取所有插件的返回值。

(demo-HCIhX0Hq) E:\demo>python demo.py
in Plugin_2.myhook() before yield...
inside Plugin_3.myhook()
in Plugin_1.myhook()
in Plugin_2.myhook() after yield...
[9, 3]
after all run ...
[9, 3]

(demo-HCIhX0Hq) E:\demo>
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

redrose2100

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

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

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

打赏作者

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

抵扣说明:

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

余额充值