From a5aacc07a9d0974eae6d532cca5a1ce2bf7ad672 Mon Sep 17 00:00:00 2001 From: Vizonex Date: Mon, 23 Mar 2026 23:52:29 -0500 Subject: [PATCH 1/5] create an optimized shortcut if __signature__ is seen --- src/pluggy/_hooks.py | 74 ++++++++++++++++++++++++++++---------------- 1 file changed, 48 insertions(+), 26 deletions(-) diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py index cdd3085c..03dd1065 100644 --- a/src/pluggy/_hooks.py +++ b/src/pluggy/_hooks.py @@ -12,6 +12,7 @@ import inspect import sys from types import ModuleType +from types import MethodType from typing import Any from typing import Final from typing import final @@ -289,32 +290,23 @@ def normalize_hookimpl_opts(opts: HookimplOpts) -> None: _PYPY = hasattr(sys, "pypy_version_info") +def _varnames_from_code(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: + """Faster shortcut than needing to parse a function's given signature.""" + code = func.__code__ + pos_count = code.co_argcount + args = code.co_varnames[:pos_count] -def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: - """Return tuple of positional and keywrord argument names for a function, - method, class or callable. - - In case of a class, its ``__init__`` method is considered. - For methods the ``self`` parameter is not included. - """ - if inspect.isclass(func): - try: - func = func.__init__ - except AttributeError: # pragma: no cover - pypy special case - return (), () - elif not inspect.isroutine(func): # callable object? - try: - func = getattr(func, "__call__", func) - except Exception: # pragma: no cover - pypy special case - return (), () + if defaults := func.__defaults__: + index = -len(defaults) + return args[:index], tuple(args[index:]) + else: + return args, () - try: - # func MUST be a function or method here or we won't parse any args. - sig = inspect.signature( - func.__func__ if inspect.ismethod(func) else func # type:ignore[arg-type] - ) - except TypeError: # pragma: no cover - return (), () +def _varnames_from_signature(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: + """extracts from a function's given signature""" + sig = inspect.signature( + func # type:ignore[arg-type] + ) _valid_param_kinds = ( inspect.Parameter.POSITIONAL_ONLY, @@ -337,9 +329,39 @@ def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: if defaults: index = -len(defaults) - args, kwargs = args[:index], tuple(args[index:]) + return args[:index], tuple(args[index:]) else: - kwargs = () + return args, () + +def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: + """Return tuple of positional and keywrord argument names for a function, + method, class or callable. + + In case of a class, its ``__init__`` method is considered. + For methods the ``self`` parameter is not included. + """ + if inspect.isclass(func): + try: + func = func.__init__ + except AttributeError: # pragma: no cover - pypy special case + return (), () + elif not inspect.isroutine(func): # callable object? + try: + func = getattr(func, "__call__", func) + except Exception: # pragma: no cover - pypy special case + return (), () + + try: + # func MUST be a function or method here or we won't parse any args. + func = func.__func__ if inspect.ismethod(func) else func + if hasattr(func, "__signature__"): + # Take the optimized approch rather than sit and parse the given signature. + args, kwargs = _varnames_from_code(func) + else: + # Fallback + args, kwargs = _varnames_from_signature(func) # type:ignore[arg-type] + except TypeError: # pragma: no cover + return (), () # strip any implicit instance arg # pypy3 uses "obj" instead of "self" for default dunder methods From 3916553d931c327b4f58da02edf0a43f67a5b904 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 04:56:29 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/pluggy/_hooks.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py index 03dd1065..9ebdbd52 100644 --- a/src/pluggy/_hooks.py +++ b/src/pluggy/_hooks.py @@ -12,7 +12,6 @@ import inspect import sys from types import ModuleType -from types import MethodType from typing import Any from typing import Final from typing import final @@ -290,6 +289,7 @@ def normalize_hookimpl_opts(opts: HookimplOpts) -> None: _PYPY = hasattr(sys, "pypy_version_info") + def _varnames_from_code(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: """Faster shortcut than needing to parse a function's given signature.""" code = func.__code__ @@ -302,10 +302,11 @@ def _varnames_from_code(func: object) -> tuple[tuple[str, ...], tuple[str, ...]] else: return args, () -def _varnames_from_signature(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: + +def _varnames_from_signature(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: """extracts from a function's given signature""" sig = inspect.signature( - func # type:ignore[arg-type] + func # type:ignore[arg-type] ) _valid_param_kinds = ( @@ -333,6 +334,7 @@ def _varnames_from_signature(func: object) -> tuple[tuple[str, ...], tuple[str, else: return args, () + def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: """Return tuple of positional and keywrord argument names for a function, method, class or callable. From 8f723b4ec047b49908e7b359ec532a0ec6fad5f4 Mon Sep 17 00:00:00 2001 From: Vizonex Date: Mon, 23 Mar 2026 23:57:21 -0500 Subject: [PATCH 3/5] fix with ruff --- src/pluggy/_hooks.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py index 03dd1065..e62e7d9d 100644 --- a/src/pluggy/_hooks.py +++ b/src/pluggy/_hooks.py @@ -12,7 +12,6 @@ import inspect import sys from types import ModuleType -from types import MethodType from typing import Any from typing import Final from typing import final From cfc2fdcf6b01977cdeb702576e031c65adc819e2 Mon Sep 17 00:00:00 2001 From: Vizonex Date: Tue, 24 Mar 2026 00:07:46 -0500 Subject: [PATCH 4/5] make sure pre-commit remains happy --- src/pluggy/_hooks.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py index 9ebdbd52..2f1744fe 100644 --- a/src/pluggy/_hooks.py +++ b/src/pluggy/_hooks.py @@ -11,6 +11,7 @@ from collections.abc import Set import inspect import sys +from types import CodeType from types import ModuleType from typing import Any from typing import Final @@ -292,11 +293,11 @@ def normalize_hookimpl_opts(opts: HookimplOpts) -> None: def _varnames_from_code(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: """Faster shortcut than needing to parse a function's given signature.""" - code = func.__code__ + code: CodeType = getattr(func, "__code__") pos_count = code.co_argcount args = code.co_varnames[:pos_count] - if defaults := func.__defaults__: + if defaults := getattr(func, "__defaults__", None): index = -len(defaults) return args[:index], tuple(args[index:]) else: @@ -356,12 +357,12 @@ def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: try: # func MUST be a function or method here or we won't parse any args. func = func.__func__ if inspect.ismethod(func) else func - if hasattr(func, "__signature__"): + if hasattr(func, "__signature__") and hasattr(func, "__code__"): # Take the optimized approch rather than sit and parse the given signature. args, kwargs = _varnames_from_code(func) else: # Fallback - args, kwargs = _varnames_from_signature(func) # type:ignore[arg-type] + args, kwargs = _varnames_from_signature(func) except TypeError: # pragma: no cover return (), () From 5ef67457022bccfe5a8f744ea4ea77c3892e1231 Mon Sep 17 00:00:00 2001 From: Vizonex Date: Tue, 24 Mar 2026 00:28:50 -0500 Subject: [PATCH 5/5] ensure _varnames_from_code is being reached. --- src/pluggy/_hooks.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py index 2f1744fe..33d26668 100644 --- a/src/pluggy/_hooks.py +++ b/src/pluggy/_hooks.py @@ -357,9 +357,9 @@ def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: try: # func MUST be a function or method here or we won't parse any args. func = func.__func__ if inspect.ismethod(func) else func - if hasattr(func, "__signature__") and hasattr(func, "__code__"): + if hasattr(func, "__code__") and inspect.isroutine(func): # Take the optimized approch rather than sit and parse the given signature. - args, kwargs = _varnames_from_code(func) + args, kwargs = _varnames_from_code(inspect.unwrap(func)) else: # Fallback args, kwargs = _varnames_from_signature(func)