溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Python?pluggy模塊如何使用

發(fā)布時(shí)間:2022-05-13 13:48:00 來(lái)源:億速云 閱讀:190 作者:iii 欄目:開(kāi)發(fā)技術(shù)

本篇內(nèi)容介紹了“Python pluggy模塊如何使用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

1 pluggy 簡(jiǎn)介

  • pluggy 作用:提供了一個(gè)簡(jiǎn)易便捷的插件系統(tǒng),可以做到插件與主題功能松耦合

  • pluggy 是pytest,tox,devpi的核心框架

2 安裝

執(zhí)行如下命令即可

pip install pluggy

3 使用初體驗(yàn)

import pluggy
# HookspecMarker 和 HookimplMarker 實(shí)質(zhì)上是一個(gè)裝飾器帶參數(shù)的裝飾器類(lèi),作用是給函數(shù)增加額外的屬性設(shè)置
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")
# 定義自己的Spec,這里可以理解為定義接口類(lèi)
class MySpec:
    # hookspec 是一個(gè)裝飾類(lèi)中的方法的裝飾器,為此方法增額外的屬性設(shè)置,這里myhook可以理解為定義了一個(gè)接口
    @hookspec
    def myhook(self, arg1, arg2):
        pass
# 定義了一個(gè)插件
class Plugin_1:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的和
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_1.myhook()")
        return arg1 + arg2
# 定義第二個(gè)插件
class Plugin_2:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_2.myhook()")
        return arg1 - arg2
# 實(shí)例化一個(gè)插件管理的對(duì)象,注意這里的名稱(chēng)要與文件開(kāi)頭定義裝飾器的時(shí)候的名稱(chēng)一致
pm = pluggy.PluginManager("myproject")
# 將自定義的接口類(lèi)加到鉤子定義中去
pm.add_hookspecs(MySpec)
# 注冊(cè)定義的兩個(gè)插件
pm.register(Plugin_1())
pm.register(Plugin_2())
# 通過(guò)插件管理對(duì)象的鉤子調(diào)用方法,這時(shí)候兩個(gè)插件中的這個(gè)方法都會(huì)執(zhí)行,而且遵循后注冊(cè)先執(zhí)行即LIFO的原則,兩個(gè)插件的結(jié)果講義列表的形式返回
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)

執(zhí)行結(jié)果如下:

inside Plugin_2.myhook()
inside Plugin_1.myhook()
[-1, 3]

4 詳解解釋

從代碼上看比較繞,其實(shí)通俗一點(diǎn)理解,其實(shí)挺簡(jiǎn)單的,可以理解為首先定義一個(gè)接口類(lèi),然后定義很多插件類(lèi),插件類(lèi)想要多少就定義多少, 接口類(lèi)中要定義接口,上面的例子值定義了一個(gè)接口,其實(shí)可以定義很多接口,在每個(gè)插件類(lèi)中需要選擇接口類(lèi)中的接口去實(shí)現(xiàn),當(dāng)然也不需要每個(gè) 都需要去實(shí)現(xiàn),可以根據(jù)自己的需要有選擇的去實(shí)現(xiàn)。

舉個(gè)例子:

比如定義了一個(gè)接口類(lèi),接口類(lèi)中定義了10個(gè)接口,同時(shí)定義了3個(gè)類(lèi),這三個(gè)類(lèi)分別實(shí)現(xiàn)了接口類(lèi)中3個(gè)接口,6個(gè)接口和10個(gè)接口,然后實(shí)例化一個(gè)插件管理的對(duì)象,實(shí)例化對(duì)象將接口類(lèi)加入定義模板中,然后去注冊(cè)這三個(gè)類(lèi),注冊(cè)之后,就可以通過(guò)插件管理對(duì)象的鉤子去調(diào)用接口類(lèi)中的每個(gè)方法了,比如調(diào)用頭三個(gè)方法,因?yàn)槊總€(gè)插件中都實(shí)現(xiàn)了,所以就會(huì)有三個(gè)結(jié)果,調(diào)用后面4-6的接口時(shí),因?yàn)橹挥袃蓚€(gè)插件實(shí)現(xiàn)了,所以只會(huì)有兩個(gè)結(jié)果返回,調(diào)用7-10的接口因?yàn)橹挥幸粋€(gè)插件類(lèi)實(shí)現(xiàn)了,所以就會(huì)只有一個(gè)結(jié)果返回,這樣插件使用起來(lái)就非常靈活,可以真正做到“熱插拔”

下面用代碼示例演示:

import pluggy
# HookspecMarker 和 HookimplMarker 實(shí)質(zhì)上是一個(gè)裝飾器帶參數(shù)的裝飾器類(lèi),作用是給函數(shù)增加額外的屬性設(shè)置
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")
# 定義自己的Spec,這里可以理解為定義接口類(lèi)
class MySpec:
    # hookspec 是一個(gè)裝飾類(lèi)中的方法的裝飾器,為此方法增額外的屬性設(shè)置,這里myhook可以理解為定義了3個(gè)接口
    @hookspec
    def myhook1(self, arg1, arg2):
        pass
    @hookspec
    def myhook2(self, arg1, arg2):
        pass
    @hookspec
    def myhook3(self, arg1, arg2):
        pass
# 定義了一個(gè)插件
class Plugin_1:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的和
    @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 myhook4(self, arg1, arg2):
        print("inside Plugin_1.myhook4()")
        return arg1 + arg2 + 2
# 定義第二個(gè)插件
class Plugin_2:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl
    def myhook1(self, arg1, arg2):
        print("inside Plugin_2.myhook1()")
        return arg1 - arg2
    @hookimpl
    def myhook2(self, arg1, arg2):
        print("inside Plugin_2.myhook2()")
        return arg1 - arg2 -1
    @hookimpl
    def myhook3(self, arg1, arg2):
        print("inside Plugin_2.myhook3()")
        return arg1 - arg2 -2
# 實(shí)例化一個(gè)插件管理的對(duì)象,注意這里的名稱(chēng)要與文件開(kāi)頭定義裝飾器的時(shí)候的名稱(chēng)一致
pm = pluggy.PluginManager("myproject")
# 將自定義的接口類(lèi)加到鉤子定義中去
pm.add_hookspecs(MySpec)
# 注冊(cè)定義的兩個(gè)插件
pm.register(Plugin_1())
pm.register(Plugin_2())
# 通過(guò)插件管理對(duì)象的鉤子調(diào)用方法,這時(shí)候兩個(gè)插件中的這個(gè)方法都會(huì)執(zhí)行,而且遵循后注冊(cè)先執(zhí)行即LIFO的原則,兩個(gè)插件的結(jié)果講義列表的形式返回
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)

執(zhí)行結(jié)果如下:

inside Plugin_2.myhook1()
inside Plugin_1.myhook1()
[-1, 3]
inside Plugin_2.myhook2()
inside Plugin_1.myhook2()
[-2, 4]
inside Plugin_2.myhook3()
[-3]
inside Plugin_1.myhook4()
[5]

從上面的代碼示例可以看出:

  • 1)myhook1 和 myhook2 因?yàn)閮蓚€(gè)插件都實(shí)現(xiàn)了,所有返回兩個(gè)結(jié)果,而且是倒序的

  • 2)myhook3 因?yàn)橹挥胁寮?實(shí)現(xiàn)了,所以只有一個(gè)返回結(jié)果

  • 3)myhook4 在spec中未定義,這里卻也有結(jié)果,目前理解可能是pluggy的bug,待后續(xù)看源碼后解釋

5 HookspeckMarker裝飾器支持傳入一些特定的參數(shù)

當(dāng)傳入firstresult=True時(shí),獲取第一個(gè)plugin執(zhí)行結(jié)果后就停止繼續(xù)執(zhí)行

import pluggy
# HookspecMarker 和 HookimplMarker 實(shí)質(zhì)上是一個(gè)裝飾器帶參數(shù)的裝飾器類(lèi),作用是給函數(shù)增加額外的屬性設(shè)置
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")
# 定義自己的Spec,這里可以理解為定義接口類(lèi)
class MySpec:
    # hookspec 是一個(gè)裝飾類(lèi)中的方法的裝飾器,為此方法增額外的屬性設(shè)置,這里myhook可以理解為定義了一個(gè)接口
    @hookspec(firstresult=True)
    def myhook(self, arg1, arg2):
        pass
# 定義了一個(gè)插件
class Plugin_1:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的和
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_1.myhook()")
        return arg1 + arg2
# 定義第二個(gè)插件
class Plugin_2:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_2.myhook()")
        return arg1 - arg2
# 實(shí)例化一個(gè)插件管理的對(duì)象,注意這里的名稱(chēng)要與文件開(kāi)頭定義裝飾器的時(shí)候的名稱(chēng)一致
pm = pluggy.PluginManager("myproject")
# 將自定義的接口類(lèi)加到鉤子定義中去
pm.add_hookspecs(MySpec)
# 注冊(cè)定義的兩個(gè)插件
pm.register(Plugin_1())
pm.register(Plugin_2())
# 通過(guò)插件管理對(duì)象的鉤子調(diào)用方法,這時(shí)候兩個(gè)插件中的這個(gè)方法都會(huì)執(zhí)行,而且遵循后注冊(cè)先執(zhí)行即LIFO的原則,兩個(gè)插件的結(jié)果講義列表的形式返回
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)

執(zhí)行結(jié)果如下:

inside Plugin_2.myhook()
-1

6 HookImplMarker裝飾器也支持傳入一些特定的參數(shù)

常用的有tryfirst和trylast以及hookwrapper

  • 當(dāng)傳入tryfirst=True時(shí),表示這個(gè)類(lèi)的hook函數(shù)會(huì)優(yōu)先執(zhí)行,其他的仍然按照后進(jìn)先出的順序執(zhí)行

import pluggy
# HookspecMarker 和 HookimplMarker 實(shí)質(zhì)上是一個(gè)裝飾器帶參數(shù)的裝飾器類(lèi),作用是給函數(shù)增加額外的屬性設(shè)置
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")
# 定義自己的Spec,這里可以理解為定義接口類(lèi)
class MySpec:
    # hookspec 是一個(gè)裝飾類(lèi)中的方法的裝飾器,為此方法增額外的屬性設(shè)置,這里myhook可以理解為定義了一個(gè)接口
    @hookspec
    def myhook(self, arg1, arg2):
        pass
# 定義了一個(gè)插件
class Plugin_1:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的和
    @hookimpl(tryfirst=True)
    def myhook(self, arg1, arg2):
        print("inside Plugin_1.myhook()")
        return arg1 + arg2
# 定義第二個(gè)插件
class Plugin_2:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_2.myhook()")
        return arg1 - arg2
# 定義第三個(gè)插件
class Plugin_3:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_3.myhook()")
        return arg1 - arg2+10
# 實(shí)例化一個(gè)插件管理的對(duì)象,注意這里的名稱(chēng)要與文件開(kāi)頭定義裝飾器的時(shí)候的名稱(chēng)一致
pm = pluggy.PluginManager("myproject")
# 將自定義的接口類(lèi)加到鉤子定義中去
pm.add_hookspecs(MySpec)
# 注冊(cè)定義的兩個(gè)插件
pm.register(Plugin_1())
pm.register(Plugin_2())
pm.register(Plugin_3())
# 通過(guò)插件管理對(duì)象的鉤子調(diào)用方法,這時(shí)候兩個(gè)插件中的這個(gè)方法都會(huì)執(zhí)行,而且遵循后注冊(cè)先執(zhí)行即LIFO的原則,兩個(gè)插件的結(jié)果講義列表的形式返回
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)

執(zhí)行結(jié)果讓如下:

inside Plugin_1.myhook()
inside Plugin_3.myhook()
inside Plugin_2.myhook()
[3, 9, -1]

  • 當(dāng)傳入trylast=True,表示當(dāng)前插件的hook函數(shù)會(huì)盡可能晚的執(zhí)行,其他的仍然按照后進(jìn)先出的順序執(zhí)行

import pluggy
# HookspecMarker 和 HookimplMarker 實(shí)質(zhì)上是一個(gè)裝飾器帶參數(shù)的裝飾器類(lèi),作用是給函數(shù)增加額外的屬性設(shè)置
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")
# 定義自己的Spec,這里可以理解為定義接口類(lèi)
class MySpec:
    # hookspec 是一個(gè)裝飾類(lèi)中的方法的裝飾器,為此方法增額外的屬性設(shè)置,這里myhook可以理解為定義了一個(gè)接口
    @hookspec
    def myhook(self, arg1, arg2):
        pass
# 定義了一個(gè)插件
class Plugin_1:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的和
    @hookimpl()
    def myhook(self, arg1, arg2):
        print("inside Plugin_1.myhook()")
        return arg1 + arg2
# 定義第二個(gè)插件
class Plugin_2:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl(trylast=True)
    def myhook(self, arg1, arg2):
        print("inside Plugin_2.myhook()")
        return arg1 - arg2
# 定義第三個(gè)插件
class Plugin_3:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_3.myhook()")
        return arg1 - arg2+10
# 實(shí)例化一個(gè)插件管理的對(duì)象,注意這里的名稱(chēng)要與文件開(kāi)頭定義裝飾器的時(shí)候的名稱(chēng)一致
pm = pluggy.PluginManager("myproject")
# 將自定義的接口類(lèi)加到鉤子定義中去
pm.add_hookspecs(MySpec)
# 注冊(cè)定義的兩個(gè)插件
pm.register(Plugin_1())
pm.register(Plugin_2())
pm.register(Plugin_3())
# 通過(guò)插件管理對(duì)象的鉤子調(diào)用方法,這時(shí)候兩個(gè)插件中的這個(gè)方法都會(huì)執(zhí)行,而且遵循后注冊(cè)先執(zhí)行即LIFO的原則,兩個(gè)插件的結(jié)果講義列表的形式返回
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)

執(zhí)行結(jié)果如下:

inside Plugin_3.myhook()
inside Plugin_1.myhook()
inside Plugin_2.myhook()
[9, 3, -1]

  • 當(dāng)傳入hookwrapper=True時(shí),需要在這個(gè)plugin中實(shí)現(xiàn)一個(gè)yield,plugin先執(zhí)行yield

之前的代碼,然后去執(zhí)行其他的pluggin,然后再回來(lái)執(zhí)行yield之后的代碼,同時(shí)通過(guò)yield可以 獲取到其他插件執(zhí)行的結(jié)果

import pluggy
# HookspecMarker 和 HookimplMarker 實(shí)質(zhì)上是一個(gè)裝飾器帶參數(shù)的裝飾器類(lèi),作用是給函數(shù)增加額外的屬性設(shè)置
hookspec = pluggy.HookspecMarker("myproject")
hookimpl = pluggy.HookimplMarker("myproject")
# 定義自己的Spec,這里可以理解為定義接口類(lèi)
class MySpec:
    # hookspec 是一個(gè)裝飾類(lèi)中的方法的裝飾器,為此方法增額外的屬性設(shè)置,這里myhook可以理解為定義了一個(gè)接口
    @hookspec
    def myhook(self, arg1, arg2):
        pass
# 定義了一個(gè)插件
class Plugin_1:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的和
    @hookimpl()
    def myhook(self, arg1, arg2):
        print("inside Plugin_1.myhook()")
        return arg1 + arg2
# 定義第二個(gè)插件
class Plugin_2:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl(hookwrapper=True)
    def myhook(self, arg1, arg2):
        print("inside Plugin_2.myhook() before yield...")
        output=yield
        result=output.get_result()
        print(result)
        print("inside Plugin_2.myhook() after yield...")
# 定義第三個(gè)插件
class Plugin_3:
    # 插件中實(shí)現(xiàn)了上面定義的接口,同樣這個(gè)實(shí)現(xiàn)接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個(gè)參數(shù)的差
    @hookimpl
    def myhook(self, arg1, arg2):
        print("inside Plugin_3.myhook()")
        return arg1 - arg2+10
# 實(shí)例化一個(gè)插件管理的對(duì)象,注意這里的名稱(chēng)要與文件開(kāi)頭定義裝飾器的時(shí)候的名稱(chēng)一致
pm = pluggy.PluginManager("myproject")
# 將自定義的接口類(lèi)加到鉤子定義中去
pm.add_hookspecs(MySpec)
# 注冊(cè)定義的兩個(gè)插件
pm.register(Plugin_1())
pm.register(Plugin_2())
pm.register(Plugin_3())
# 通過(guò)插件管理對(duì)象的鉤子調(diào)用方法,這時(shí)候兩個(gè)插件中的這個(gè)方法都會(huì)執(zhí)行,而且遵循后注冊(cè)先執(zhí)行即LIFO的原則,兩個(gè)插件的結(jié)果講義列表的形式返回
results = pm.hook.myhook(arg1=1, arg2=2)
print(results)

執(zhí)行結(jié)果如下:

inside Plugin_2.myhook() before yield...
inside Plugin_3.myhook()
inside Plugin_1.myhook()
[9, 3]
inside Plugin_2.myhook() after yield...
[9, 3]

“Python pluggy模塊如何使用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI