[pytest源碼2]-pluggy準備工作

前言

簡單瞭解了pluggy之後,我們還需要再瞭解些知識,爲解讀代碼邏輯做準備
個人拙見,有錯請各位指出。
如果的我的文章對您有幫助,不符動動您的金手指給個Star,予人玫瑰,手有餘香,不勝感激。 GitHub



hook和plugin的關係

hook和plugin是1:N的對應關係,假設同時註冊了多個實現了同一hook的plugin,則會對應的返回多個結果。

Demo如下

# -*- coding:utf-8 -*-

from pluggy import PluginManager, HookspecMarker, HookimplMarker

hookspec = HookspecMarker("myPluggyDemo_2")
hookimpl = HookimplMarker("myPluggyDemo_2")


class HookSpec:
    @hookspec
    def calculate(self, a, b):
        pass


class HookImpl1:
    @hookimpl
    def calculate(self, a, b):
        return a + b


class HookImpl2:
    @hookimpl
    def calculate(self, a, b):
        return a * b
    

pm = PluginManager("myPluggyDemo_2")
pm.add_hookspecs(HookSpec)
pm.register(HookImpl1())
pm.register(HookImpl2())
print(pm.hook.calculate(a=2, b=3))

Output

[6, 5]

解析:

  • 在Demo2中,我們註冊了兩個pluginHookImpl1HookImpl2,分別實現了加法和乘法兩個邏輯。
  • 每次調用hook都會返回兩個plugin執行的結果,先執行後註冊的HookImpl2,再執行先註冊的HookImpl1,即越晚註冊的plugin越先執行。(後續會講原因)



Plugin的調用順序與參數



HookspecMarker裝飾器參數

HookspckMarker裝飾器支持傳入一些特定的參數,常用的有

  • firstresult - 如果firstresult值爲True時,獲取第一個plugin執行結果後就停止(中斷)繼續執行。
  • historic - 如果值爲True時,表示這個hook是需要保存調用記錄(call history)的,並將該調用記錄回放在未來新註冊的plugins上。

當裝飾器傳入了firstresult=True時,plugin的執行會在後註冊的HookImpl2執行完畢後停止,不再往下執行。

Demo如下

# -*- coding:utf-8 -*-

from pluggy import PluginManager, HookspecMarker, HookimplMarker

hookspec = HookspecMarker("myPluggyDemo_3")
hookimpl = HookimplMarker("myPluggyDemo_3")


class HookSpec:
    @hookspec(firstresult=True)
    def calculate(self, a, b): 
        pass


class HookImpl1:
    @hookimpl
    def calculate(self, a, b):
        return a + b            #本例子中不會執行加法邏輯


class HookImpl2:
    @hookimpl
    def calculate(self, a, b):
        return a * b


pm = PluginManager("myPluggyDemo_3")
pm.add_hookspecs(HookSpec)
pm.register(HookImpl1())
pm.register(HookImpl2())
print(pm.hook.calculate(a=2, b=3))

Output

6


HookimplMarker裝飾器參數

HookimplMarker裝飾器支持傳入一些特定的參數,常用的有

  • tryfirst - 如果tryfirst值爲True,則此plugin會儘可能早的在1:N的實現鏈路執行
  • trylast - 如果trylast值爲True,則此plugin會相應地儘可能晚的在1:N的實現鏈中執行
  • hookwrapper - 如果該參數爲True,需要在plugin內實現一個yield,plugin執行時先執行wrapper plugin前面部分的邏輯,然後轉去執行其他plugin,最後再回來執行wrapper plugin後面部分的邏輯。
  • optionalhook - 如果該參數爲True,在此plugin缺少相匹配的hook時,不會報error(spec is found)。


tryfirst的Demo

我們修改一下demo3,把HookImpl1加上tryfirst=True參數,即可達到先執行先註冊的HookImpl1。

# -*- coding:utf-8 -*-

from pluggy import PluginManager, HookspecMarker, HookimplMarker

hookspec = HookspecMarker("myPluggyDemo_4")
hookimpl = HookimplMarker("myPluggyDemo_4")


class HookSpec:
    @hookspec()
    def calculate(self, a, b):
        pass


class HookImpl1:
    @hookimpl(tryfirst=True)
    def calculate(self, a, b):
        return a + b


class HookImpl2:
    @hookimpl
    def calculate(self, a, b):
        return a * b


pm = PluginManager("myPluggyDemo_4")
pm.add_hookspecs(HookSpec)
pm.register(HookImpl1())
pm.register(HookImpl2())
print(pm.hook.calculate(a=2, b=3))

Output

[5, 6]

trylast以此類推,攜帶者變爲後執行


hookwrapper

我們實現一個特殊的pluginWrapperPlugin

# -*- coding:utf-8 -*-

from pluggy import PluginManager, HookspecMarker, HookimplMarker

hookspec = HookspecMarker("myPluggyDemo_5")
hookimpl = HookimplMarker("myPluggyDemo_5")


class HookSpec:
    @hookspec
    def calculate(self, a, b):
        pass


class HookImpl1:
    @hookimpl
    def calculate(self, a, b):
        print('HookImpl1 execute!')
        return a + b


class HookImpl2:
    @hookimpl(tryfirst=True)
    def calculate(self, a, b):
        print('HookImpl2 execute!')
        return a * b


class WrapperPluggy:
    @hookimpl(hookwrapper=True)
    def calculate(self, a, b):
        print('WrapperPluggy execute!')
        print("Before yield")
        result = yield            #此處返回的值爲其他兩個pluggy執行的結果
        print(f"After yield,result is {result.get_result()}")
        return a * b + (a + b)


pm = PluginManager("myPluggyDemo_5")
pm.add_hookspecs(HookSpec)
pm.register(HookImpl1())
pm.register(HookImpl2())
pm.register(WrapperPluggy())
print(pm.hook.calculate(a=2, b=3))

Output

WrapperPluggy execute!
Before yield
HookImpl2 execute!
HookImpl1 execute!
After yield,result is [6, 5]
[6, 5]

解析:

  • ImplWrapper中的pluggy的代碼邏輯,以result = yield 爲分割線,分成兩個部分。第一部分執行完畢後,中斷繼續執行,轉去執行其他plugin,待其他plugin都執行完時,回來繼續執行剩下的部分。
  • result = yieldresult通過yield來獲取到其他plugin執行的結果,即非wrapper plugin的執行結果(HookImpl2HookImpl1
  • 從Output中可以看出,我們WrapperPluggy的返回結果沒有被打印出來,這是因爲wrapper plugin的返回值會被Ignore,原因後續會提到。



GitHubhttps://github.com/potatoImp/pytestCodeParsing

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章