網站首頁 編程語言 正文
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]
原文鏈接:https://juejin.cn/post/7096346688188579847
相關推薦
- 2022-12-21 Docker教程之dockerfile構建centos鏡像_docker
- 2023-02-02 python保留兩位小數的3種方法實例_python
- 2022-11-27 C語言三種函數調用約定_cdecl與_stdcall及_fastcall詳細講解_C 語言
- 2022-05-26 Flutter開發實現底部留言板_Android
- 2022-12-09 ReactQuery系列React?Query?實踐示例詳解_React
- 2023-12-21 APP,微信小程序獲取本地緩存,清除 uniApp
- 2022-05-13 python實現圖書館借閱系統_python
- 2022-12-25 Python中你所不知道的星號?*?用法_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支