Python pluggy模塊的用法示例演示
1 pluggy 簡介
- pluggy 作用:提供瞭一個簡易便捷的插件系統,可以做到插件與主題功能松耦合
- pluggy 是pytest,tox,devpi的核心框架
2 安裝
執行如下命令即可
pip install pluggy
3 使用初體驗
import pluggy # HookspecMarker 和 HookimplMarker 實質上是一個裝飾器帶參數的裝飾器類,作用是給函數增加額外的屬性設置 hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # 定義自己的Spec,這裡可以理解為定義接口類 class MySpec: # hookspec 是一個裝飾類中的方法的裝飾器,為此方法增額外的屬性設置,這裡myhook可以理解為定義瞭一個接口 @hookspec def myhook(self, arg1, arg2): pass # 定義瞭一個插件 class Plugin_1: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的和 @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # 定義第二個插件 class Plugin_2: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # 實例化一個插件管理的對象,註意這裡的名稱要與文件開頭定義裝飾器的時候的名稱一致 pm = pluggy.PluginManager("myproject") # 將自定義的接口類加到鉤子定義中去 pm.add_hookspecs(MySpec) # 註冊定義的兩個插件 pm.register(Plugin_1()) pm.register(Plugin_2()) # 通過插件管理對象的鉤子調用方法,這時候兩個插件中的這個方法都會執行,而且遵循後註冊先執行即LIFO的原則,兩個插件的結果講義列表的形式返回 results = pm.hook.myhook(arg1=1, arg2=2) print(results)
執行結果如下:
inside Plugin_2.myhook()
inside Plugin_1.myhook()
[-1, 3]
4 詳解解釋
從代碼上看比較繞,其實通俗一點理解,其實挺簡單的,可以理解為首先定義一個接口類,然後定義很多插件類,插件類想要多少就定義多少, 接口類中要定義接口,上面的例子值定義瞭一個接口,其實可以定義很多接口,在每個插件類中需要選擇接口類中的接口去實現,當然也不需要每個 都需要去實現,可以根據自己的需要有選擇的去實現。
舉個例子:
比如定義瞭一個接口類,接口類中定義瞭10個接口,同時定義瞭3個類,這三個類分別實現瞭接口類中3個接口,6個接口和10個接口,然後實例化一個插件管理的對象,實例化對象將接口類加入定義模板中,然後去註冊這三個類,註冊之後,就可以通過插件管理對象的鉤子去調用接口類中的每個方法瞭,比如調用頭三個方法,因為每個插件中都實現瞭,所以就會有三個結果,調用後面4-6的接口時,因為隻有兩個插件實現瞭,所以隻會有兩個結果返回,調用7-10的接口因為隻有一個插件類實現瞭,所以就會隻有一個結果返回,這樣插件使用起來就非常靈活,可以真正做到“熱插拔”
下面用代碼示例演示:
import pluggy # HookspecMarker 和 HookimplMarker 實質上是一個裝飾器帶參數的裝飾器類,作用是給函數增加額外的屬性設置 hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # 定義自己的Spec,這裡可以理解為定義接口類 class MySpec: # hookspec 是一個裝飾類中的方法的裝飾器,為此方法增額外的屬性設置,這裡myhook可以理解為定義瞭3個接口 @hookspec def myhook1(self, arg1, arg2): pass @hookspec def myhook2(self, arg1, arg2): pass @hookspec def myhook3(self, arg1, arg2): pass # 定義瞭一個插件 class Plugin_1: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的和 @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 # 定義第二個插件 class Plugin_2: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @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 # 實例化一個插件管理的對象,註意這裡的名稱要與文件開頭定義裝飾器的時候的名稱一致 pm = pluggy.PluginManager("myproject") # 將自定義的接口類加到鉤子定義中去 pm.add_hookspecs(MySpec) # 註冊定義的兩個插件 pm.register(Plugin_1()) pm.register(Plugin_2()) # 通過插件管理對象的鉤子調用方法,這時候兩個插件中的這個方法都會執行,而且遵循後註冊先執行即LIFO的原則,兩個插件的結果講義列表的形式返回 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)
執行結果如下:
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 因為兩個插件都實現瞭,所有返回兩個結果,而且是倒序的
- 2)myhook3 因為隻有插件2實現瞭,所以隻有一個返回結果
- 3)myhook4 在spec中未定義,這裡卻也有結果,目前理解可能是pluggy的bug,待後續看源碼後解釋
5 HookspeckMarker裝飾器支持傳入一些特定的參數
當傳入firstresult=True時,獲取第一個plugin執行結果後就停止繼續執行
import pluggy # HookspecMarker 和 HookimplMarker 實質上是一個裝飾器帶參數的裝飾器類,作用是給函數增加額外的屬性設置 hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # 定義自己的Spec,這裡可以理解為定義接口類 class MySpec: # hookspec 是一個裝飾類中的方法的裝飾器,為此方法增額外的屬性設置,這裡myhook可以理解為定義瞭一個接口 @hookspec(firstresult=True) def myhook(self, arg1, arg2): pass # 定義瞭一個插件 class Plugin_1: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的和 @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # 定義第二個插件 class Plugin_2: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # 實例化一個插件管理的對象,註意這裡的名稱要與文件開頭定義裝飾器的時候的名稱一致 pm = pluggy.PluginManager("myproject") # 將自定義的接口類加到鉤子定義中去 pm.add_hookspecs(MySpec) # 註冊定義的兩個插件 pm.register(Plugin_1()) pm.register(Plugin_2()) # 通過插件管理對象的鉤子調用方法,這時候兩個插件中的這個方法都會執行,而且遵循後註冊先執行即LIFO的原則,兩個插件的結果講義列表的形式返回 results = pm.hook.myhook(arg1=1, arg2=2) print(results)
執行結果如下:
inside Plugin_2.myhook()
-1
6 HookImplMarker裝飾器也支持傳入一些特定的參數
常用的有tryfirst和trylast以及hookwrapper
- 當傳入tryfirst=True時,表示這個類的hook函數會優先執行,其他的仍然按照後進先出的順序執行
import pluggy # HookspecMarker 和 HookimplMarker 實質上是一個裝飾器帶參數的裝飾器類,作用是給函數增加額外的屬性設置 hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # 定義自己的Spec,這裡可以理解為定義接口類 class MySpec: # hookspec 是一個裝飾類中的方法的裝飾器,為此方法增額外的屬性設置,這裡myhook可以理解為定義瞭一個接口 @hookspec def myhook(self, arg1, arg2): pass # 定義瞭一個插件 class Plugin_1: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的和 @hookimpl(tryfirst=True) def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # 定義第二個插件 class Plugin_2: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @hookimpl def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # 定義第三個插件 class Plugin_3: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @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()) # 通過插件管理對象的鉤子調用方法,這時候兩個插件中的這個方法都會執行,而且遵循後註冊先執行即LIFO的原則,兩個插件的結果講義列表的形式返回 results = pm.hook.myhook(arg1=1, arg2=2) print(results)
執行結果讓如下:
inside Plugin_1.myhook()
inside Plugin_3.myhook()
inside Plugin_2.myhook()
[3, 9, -1]
- 當傳入trylast=True,表示當前插件的hook函數會盡可能晚的執行,其他的仍然按照後進先出的順序執行
import pluggy # HookspecMarker 和 HookimplMarker 實質上是一個裝飾器帶參數的裝飾器類,作用是給函數增加額外的屬性設置 hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # 定義自己的Spec,這裡可以理解為定義接口類 class MySpec: # hookspec 是一個裝飾類中的方法的裝飾器,為此方法增額外的屬性設置,這裡myhook可以理解為定義瞭一個接口 @hookspec def myhook(self, arg1, arg2): pass # 定義瞭一個插件 class Plugin_1: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的和 @hookimpl() def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # 定義第二個插件 class Plugin_2: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @hookimpl(trylast=True) def myhook(self, arg1, arg2): print("inside Plugin_2.myhook()") return arg1 - arg2 # 定義第三個插件 class Plugin_3: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @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()) # 通過插件管理對象的鉤子調用方法,這時候兩個插件中的這個方法都會執行,而且遵循後註冊先執行即LIFO的原則,兩個插件的結果講義列表的形式返回 results = pm.hook.myhook(arg1=1, arg2=2) print(results)
執行結果如下:
inside Plugin_3.myhook()
inside Plugin_1.myhook()
inside Plugin_2.myhook()
[9, 3, -1]
- 當傳入hookwrapper=True時,需要在這個plugin中實現一個yield,plugin先執行yield
之前的代碼,然後去執行其他的pluggin,然後再回來執行yield之後的代碼,同時通過yield可以 獲取到其他插件執行的結果
import pluggy # HookspecMarker 和 HookimplMarker 實質上是一個裝飾器帶參數的裝飾器類,作用是給函數增加額外的屬性設置 hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") # 定義自己的Spec,這裡可以理解為定義接口類 class MySpec: # hookspec 是一個裝飾類中的方法的裝飾器,為此方法增額外的屬性設置,這裡myhook可以理解為定義瞭一個接口 @hookspec def myhook(self, arg1, arg2): pass # 定義瞭一個插件 class Plugin_1: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的和 @hookimpl() def myhook(self, arg1, arg2): print("inside Plugin_1.myhook()") return arg1 + arg2 # 定義第二個插件 class Plugin_2: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @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...") # 定義第三個插件 class Plugin_3: # 插件中實現瞭上面定義的接口,同樣這個實現接口的方法用 hookimpl裝飾器裝飾,功能是返回兩個參數的差 @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()) # 通過插件管理對象的鉤子調用方法,這時候兩個插件中的這個方法都會執行,而且遵循後註冊先執行即LIFO的原則,兩個插件的結果講義列表的形式返回 results = pm.hook.myhook(arg1=1, arg2=2) print(results)
執行結果如下:
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模塊的用法的文章就介紹到這瞭,更多相關Python pluggy模塊內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Python測試框架pytest核心庫pluggy詳解
- Python 生成器yield原理及用法
- pytorch中的hook機制register_forward_hook
- Python的數據類型與標識符和判斷語句詳解
- 深入理解python協程