Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/pluggy/_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@
]
_HookImplFunction: TypeAlias = Callable[..., _T | Generator[None, Result[_T], None]]

if sys.version_info >= (3, 14):
from annotationlib import Format

def _signature(func: Callable[..., object]) -> inspect.Signature:
return inspect.signature(func, annotation_format=Format.STRING)
else:

def _signature(func: Callable[..., object]) -> inspect.Signature:
return inspect.signature(func)


class HookspecOpts(TypedDict):
"""Options for a hook specification."""
Expand Down Expand Up @@ -310,7 +320,7 @@ 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.
sig = inspect.signature(
sig = _signature(
func.__func__ if inspect.ismethod(func) else func # type:ignore[arg-type]
)
except TypeError: # pragma: no cover
Expand Down
3 changes: 2 additions & 1 deletion src/pluggy/_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from ._hooks import _HookImplFunction
from ._hooks import _Namespace
from ._hooks import _Plugin
from ._hooks import _signature
from ._hooks import _SubsetHookCaller
from ._hooks import HookCaller
from ._hooks import HookImpl
Expand Down Expand Up @@ -522,4 +523,4 @@ def subset_hook_caller(


def _formatdef(func: Callable[..., object]) -> str:
return f"{func.__name__}{inspect.signature(func)}"
return f"{func.__name__}{_signature(func)}"
33 changes: 33 additions & 0 deletions testing/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
from collections.abc import Callable
from functools import wraps
import sys
from typing import Any
from typing import cast
from typing import TYPE_CHECKING
from typing import TypeVar

from pluggy._hooks import varnames
from pluggy._manager import _formatdef


if TYPE_CHECKING:
# Cannot use typing.Tuple due to pyupgrade replacing it with tuple
from non_existent_module import Tuple


def test_varnames() -> None:
def f(x) -> None:
i = 3 # noqa #pragma: no cover
Expand Down Expand Up @@ -90,6 +97,32 @@ def function4(arg1, *args, **kwargs):
assert _formatdef(function4) == "function4(arg1, *args, **kwargs)"


def test_varnames_with_annotations() -> None:
if sys.version_info >= (3, 14):

def hook(arg1: Tuple[int]) -> None: # type: ignore[no-any-unimported]
pass
else:

def hook(arg1: "Tuple[int]") -> "None": # type: ignore[no-any-unimported]
pass

assert varnames(hook) == (("arg1",), ())


def test_formatdef_with_annotations() -> None:
if sys.version_info >= (3, 14):

def hook(arg1: Tuple[int]) -> None: # type: ignore[no-any-unimported]
pass
else:

def hook(arg1: "Tuple[int]") -> "None": # type: ignore[no-any-unimported]
pass

assert _formatdef(hook) == "hook(arg1: 'Tuple[int]') -> 'None'"


def test_varnames_decorator() -> None:
F = TypeVar("F", bound=Callable[..., Any])

Expand Down
Loading