您好,登錄后才能下訂單哦!
這篇文章主要介紹了Python測試框架pytest核心庫pluggy怎么使用的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Python測試框架pytest核心庫pluggy怎么使用文章都會有所收獲,下面我們一起來看看吧。
import pluggy # HookspecMarker 和 HookimplMarker 實質上是一個裝飾器帶參數的裝飾器類,作用是給函數增加額外的屬性設置 hookspec = pluggy.HookspecMarker("myproject") hookimpl = pluggy.HookimplMarker("myproject") ''' HookspeckMarker: 傳入firstresult=True時,獲取第一個plugin執行結果后就停止繼續執行 @hookspec(firstresult=True) historic - 表示這個 hook 是需要保存call history 的,當有新的 plugin 注冊的時候,需要回放歷史 hookimpl: 當傳入tryfirst=True時,表示這個類的hook函數會優先執行,其他的仍然按照后進先出的順序執行 當傳入trylast=True,表示當前插件的hook函數會盡可能晚的執行,其他的仍然按照后進先出的順序執行 當傳入hookwrapper=True時,需要在這個plugin中實現一個yield,plugin先執行yield之前的代碼, 然后去執行其他的pluggin,然后再回來執行yield之后的代碼,同時通過yield可以獲取到其他插件執行的結果 ''' # 定義自己的Spec,這里可以理解為定義接口類 class MySpec: # hookspec 是一個裝飾類中的方法的裝飾器,為此方法增額外的屬性設置,這里myhook可以理解為定義了一個接口 # 會給當前方法添加屬性 鍵為 {self.project_name + "_spec"} 值是裝飾器傳入的參數 @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): out = yield 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)
初始化一些參數,如_name2plugin:存放后續注冊 plugin
將定義的類已參數的方式傳遞進去 (module_or_class)
遍歷類中全部的方法
判斷: getattr(method, self.project_name + "_spec", None),判斷類方法中是否有當前定義 myproject+spec 的屬性,
如果有則返回裝飾器所得到的參數,沒有則返回 None,其實就是判斷有沒有被@hookspec裝飾,因為裝飾了會設置上 myproject+spec 這個屬性以及相對應的值
如果有被裝飾: 判斷一下 self.hook 中是否以及存在了這個 spec
如果不存在: 創建一個_HookCaller(spec 名字,_hookexec(本質就是一個執行 hook 的方法),傳遞進來的 spec 對象,第三步獲得的參數,也就是通過裝飾器 set 到方法中的一些參數) 對象
先判斷參數是否存在,如果存在,創建一個 HookSpec 給 self.spec,傳遞參數為 當前 spec 對象,當前 spec 名字,參數,self.function 就是對應的那個被裝飾的方法,最后判斷一下這個 spec 需不需要保存歷史,如果需要,初始化一個列表
init: 判斷 spec 對象是否為空,如果不為空:
將上面初始化的對象,通過 setattr 的方式存在到 self.hook 中,名字就是被裝飾方法的名字,值是剛剛創建的對象
最后會在 names 的列表中把這個添加的方法的名字添加進去,判斷一個 names 是否為空,如果為空,則拋出異常
添加 add_hookspecs 的步驟全部完成
注冊插件 (register): 傳遞實現插件的實體類對象
判斷是否傳遞插件名字,如果沒傳,就獲取對象的name屬性,如果還沒有就直接用 id() 生產一個隨機字符串做當前對象在插件中的名字
判斷名字是否存在,或者是否已被注冊: self._name2plugin 和 self._plugin2hookcaller,前者是用 plugin_name 做 key,后者是用 plugin object 做 key,判斷是否已經注冊過重復的 plugin
self._name2plugin[plugin_name(插件名字)] = plugin(傳遞的實體類對象)
self._plugin2hookcallers[plugin(傳遞的實體類對象)] = hookcallers = [],其實就是初始化一下 self._plugin2hookcallers[plugin],因為列表的引用傳遞,所有直接修改 hookcallers 也可以作用在 self 中
遍歷實體類對象的方法列表,判斷是否被 impl 裝飾:
a.先獲取到方法對象
b.判斷對象是否是內置函數、函數、方法或者方法描述符,如果不是直接返回
c.獲取該方法的屬性 hook 對象創建時傳遞的名字 (myproject) + "_impl" ,沒有則返回 None
d.判斷獲取到的值是不是 None 并且不是一個字典,則將獲取到的 res 賦值為 None
e. 最后返回 res,其實就是 hookimpl 裝飾器,如果你不給值就給一堆默認值
先判斷參數列表是否為空: 如果不為空,進行設置默認值 (其實正常是不會出現沒有值的情況),然后從實體類對象中獲取到該方法的對象
在創建一個新的對象 (HookImpl):init(self,傳遞進來的實體類對象,hook 名字 (第一步獲取或者 id 生成),method 對象,第四步返回的參數字典),并且將參數列表跟新到 self.dict中
判斷 self.hook 中是否以及注冊了當前插件 (就是 add_hookspecs 注冊的 spec 中是否有當前方法)
如果沒有注冊,會直接注冊一個,這樣注冊 specmodule_or_class 參數會為空,意為著不會有額外的一些參數 eg:tryfirst
hook.has_spec() 判斷注冊 spec 的 spec 屬性不為空
self._verify_hook(hook(spec 對象), hookimpl(插件對象)) a.先判斷當前對象中是否有 (_call_history 屬性),歷史 和是否 需要使用 yield b. 判斷 hookimpl 和 hook.spec 的參數列表是否相等,如果不相等報錯
hook._maybe_apply_history(hookimpl)
a.判斷是否有_call_history 這個屬性
hook._add_hookimpl(hookimpl):
a.判斷是否為 hookwrapper 為 True,添加到不同的 wrappers 中
b.判斷是否有 trylast tryfirst 屬性,將 hookimpl 存放到對應位置
c.將 hook 添加到 hookcallers 中
遍歷結束后,返回 plugin_name(第一步產生)
運行插件 pm.hook.myhook(arg1=1, arg2=2):本質就是調用對象的call方法
先判斷是否有順序參數,如果有直接報錯
在判斷是否有_call_history 這個屬性
判斷實際傳入參數,是否和插件需要參數一樣
self._hookexec(self(hook 對象), self.get_hookimpls()(全部的已經注冊的插件), kwargs(傳入的參數))
self._inner_hookexec(hook(hook 對象), methods(插件), kwargs(參數))
# 實際調用,也就是hook.multicall的方法 self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall( methods, kwargs, firstresult=hook.spec.opts.get("firstresult") if hook.spec else False, )
_multicall(hook_impls(插件), caller_kwargs(參數), firstresult=False(@hookspec傳入,默認 False))
1. 先將 hook_impls 變成一個可迭代對象 (reversed(hook_impls))
2. 先把順序參數的參數列表,拿到 (列表推導式)
3. 判斷需不需要將其他插件執行的結果傳遞進去
- 需要
- 先從 hook_impl 中拿出對應的方法并且傳遞參數,執行關鍵字 yield 前面部分
- 然后 next()
- 最后將這個方法添加到 teardowns 列表中去- 不需要
- 先從 hook_impl 中拿出對于的方法并且傳遞參數
- 判斷執行后的返回值是不是為空,不為空則添加到 results 列表中
- 最后判斷是否有 firstresult 屬性,如果有直接結束循環
4. 最后執行 (finally 中代碼)
- 如果 firstresult 為 true,那么直接返回第一個插件返回的結果即可
- 執行 teardowns 列表中的需要最后執行的插件
- 通過迭代器的 send 方法,將上幾個插件的結果傳遞進去
5. 返回 result 對象 :會判斷是否有報錯 如果沒有直接返回結果列表,如果有報錯會拋出異常
關于“Python測試框架pytest核心庫pluggy怎么使用”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Python測試框架pytest核心庫pluggy怎么使用”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。