Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More steps towards exporting our typings #430

Merged
merged 7 commits into from
Aug 26, 2023
Merged
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
23 changes: 17 additions & 6 deletions docs/api_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@ API Reference

.. autodecorator:: pluggy.HookimplMarker

.. autoclass:: pluggy.Result()
:show-inheritance:
.. autoclass:: pluggy.HookRelay()
:members:

.. data:: <hook name>

:type: HookCaller

The caller for the hook with the given name.

.. autoclass:: pluggy.HookCaller()
:members:
:special-members: __call__
Expand All @@ -26,11 +31,17 @@ API Reference
:show-inheritance:
:members:

.. autoclass:: pluggy.HookRelay()
.. autoclass:: pluggy.Result()
:show-inheritance:
:members:

.. data:: <hook name>
.. autoclass:: pluggy.HookImpl()
:members:

:type: HookCaller
.. autoclass:: pluggy.HookspecOpts()
:show-inheritance:
:members:

The caller for the hook with the given name.
.. autoclass:: pluggy.HookimplOpts()
:show-inheritance:
:members:
13 changes: 13 additions & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,19 @@

autodoc_member_order = "bysource"

nitpicky = True
nitpick_ignore = {
# Don't want to expose this yet (see #428).
("py:class", "pluggy._tracing.TagTracerSub"),
# Compat hack, don't want to expose it.
("py:class", "pluggy._manager.DistFacade"),
# `types.ModuleType` turns into `module` but then fails to resolve...
("py:class", "module"),
# Just a TypeVar.
("py:obj", "pluggy._result.ResultType"),
("py:class", "pluggy._result.ResultType"),
}

# -- Options for Texinfo output -------------------------------------------

# Grouping the document tree into Texinfo files. List of tuples
Expand Down
4 changes: 2 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -966,11 +966,11 @@ The expected signature and default implementations for these functions is:

.. code-block:: python

def before(hook_name, methods, kwargs):
def before(hook_name, hook_impls, kwargs):
pass


def after(outcome, hook_name, methods, kwargs):
def after(outcome, hook_name, hook_impls, kwargs):
pass

Public API
Expand Down
10 changes: 6 additions & 4 deletions src/pluggy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@
"PluginValidationError",
"HookCaller",
"HookCallError",
"HookSpecOpts",
"HookImplOpts",
"HookspecOpts",
"HookimplOpts",
"HookImpl",
"HookRelay",
"HookspecMarker",
"HookimplMarker",
Expand All @@ -25,6 +26,7 @@
HookimplMarker,
HookCaller,
HookRelay,
HookSpecOpts,
HookImplOpts,
HookspecOpts,
HookimplOpts,
HookImpl,
)
66 changes: 47 additions & 19 deletions src/pluggy/_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from typing import Any
from typing import Callable
from typing import Final
from typing import final
from typing import Generator
from typing import List
from typing import Mapping
Expand All @@ -37,7 +38,7 @@
_HookImplFunction = Callable[..., Union[_T, Generator[None, Result[_T], None]]]


class HookSpecOpts(TypedDict):
class HookspecOpts(TypedDict):
"""Options for a hook specification."""

#: Whether the hook is :ref:`first result only <firstresult>`.
Expand All @@ -48,7 +49,7 @@
warn_on_impl: Warning | None


class HookImplOpts(TypedDict):
class HookimplOpts(TypedDict):
"""Options for a hook implementation."""

#: Whether the hook implementation is a :ref:`wrapper <hookwrapper>`.
Expand All @@ -69,6 +70,7 @@
specname: str | None


@final
class HookspecMarker:
"""Decorator for marking functions as hook specifications.

Expand Down Expand Up @@ -132,7 +134,7 @@
def setattr_hookspec_opts(func: _F) -> _F:
if historic and firstresult:
raise ValueError("cannot have a historic firstresult hook")
opts: HookSpecOpts = {
opts: HookspecOpts = {
"firstresult": firstresult,
"historic": historic,
"warn_on_impl": warn_on_impl,
Expand All @@ -146,6 +148,7 @@
return setattr_hookspec_opts


@final
class HookimplMarker:
"""Decorator for marking functions as hook implementations.

Expand Down Expand Up @@ -247,7 +250,7 @@
"""

def setattr_hookimpl_opts(func: _F) -> _F:
opts: HookImplOpts = {
opts: HookimplOpts = {
"wrapper": wrapper,
"hookwrapper": hookwrapper,
"optionalhook": optionalhook,
Expand All @@ -264,7 +267,7 @@
return setattr_hookimpl_opts(function)


def normalize_hookimpl_opts(opts: HookImplOpts) -> None:
def normalize_hookimpl_opts(opts: HookimplOpts) -> None:
opts.setdefault("tryfirst", False)
opts.setdefault("trylast", False)
opts.setdefault("wrapper", False)
Expand Down Expand Up @@ -341,6 +344,7 @@
return args, kwargs


@final
class HookRelay:
"""Hook holder object for performing 1:N hook calls where N is the number
of registered plugins."""
Expand Down Expand Up @@ -379,13 +383,15 @@
name: str,
hook_execute: _HookExec,
specmodule_or_class: _Namespace | None = None,
spec_opts: HookSpecOpts | None = None,
spec_opts: HookspecOpts | None = None,
) -> None:
""":meta private:"""
#: Name of the hook getting called.

Check warning on line 389 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L389

Added line #L389 was not covered by tests
self.name: Final = name
self._hookexec: Final = hook_execute
self._hookimpls: Final[list[HookImpl]] = []
self._call_history: _CallHistory | None = None
# TODO: Document, or make private.
self.spec: HookSpec | None = None
if specmodule_or_class is not None:
assert spec_opts is not None
Expand All @@ -399,7 +405,7 @@
def set_specification(
self,
specmodule_or_class: _Namespace,
spec_opts: HookSpecOpts,
spec_opts: HookspecOpts,
) -> None:
if self.spec is not None:
raise ValueError(
Expand Down Expand Up @@ -522,7 +528,7 @@
not self.is_historic()
), "Cannot directly call a historic hook - use call_historic instead."
self._verify_all_args_are_provided(kwargs)
opts: HookImplOpts = {
opts: HookimplOpts = {
"wrapper": False,
"hookwrapper": False,
"optionalhook": False,
Expand Down Expand Up @@ -606,7 +612,10 @@
return f"<_SubsetHookCaller {self.name!r}>"


@final
class HookImpl:
"""A hook implementation in a :class:`HookCaller`."""

__slots__ = (
"function",
"argnames",
Expand All @@ -626,23 +635,42 @@
plugin: _Plugin,
plugin_name: str,
function: _HookImplFunction[object],
hook_impl_opts: HookImplOpts,
hook_impl_opts: HookimplOpts,
) -> None:
""":meta private:"""
#: The hook implementation function.
self.function: Final = function
self.argnames, self.kwargnames = varnames(self.function)
self.plugin = plugin
self.opts = hook_impl_opts
self.plugin_name = plugin_name
self.wrapper = hook_impl_opts["wrapper"]
self.hookwrapper = hook_impl_opts["hookwrapper"]
self.optionalhook = hook_impl_opts["optionalhook"]
self.tryfirst = hook_impl_opts["tryfirst"]
self.trylast = hook_impl_opts["trylast"]
argnames, kwargnames = varnames(self.function)
#: The positional parameter names of ``function```.
self.argnames: Final = argnames
#: The keyword parameter names of ``function```.
self.kwargnames: Final = kwargnames
#: The plugin which defined this hook implementation.
self.plugin: Final = plugin
#: The :class:`HookimplOpts` used to configure this hook implementation.
self.opts: Final = hook_impl_opts
#: The name of the plugin which defined this hook implementation.
self.plugin_name: Final = plugin_name
#: Whether the hook implementation is a :ref:`wrapper <hookwrapper>`.
self.wrapper: Final = hook_impl_opts["wrapper"]
#: Whether the hook implementation is an :ref:`old-style wrapper
#: <old_style_hookwrappers>`.
self.hookwrapper: Final = hook_impl_opts["hookwrapper"]
#: Whether validation against a hook specification is :ref:`optional
#: <optionalhook>`.
self.optionalhook: Final = hook_impl_opts["optionalhook"]
#: Whether to try to order this hook implementation :ref:`first
#: <callorder>`.
self.tryfirst: Final = hook_impl_opts["tryfirst"]
#: Whether to try to order this hook implementation :ref:`last
#: <callorder>`.
self.trylast: Final = hook_impl_opts["trylast"]

def __repr__(self) -> str:
return f"<HookImpl plugin_name={self.plugin_name!r}, plugin={self.plugin!r}>"


@final
class HookSpec:
__slots__ = (
"namespace",
Expand All @@ -654,7 +682,7 @@
"warn_on_impl",
)

def __init__(self, namespace: _Namespace, name: str, opts: HookSpecOpts) -> None:
def __init__(self, namespace: _Namespace, name: str, opts: HookspecOpts) -> None:
self.namespace = namespace
self.function: Callable[..., object] = getattr(namespace, name)
self.name = name
Expand Down
21 changes: 10 additions & 11 deletions src/pluggy/_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@
from ._hooks import _SubsetHookCaller
from ._hooks import HookCaller
from ._hooks import HookImpl
from ._hooks import HookImplOpts
from ._hooks import HookimplOpts
from ._hooks import HookRelay
from ._hooks import HookSpec
from ._hooks import HookSpecOpts
from ._hooks import HookspecOpts
from ._hooks import normalize_hookimpl_opts
from ._result import Result

Expand Down Expand Up @@ -92,12 +91,12 @@ class PluginManager:

def __init__(self, project_name: str) -> None:
#: The project name.
self.project_name: Final[str] = project_name
self.project_name: Final = project_name
self._name2plugin: Final[dict[str, _Plugin]] = {}
self._plugin_distinfo: Final[list[tuple[_Plugin, DistFacade]]] = []
#: The "hook relay", used to call a hook on all registered plugins.
#: See :ref:`calling`.
self.hook: Final[HookRelay] = HookRelay()
self.hook: Final = HookRelay()
#: The tracing entry point. See :ref:`tracing`.
self.trace: Final[_tracing.TagTracerSub] = _tracing.TagTracer().get(
"pluginmanage"
Expand Down Expand Up @@ -166,7 +165,7 @@ def register(self, plugin: _Plugin, name: str | None = None) -> str | None:
hook._add_hookimpl(hookimpl)
return plugin_name

def parse_hookimpl_opts(self, plugin: _Plugin, name: str) -> HookImplOpts | None:
def parse_hookimpl_opts(self, plugin: _Plugin, name: str) -> HookimplOpts | None:
"""Try to obtain a hook implementation from an item with the given name
in the given plugin which is being searched for hook impls.

Expand All @@ -175,13 +174,13 @@ def parse_hookimpl_opts(self, plugin: _Plugin, name: str) -> HookImplOpts | None

This method can be overridden by ``PluginManager`` subclasses to
customize how hook implementation are picked up. By default, returns the
options for items decorated with :class:`HookImplMarker`.
options for items decorated with :class:`HookimplMarker`.
"""
method: object = getattr(plugin, name)
if not inspect.isroutine(method):
return None
try:
res: HookImplOpts | None = getattr(
res: HookimplOpts | None = getattr(
method, self.project_name + "_impl", None
)
except Exception:
Expand Down Expand Up @@ -260,7 +259,7 @@ def add_hookspecs(self, module_or_class: _Namespace) -> None:

def parse_hookspec_opts(
self, module_or_class: _Namespace, name: str
) -> HookSpecOpts | None:
) -> HookspecOpts | None:
"""Try to obtain a hook specification from an item with the given name
in the given module or class which is being searched for hook specs.

Expand All @@ -272,8 +271,8 @@ def parse_hookspec_opts(
customize how hook specifications are picked up. By default, returns the
options for items decorated with :class:`HookspecMarker`.
"""
method: HookSpec = getattr(module_or_class, name)
opts: HookSpecOpts | None = getattr(method, self.project_name + "_spec", None)
method = getattr(module_or_class, name)
opts: HookspecOpts | None = getattr(method, self.project_name + "_spec", None)
return opts

def get_plugins(self) -> set[Any]:
Expand Down
2 changes: 2 additions & 0 deletions src/pluggy/_result.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from types import TracebackType
from typing import Callable
from typing import cast
from typing import final
from typing import Generator
from typing import Generic
from typing import NoReturn
Expand Down Expand Up @@ -36,6 +37,7 @@ class HookCallError(Exception):
"""Hook was called incorrectly."""


@final
class Result(Generic[ResultType]):
"""An object used to inspect and set the result in a :ref:`hook wrapper
<hookwrappers>`."""
Expand Down