ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【pytest框架源码分析四】pluggy源码分析之hook实行 [打印本页]

作者: 石小疯    时间: 10 小时前
标题: 【pytest框架源码分析四】pluggy源码分析之hook实行
pluggy的主要实行方法在_callers.py中,这里简单介绍下。
  1. def _multicall(
  2.     hook_name: str,
  3.     hook_impls: Sequence[HookImpl],
  4.     caller_kwargs: Mapping[str, object],
  5.     firstresult: bool,
  6. ) -> object | list[object]:
  7.     """Execute a call into multiple python functions/methods and return the
  8.     result(s).
  9.     ``caller_kwargs`` comes from HookCaller.__call__().
  10.     """
  11.     __tracebackhide__ = True
  12.     results: list[object] = []
  13.     exception = None
  14.     only_new_style_wrappers = True
  15.     try:  # run impl and wrapper setup functions in a loop
  16.         teardowns: list[Teardown] = []
  17.         try:
  18.             for hook_impl in reversed(hook_impls):
  19.                 try:
  20.                     args = [caller_kwargs[argname] for argname in hook_impl.argnames]
  21.                 except KeyError:
  22.                     for argname in hook_impl.argnames:
  23.                         if argname not in caller_kwargs:
  24.                             raise HookCallError(
  25.                                 f"hook call must provide argument {argname!r}"
  26.                             )
  27.                 if hook_impl.hookwrapper:
  28.                     only_new_style_wrappers = False
  29.                     try:
  30.                         # If this cast is not valid, a type error is raised below,
  31.                         # which is the desired response.
  32.                         res = hook_impl.function(*args)
  33.                         wrapper_gen = cast(Generator[None, Result[object], None], res)
  34.                         next(wrapper_gen)  # first yield
  35.                         teardowns.append((wrapper_gen, hook_impl))
  36.                     except StopIteration:
  37.                         _raise_wrapfail(wrapper_gen, "did not yield")
  38.                 elif hook_impl.wrapper:
  39.                     try:
  40.                         # If this cast is not valid, a type error is raised below,
  41.                         # which is the desired response.
  42.                         res = hook_impl.function(*args)
  43.                         function_gen = cast(Generator[None, object, object], res)
  44.                         next(function_gen)  # first yield
  45.                         teardowns.append(function_gen)
  46.                     except StopIteration:
  47.                         _raise_wrapfail(function_gen, "did not yield")
  48.                 else:
  49.                     res = hook_impl.function(*args)
  50.                     if res is not None:
  51.                         results.append(res)
  52.                         if firstresult:  # halt further impl calls
  53.                             break
  54.         except BaseException as exc:
  55.             exception = exc
  56.     finally:
  57.         # Fast path - only new-style wrappers, no Result.
  58.         if only_new_style_wrappers:
  59.             if firstresult:  # first result hooks return a single value
  60.                 result = results[0] if results else None
  61.             else:
  62.                 result = results
  63.             # run all wrapper post-yield blocks
  64.             for teardown in reversed(teardowns):
  65.                 try:
  66.                     if exception is not None:
  67.                         teardown.throw(exception)  # type: ignore[union-attr]
  68.                     else:
  69.                         teardown.send(result)  # type: ignore[union-attr]
  70.                     # Following is unreachable for a well behaved hook wrapper.
  71.                     # Try to force finalizers otherwise postponed till GC action.
  72.                     # Note: close() may raise if generator handles GeneratorExit.
  73.                     teardown.close()  # type: ignore[union-attr]
  74.                 except StopIteration as si:
  75.                     result = si.value
  76.                     exception = None
  77.                     continue
  78.                 except BaseException as e:
  79.                     exception = e
  80.                     continue
  81.                 _raise_wrapfail(teardown, "has second yield")  # type: ignore[arg-type]
  82.             if exception is not None:
  83.                 raise exception.with_traceback(exception.__traceback__)
  84.             else:
  85.                 return result
  86.         # Slow path - need to support old-style wrappers.
  87.         else:
  88.             if firstresult:  # first result hooks return a single value
  89.                 outcome: Result[object | list[object]] = Result(
  90.                     results[0] if results else None, exception
  91.                 )
  92.             else:
  93.                 outcome = Result(results, exception)
  94.             # run all wrapper post-yield blocks
  95.             for teardown in reversed(teardowns):
  96.                 if isinstance(teardown, tuple):
  97.                     try:
  98.                         teardown[0].send(outcome)
  99.                     except StopIteration:
  100.                         pass
  101.                     except BaseException as e:
  102.                         _warn_teardown_exception(hook_name, teardown[1], e)
  103.                         raise
  104.                     else:
  105.                         _raise_wrapfail(teardown[0], "has second yield")
  106.                 else:
  107.                     try:
  108.                         if outcome._exception is not None:
  109.                             teardown.throw(outcome._exception)
  110.                         else:
  111.                             teardown.send(outcome._result)
  112.                         # Following is unreachable for a well behaved hook wrapper.
  113.                         # Try to force finalizers otherwise postponed till GC action.
  114.                         # Note: close() may raise if generator handles GeneratorExit.
  115.                         teardown.close()
  116.                     except StopIteration as si:
  117.                         outcome.force_result(si.value)
  118.                         continue
  119.                     except BaseException as e:
  120.                         outcome.force_exception(e)
  121.                         continue
  122.                     _raise_wrapfail(teardown, "has second yield")
  123.             return outcome.get_result()
复制代码
其前面的调用过程可参考前面几篇,这里主要介绍下_multicall方法自己。
其入参如下:
hook_name:hook的name,为我们所写方法的名称
hook_impls:hook的impl列表,impl中包括插件,插件的方法及其设置,一个方法一个impl
caller_kwargs:调用hook方法时的入参,即我们编写impl方法自己的入参
firstresult:是否只必要首次结果,bool类型
下面是方法中的正式流程
1.首先界说了个teardown的list,这个是teardown的界说。这里teardown主要是为了天生器函数预备的,在我们调用插件时,如果希望有些函数中某些步骤在所有方法最后实行,则在函数中加入yield,并且wrapper设置成true。则该函数yield前的步骤先按顺序实行,yield之后的在所有函数实行完成后实行。
  1. Teardown = Union[
  2.     Tuple[Generator[None, Result[object], None], HookImpl],
  3.     Generator[None, object, object],
  4. ]
复制代码
2.遍历hook_impls,留意这里reversed,将hook_impls反转了,这是为什么先添加的hook_impl后实行,后添加的先实行。
3.获取调用时的所有args,如果缺少参数,则报错。
4.接下来判断hookwrapper和wrapper参数是否为true,如果为true,这两个处置惩罚过程类似。都是先实行yield之前的内容,然后返回天生器函数放在teardowns里,留待所有函数实行完成再实行。如果不为true,则正常实行函数,并把返回值放到results中。如果只取第一次结果,则直接break跳出循环。
5.然后到finally,这里主要是处置惩罚天生器函数,即上方hookwrapper和wrapper参数为true的情况。
(1)如果是wrappers是true,先根据firstresult取result,然后再取teardowns中的内容。如果上面的实行有异常,则在这里抛出异常;如果没有异常,则继续实行send()方法,这一步大都会抛出异常,由于函数中只会有一个yield,如果这边不抛出异常,说明函数中有两个yeild,背面会提示报错"has second yield"。最后返回result(如果没有声明wrappers和hookwrapper参数,也是走该路径)–留意我们编写的方法中只能有一个yield。
(2)如果hookwrapper是true,也是先判断是否取firstresult,然后和上面的exception组成Result类型的outcome。接下来和上面类似,取teardowns中的内容,这里多个判断isinstance(teardown, tuple),主要和上面的处置惩罚有关,团体是一致的,有异常抛出异常,无异常则继续实行。最后返回result。
这边wrappers路径的处置惩罚过程要比hookwrapper简单。我们背面可以用wrappers参数来处置惩罚天生器函数。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4