|
2 | 2 | Benchmarking and performance tests. |
3 | 3 | """ |
4 | 4 |
|
| 5 | +import inspect |
| 6 | +import sys |
5 | 7 | from typing import Any |
6 | 8 |
|
7 | 9 | import pytest |
|
11 | 13 | from pluggy import PluginManager |
12 | 14 | from pluggy._callers import _multicall |
13 | 15 | from pluggy._hooks import HookImpl |
| 16 | +from pluggy._hooks import varnames |
| 17 | + |
| 18 | + |
| 19 | +_PYPY = hasattr(sys, "pypy_version_info") |
| 20 | + |
| 21 | + |
| 22 | +def _varnames_legacy(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: |
| 23 | + """Pre-PEP 649 implementation using inspect.signature for comparison.""" |
| 24 | + if inspect.isclass(func): |
| 25 | + try: |
| 26 | + func = func.__init__ |
| 27 | + except AttributeError: |
| 28 | + return (), () |
| 29 | + elif not inspect.isroutine(func): |
| 30 | + try: |
| 31 | + func = getattr(func, "__call__", func) |
| 32 | + except Exception: |
| 33 | + return (), () |
| 34 | + |
| 35 | + try: |
| 36 | + sig = inspect.signature( |
| 37 | + func.__func__ if inspect.ismethod(func) else func # type: ignore[arg-type] |
| 38 | + ) |
| 39 | + except TypeError: |
| 40 | + return (), () |
| 41 | + |
| 42 | + _valid_param_kinds = ( |
| 43 | + inspect.Parameter.POSITIONAL_ONLY, |
| 44 | + inspect.Parameter.POSITIONAL_OR_KEYWORD, |
| 45 | + ) |
| 46 | + _valid_params = { |
| 47 | + name: param |
| 48 | + for name, param in sig.parameters.items() |
| 49 | + if param.kind in _valid_param_kinds |
| 50 | + } |
| 51 | + args = tuple(_valid_params) |
| 52 | + defaults = ( |
| 53 | + tuple( |
| 54 | + param.default |
| 55 | + for param in _valid_params.values() |
| 56 | + if param.default is not param.empty |
| 57 | + ) |
| 58 | + or None |
| 59 | + ) |
| 60 | + |
| 61 | + if defaults: |
| 62 | + index = -len(defaults) |
| 63 | + args, kwargs = args[:index], tuple(args[index:]) |
| 64 | + else: |
| 65 | + kwargs = () |
| 66 | + |
| 67 | + if not _PYPY: |
| 68 | + implicit_names: tuple[str, ...] = ("self",) |
| 69 | + else: |
| 70 | + implicit_names = ("self", "obj") |
| 71 | + if args: |
| 72 | + qualname: str = getattr(func, "__qualname__", "") |
| 73 | + if inspect.ismethod(func) or ("." in qualname and args[0] in implicit_names): |
| 74 | + args = args[1:] |
| 75 | + |
| 76 | + return args, kwargs |
14 | 77 |
|
15 | 78 |
|
16 | 79 | hookspec = HookspecMarker("example") |
@@ -106,3 +169,30 @@ def fun(self): |
106 | 169 | pm.register(PluginWrap(i), name=f"wrap_plug_{i}") |
107 | 170 |
|
108 | 171 | benchmark(pm.hook.fun, hooks=pm.hook, nesting=nesting) |
| 172 | + |
| 173 | + |
| 174 | +def _plain_func(x: int, y: str, z: float = 1.0) -> None: |
| 175 | + pass |
| 176 | + |
| 177 | + |
| 178 | +class _MethodHolder: |
| 179 | + def method(self, x: int, y: str, z: float = 1.0) -> None: |
| 180 | + pass |
| 181 | + |
| 182 | + |
| 183 | +_varnames_funcs = [ |
| 184 | + pytest.param(_plain_func, id="plain_function"), |
| 185 | + pytest.param(_MethodHolder.method, id="unbound_method"), |
| 186 | + pytest.param(_MethodHolder().method, id="bound_method"), |
| 187 | + pytest.param(_MethodHolder, id="class"), |
| 188 | +] |
| 189 | + |
| 190 | + |
| 191 | +@pytest.mark.parametrize("func", _varnames_funcs) |
| 192 | +def test_varnames(benchmark, func: object) -> None: |
| 193 | + benchmark(varnames, func) |
| 194 | + |
| 195 | + |
| 196 | +@pytest.mark.parametrize("func", _varnames_funcs) |
| 197 | +def test_varnames_legacy(benchmark, func: object) -> None: |
| 198 | + benchmark(_varnames_legacy, func) |
0 commit comments