diff --git a/pyproject.toml b/pyproject.toml index 54d72958..d8648256 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -2,6 +2,7 @@ requires = [ "setuptools>=65.0", "setuptools-scm[toml]>=8.0", + "mypy>=1.15", ] build-backend = "setuptools.build_meta" diff --git a/src/pluggy/__init__.py b/src/pluggy/__init__.py index 8a651f49..233433a7 100644 --- a/src/pluggy/__init__.py +++ b/src/pluggy/__init__.py @@ -7,7 +7,6 @@ "HookspecOpts", "HookimplOpts", "HookImpl", - "HookRelay", "HookspecMarker", "HookimplMarker", "Result", @@ -18,7 +17,6 @@ from ._hooks import HookImpl from ._hooks import HookimplMarker from ._hooks import HookimplOpts -from ._hooks import HookRelay from ._hooks import HookspecMarker from ._hooks import HookspecOpts from ._manager import PluginManager diff --git a/src/pluggy/_hookrelay.py b/src/pluggy/_hookrelay.py new file mode 100644 index 00000000..a42493bf --- /dev/null +++ b/src/pluggy/_hookrelay.py @@ -0,0 +1,27 @@ +""" +separate module for only hookrelay as mypyc doesnt support dynamic attributes/getattr +""" + +from __future__ import annotations + +from typing import final +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from pluggy import HookCaller + + +@final +class HookRelay: + """Hook holder object for performing 1:N hook calls where N is the number + of registered plugins.""" + + __slots__ = ("__dict__",) + + def __init__(self) -> None: + """:meta private:""" + + if TYPE_CHECKING: + + def __getattr__(self, name: str) -> HookCaller: ... diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py index 97fef0d7..6bc39010 100644 --- a/src/pluggy/_hooks.py +++ b/src/pluggy/_hooks.py @@ -17,12 +17,12 @@ from typing import final from typing import Optional from typing import overload -from typing import TYPE_CHECKING from typing import TypedDict from typing import TypeVar from typing import Union import warnings +from ._hookrelay import HookRelay from ._result import Result @@ -30,11 +30,12 @@ _F = TypeVar("_F", bound=Callable[..., object]) _Namespace = Union[ModuleType, type] _Plugin = object -_HookExec = Callable[ - [str, Sequence["HookImpl"], Mapping[str, object], bool], - Union[object, list[object]], + +type _HookExec = Callable[ + [str, Sequence[HookImpl], Mapping[str, object], bool], Union[object, list[object]] ] -_HookImplFunction = Callable[..., Union[_T, Generator[None, Result[_T], None]]] + +type _HookImplFunction[T] = Callable[..., Union[T, Generator[None, Result[T], None]]] class HookspecOpts(TypedDict): @@ -355,21 +356,6 @@ def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]: return args, kwargs -@final -class HookRelay: - """Hook holder object for performing 1:N hook calls where N is the number - of registered plugins.""" - - __slots__ = ("__dict__",) - - def __init__(self) -> None: - """:meta private:""" - - if TYPE_CHECKING: - - def __getattr__(self, name: str) -> HookCaller: ... - - # Historical name (pluggy<=1.2), kept for backward compatibility. _HookRelay = HookRelay @@ -388,6 +374,8 @@ class HookCaller: "_call_history", ) + _call_history: _CallHistory | None + def __init__( self, name: str, @@ -407,7 +395,7 @@ def __init__( # 5. wrappers # 6. tryfirst wrappers self._hookimpls: Final[list[HookImpl]] = [] - self._call_history: _CallHistory | None = None + self._call_history = None # TODO: Document, or make private. self.spec: HookSpec | None = None if specmodule_or_class is not None: diff --git a/src/pluggy/_manager.py b/src/pluggy/_manager.py index ff1e3ce6..170babf7 100644 --- a/src/pluggy/_manager.py +++ b/src/pluggy/_manager.py @@ -10,10 +10,12 @@ from typing import cast from typing import Final from typing import TYPE_CHECKING +from typing import TypeAlias import warnings from . import _tracing from ._callers import _multicall +from ._hookrelay import HookRelay from ._hooks import _HookImplFunction from ._hooks import _Namespace from ._hooks import _Plugin @@ -21,7 +23,6 @@ from ._hooks import HookCaller from ._hooks import HookImpl from ._hooks import HookimplOpts -from ._hooks import HookRelay from ._hooks import HookspecOpts from ._hooks import normalize_hookimpl_opts from ._result import Result @@ -32,8 +33,10 @@ import importlib.metadata -_BeforeTrace = Callable[[str, Sequence[HookImpl], Mapping[str, Any]], None] -_AfterTrace = Callable[[Result[Any], str, Sequence[HookImpl], Mapping[str, Any]], None] +_BeforeTrace: TypeAlias = Callable[[str, Sequence[HookImpl], Mapping[str, Any]], None] +_AfterTrace: TypeAlias = Callable[ + [Result[Any], str, Sequence[HookImpl], Mapping[str, Any]], None +] def _warn_for_function(warning: Warning, function: Callable[..., object]) -> None: diff --git a/src/pluggy/_result.py b/src/pluggy/_result.py index 656a5841..eb07448a 100644 --- a/src/pluggy/_result.py +++ b/src/pluggy/_result.py @@ -57,12 +57,12 @@ def exception(self) -> BaseException | None: def from_call(cls, func: Callable[[], ResultType]) -> Result[ResultType]: """:meta private:""" __tracebackhide__ = True - result = exception = None try: result = func() except BaseException as exc: - exception = exc - return cls(result, exception) + return cls(None, exc) + else: + return cls(result, None) def force_result(self, result: ResultType) -> None: """Force the result(s) to ``result``.