pytest核心组件:pluggy插件系统(二)

本文系《pytest源码剖析》系列内容

正在连载,欢迎关注

图片

3. 插件机制实例分析

在 pluggy 的文档中提供了一个完整的名为 “eggsample” 软件 + 插件实例 ,

接下来,我以这个实例进行分析,更加深入得体验 pluggy 插件系统的使用

01

编写软件

以一个名为 eggsample 的项目编写软件本体,

首先,创建第一个文件来定义 hookimpl,供内部或外部插件实现 hook 使用

# eggsample/eggsample/__init__.pyimport pluggy
hookimpl = pluggy.HookimplMarker("eggsample")

接着,创建第二个文件来定义 hookspecs,申明本软件中一共有些什么样的 hook

# eggsample/eggsample/hookspecs.pyimport pluggy
hookspec = pluggy.HookspecMarker("eggsample")

@hookspecdef eggsample_add_ingredients(ingredients: tuple):    """本hook接收ingredients参数,并返回修改后的ingredients"""

@hookspecdef eggsample_prep_condiments(condiments: dict):    """本hook接收condiments参数,并返回一个字符串作为插件评论"""

接下来,实现一个内部插件,供软件本体使用

# eggsample/eggsample/lib.py # 此时lib.py就相当于一个插件了,只不过它是内部插件,不需要安装
@eggsample.hookimpl  # 实现了hook,但是没有按照hook要求接收参数def eggsample_add_ingredients():     spices = ["salt", "pepper"]    you_can_never_have_enough_eggs = ["egg", "egg"]    ingredients = spices + you_can_never_have_enough_eggs    return ingredients

@eggsample.hookimpl  # 实现了hook,但是没有按照hook要求返回字符串def eggsample_prep_condiments(condiments):    condiments["mint sauce"] = 1

最后,编写软件本体的代码

# eggsample/eggsample/host.py
import itertoolsimport random
import pluggy
from eggsample import hookspecs, lib
condiments_tray = {"pickled walnuts": 13, "steak sauce": 4, "mushy peas": 2}

def main():    pm = get_plugin_manager()    cook = EggsellentCook(pm.hook)    cook.add_ingredients()    cook.prepare_the_food()    cook.serve_the_food()

def get_plugin_manager():    pm = pluggy.PluginManager("eggsample")    pm.add_hookspecs(hookspecs)    pm.load_setuptools_entrypoints("eggsample")    pm.register(lib)    return pm

class EggsellentCook:    FAVORITE_INGREDIENTS = ("egg", "egg", "egg")
    def __init__(self, hook):        self.hook = hook        self.ingredients = None
    def add_ingredients(self):        results = self.hook.eggsample_add_ingredients(            ingredients=self.FAVORITE_INGREDIENTS        )        my_ingredients = list(self.FAVORITE_INGREDIENTS)        # Each hook returns a list - so we chain this list of lists        other_ingredients = list(itertools.chain(*results))        self.ingredients = my_ingredients + other_ingredients
    def prepare_the_food(self):        random.shuffle(self.ingredients)
    def serve_the_food(self):        condiment_comments = self.hook.eggsample_prep_condiments(            condiments=condiments_tray        )        print(f"Your food. Enjoy some {', '.join(self.ingredients)}")        print(f"Some condiments? We have {', '.join(condiments_tray.keys())}")        if any(condiment_comments):            print("\n".join(condiment_comments))

if __name__ == "__main__":    main()

在软件本体中,做了几件事情:

  1. 创建 pm(22 行)

  2. 加载 hook 申明(23 行)

  3. 通过 setuptools 加载第三方插件 (24 行)

  4. 通过 import 加载内置插件 (25 行)

  5. 调用 hook,完成软件功能

从这个例子来看,hook 的调用代码和 非 hook 调用代码混在一起的,

也就是说,软件会将插件(hook 的实现)作为自己的一部分进行使用,而不关心这些插件被定义在哪里

还没有结束,为了使用插件能够像 pytest 那样能够安装、运行,还需要为软件额外创建一个 setup.py

# eggsample/setup.py
setup(    name="eggsample",     install_requires="pluggy",    entry_points={"console_scripts": ["eggsample=eggsample.host:main"]},    packages=find_packages(),)

全部就绪之后,就可以使用 pip 进行安装了

pip install -e eggsample

OK,一个 python 软件就开发完毕并且安装成功了!

运行一下看看效果,在命令执行 eggsample

图片

它会将内部定义的数据 ("egg", "egg", "egg") 再加上插件返回的数据(["salt", "pepper","egg", "egg"])合并在一起,打乱顺序后输出

egg, egg, salt, egg, egg, pepper, egg

同时,也会将内部定义的数据 ({"pickled walnuts": 13, "steak sauce": 4, "mushy peas": 2}) 传递给插件,在插件对数据进行修改后再进行输出

pickled walnuts, steak sauce, mushy peas, mint sauce

目前软件只使用了内部插件来完成部分功能,

接下来看一看外部插件如何创建,

以及安装外部插件后,软件的执行效果

首发于公众号:测试开发研习社

原创不易,喜欢请星标+点赞+在看

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值