From e73533604f133aab2b4b72a16ef9a3405b13e5c4 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 15 Jan 2022 18:24:06 +0200 Subject: [PATCH] Some style improvements to docstrings/API Reference --- docs/api_reference.rst | 3 +- src/pluggy/_hooks.py | 75 +++++++++++++++------------- src/pluggy/_manager.py | 111 ++++++++++++++++++++++++----------------- src/pluggy/_result.py | 6 +-- 4 files changed, 107 insertions(+), 88 deletions(-) diff --git a/docs/api_reference.rst b/docs/api_reference.rst index d9552d44..7ae6bb2b 100644 --- a/docs/api_reference.rst +++ b/docs/api_reference.rst @@ -1,12 +1,11 @@ :orphan: -Api Reference +API Reference ============= .. automodule:: pluggy :members: :undoc-members: - :show-inheritance: .. autoclass:: pluggy._callers._Result .. automethod:: pluggy._callers._Result.get_result diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py index 403c39d3..d38cece7 100644 --- a/src/pluggy/_hooks.py +++ b/src/pluggy/_hooks.py @@ -53,11 +53,11 @@ class _HookImplOpts(TypedDict): class HookspecMarker: - """Decorator helper class for marking functions as hook specifications. + """Decorator for marking functions as hook specifications. - You can instantiate it with a project_name to get a decorator. - Calling :py:meth:`.PluginManager.add_hookspecs` later will discover all marked functions - if the :py:class:`.PluginManager` uses the same project_name. + Instantiate it with a project_name to get a decorator. + Calling :meth:`PluginManager.add_hookspecs` later will discover all marked + functions if the :class:`PluginManager` uses the same project_name. """ __slots__ = ("project_name",) @@ -92,18 +92,18 @@ def __call__( # noqa: F811 historic: bool = False, warn_on_impl: Optional[Warning] = None, ) -> Union[_F, Callable[[_F], _F]]: - """if passed a function, directly sets attributes on the function - which will make it discoverable to :py:meth:`.PluginManager.add_hookspecs`. - If passed no function, returns a decorator which can be applied to a function - later using the attributes supplied. + """If passed a function, directly sets attributes on the function + which will make it discoverable to :meth:`PluginManager.add_hookspecs`. - If ``firstresult`` is ``True`` the 1:N hook call (N being the number of registered - hook implementation functions) will stop at I<=N when the I'th function - returns a non-``None`` result. + If passed no function, returns a decorator which can be applied to a + function later using the attributes supplied. - If ``historic`` is ``True`` calls to a hook will be memorized and replayed - on later registered plugins. + If ``firstresult`` is ``True``, the 1:N hook call (N being the number of + registered hook implementation functions) will stop at I<=N when the + I'th function returns a non-``None`` result. + If ``historic`` is ``True``, every call to the hook will be memorized + and replayed on plugins registered after the call was made. """ def setattr_hookspec_opts(func: _F) -> _F: @@ -124,11 +124,11 @@ def setattr_hookspec_opts(func: _F) -> _F: class HookimplMarker: - """Decorator helper class for marking functions as hook implementations. + """Decorator for marking functions as hook implementations. - You can instantiate with a ``project_name`` to get a decorator. - Calling :py:meth:`.PluginManager.register` later will discover all marked functions - if the :py:class:`.PluginManager` uses the same project_name. + Instantiate it with a ``project_name`` to get a decorator. + Calling :meth:`PluginManager.register` later will discover all marked + functions if the :class:`PluginManager` uses the same project_name. """ __slots__ = ("project_name",) @@ -169,30 +169,33 @@ def __call__( # noqa: F811 trylast: bool = False, specname: Optional[str] = None, ) -> Union[_F, Callable[[_F], _F]]: - """if passed a function, directly sets attributes on the function - which will make it discoverable to :py:meth:`.PluginManager.register`. + """If passed a function, directly sets attributes on the function + which will make it discoverable to :meth:`PluginManager.register`. + If passed no function, returns a decorator which can be applied to a function later using the attributes supplied. - If ``optionalhook`` is ``True`` a missing matching hook specification will not result - in an error (by default it is an error if no matching spec is found). - - If ``tryfirst`` is ``True`` this hook implementation will run as early as possible - in the chain of N hook implementations for a specification. + If ``optionalhook`` is ``True``, a missing matching hook specification + will not result in an error (by default it is an error if no matching + spec is found). - If ``trylast`` is ``True`` this hook implementation will run as late as possible - in the chain of N hook implementations. + If ``tryfirst`` is ``True``, this hook implementation will run as early + as possible in the chain of N hook implementations for a specification. - If ``hookwrapper`` is ``True`` the hook implementations needs to execute exactly - one ``yield``. The code before the ``yield`` is run early before any non-hookwrapper - function is run. The code after the ``yield`` is run after all non-hookwrapper - function have run. The ``yield`` receives a :py:class:`.callers._Result` object - representing the exception or result outcome of the inner calls (including other - hookwrapper calls). + If ``trylast`` is ``True``, this hook implementation will run as late as + possible in the chain of N hook implementations. - If ``specname`` is provided, it will be used instead of the function name when - matching this hook implementation to a hook specification during registration. + If ``hookwrapper`` is ``True``, the hook implementations needs to + execute exactly one ``yield``. The code before the ``yield`` is run + early before any non-hookwrapper function is run. The code after the + ``yield`` is run after all non-hookwrapper function have run The + ``yield`` receives a :class:`_Result` object representing the exception + or result outcome of the inner calls (including other hookwrapper + calls). + If ``specname`` is provided, it will be used instead of the function + name when matching this hook implementation to a hook specification + during registration. """ def setattr_hookimpl_opts(func: _F) -> _F: @@ -268,7 +271,7 @@ def varnames(func: object) -> Tuple[Tuple[str, ...], Tuple[str, ...]]: class _HookRelay: - """hook holder object for performing 1:N hook calls where N is the number + """Hook holder object for performing 1:N hook calls where N is the number of registered plugins.""" __slots__ = ("__dict__",) @@ -396,7 +399,7 @@ def call_historic( """Call the hook with given ``kwargs`` for all registered plugins and for all plugins which will be registered afterwards. - If ``result_callback`` is not ``None`` it will be called for for each + If ``result_callback`` is provided, it will be called for each non-``None`` result obtained from a hook implementation. """ assert self._call_history is not None diff --git a/src/pluggy/_manager.py b/src/pluggy/_manager.py index 3e16011e..023cac0f 100644 --- a/src/pluggy/_manager.py +++ b/src/pluggy/_manager.py @@ -58,15 +58,14 @@ def _warn_for_function(warning: Warning, function: Callable[..., object]) -> Non class PluginValidationError(Exception): - """plugin failed validation. + """Plugin failed validation. - :param object plugin: the plugin which failed validation, - may be a module or an arbitrary object. + :param plugin: The plugin which failed validation. """ def __init__(self, plugin: _Plugin, message: str) -> None: + super().__init__(message) self.plugin = plugin - super(Exception, self).__init__(message) class DistFacade: @@ -88,17 +87,16 @@ def __dir__(self) -> List[str]: class PluginManager: - """Core :py:class:`.PluginManager` class which manages registration - of plugin objects and 1:N hook calling. + """Core class which manages registration of plugin objects and 1:N hook + calling. - You can register new hooks by calling :py:meth:`add_hookspecs(module_or_class) - <.PluginManager.add_hookspecs>`. - You can register plugin objects (which contain hooks) by calling - :py:meth:`register(plugin) <.PluginManager.register>`. The :py:class:`.PluginManager` - is initialized with a prefix that is searched for in the names of the dict - of registered plugin objects. + You can register new hooks by calling :meth:`add_hookspecs(module_or_class) + `. - For debugging purposes you can call :py:meth:`.PluginManager.enable_tracing` + You can register plugin objects (which contain hook implementations) by + calling :meth:`register(plugin) `. + + For debugging purposes you can call :meth:`PluginManager.enable_tracing` which will subsequently send debug information to the trace helper. """ @@ -132,9 +130,15 @@ def _hookexec( return self._inner_hookexec(hook_name, methods, kwargs, firstresult) def register(self, plugin: _Plugin, name: Optional[str] = None) -> Optional[str]: - """Register a plugin and return its canonical name or ``None`` if the name - is blocked from registering. Raise a :py:class:`ValueError` if the plugin - is already registered.""" + """Register a plugin and return its name. + + If a name is not specified, a name is generated using + :func:`get_canonical_name`. + + If the name is blocked from registering, returns ``None``. + + If the plugin is already registered, raises a :class:`ValueError`. + """ plugin_name = name or self.get_canonical_name(plugin) if plugin_name in self._name2plugin: @@ -193,8 +197,11 @@ def parse_hookimpl_opts( def unregister( self, plugin: Optional[_Plugin] = None, name: Optional[str] = None ) -> _Plugin: - """unregister a plugin object and all its contained hook implementations - from internal data structures.""" + """Unregister a plugin and all of its hook implementations. + + The plugin can be specified either by the plugin object or the plugin + name. If both are specified, they must agree. + """ if name is None: assert plugin is not None, "one of name or plugin needs to be specified" name = self.get_name(plugin) @@ -216,17 +223,20 @@ def unregister( return plugin def set_blocked(self, name: str) -> None: - """block registrations of the given name, unregister if already registered.""" + """Block registrations of the given name, unregister if already registered.""" self.unregister(name=name) self._name2plugin[name] = None def is_blocked(self, name: str) -> bool: - """return ``True`` if the given plugin name is blocked.""" + """Return whether the given plugin name is blocked.""" return name in self._name2plugin and self._name2plugin[name] is None def add_hookspecs(self, module_or_class: _Namespace) -> None: - """add new hook specifications defined in the given ``module_or_class``. - Functions are recognized if they have been decorated accordingly.""" + """Add new hook specifications defined in the given ``module_or_class``. + + Functions are recognized as hook specifications if they have been + decorated with a matching :class:`HookspecMarker`. + """ names = [] for name in dir(module_or_class): spec_opts = self.parse_hookspec_opts(module_or_class, name) @@ -236,7 +246,7 @@ def add_hookspecs(self, module_or_class: _Namespace) -> None: hc = _HookCaller(name, self._hookexec, module_or_class, spec_opts) setattr(self.hook, name, hc) else: - # plugins registered this hook without knowing the spec + # Plugins registered this hook without knowing the spec. hc.set_specification(module_or_class, spec_opts) for hookfunction in hc.get_hookimpls(): self._verify_hook(hc, hookfunction) @@ -257,32 +267,35 @@ def parse_hookspec_opts( return opts def get_plugins(self) -> Set[Any]: - """return the set of registered plugins.""" + """Return a set of all registered plugin objects.""" return set(self._name2plugin.values()) def is_registered(self, plugin: _Plugin) -> bool: - """Return ``True`` if the plugin is already registered.""" + """Return whether the plugin is already registered.""" return any(plugin == val for val in self._name2plugin.values()) def get_canonical_name(self, plugin: _Plugin) -> str: - """Return canonical name for a plugin object. Note that a plugin - may be registered under a different name which was specified - by the caller of :py:meth:`register(plugin, name) <.PluginManager.register>`. - To obtain the name of an registered plugin use :py:meth:`get_name(plugin) - <.PluginManager.get_name>` instead.""" + """Return a canonical name for a plugin object. + + Note that a plugin may be registered under a different name + specified by the caller of :meth:`register(plugin, name) `. + To obtain the name of n registered plugin use :meth:`get_name(plugin) + ` instead. + """ name: Optional[str] = getattr(plugin, "__name__", None) return name or str(id(plugin)) def get_plugin(self, name: str) -> Optional[Any]: - """Return a plugin or ``None`` for the given name.""" + """Return the plugin registered under the given name, if any.""" return self._name2plugin.get(name) def has_plugin(self, name: str) -> bool: - """Return ``True`` if a plugin with the given name is registered.""" + """Return whether a plugin with the given name is registered.""" return self.get_plugin(name) is not None def get_name(self, plugin: _Plugin) -> Optional[str]: - """Return name for registered plugin or ``None`` if not registered.""" + """Return the name the plugin is registered under, or ``None`` if + is isn't.""" for name, val in self._name2plugin.items(): if plugin == val: return name @@ -325,8 +338,9 @@ def _verify_hook(self, hook: _HookCaller, hookimpl: HookImpl) -> None: ) def check_pending(self) -> None: - """Verify that all hooks which have not been verified against - a hook specification are optional, otherwise raise :py:class:`.PluginValidationError`.""" + """Verify that all hooks which have not been verified against a + hook specification are optional, otherwise raise + :class:`PluginValidationError`.""" for name in self.hook.__dict__: if name[0] != "_": hook: _HookCaller = getattr(self.hook, name) @@ -344,10 +358,10 @@ def load_setuptools_entrypoints( ) -> int: """Load modules from querying the specified setuptools ``group``. - :param str group: entry point group to load plugins - :param str name: if given, loads only plugins with the given ``name``. + :param str group: Entry point group to load plugins. + :param str name: If given, loads only plugins with the given ``name``. :rtype: int - :return: return the number of loaded plugins by this call. + :return: The number of plugins loaded by this call. """ count = 0 for dist in list(importlib_metadata.distributions()): @@ -367,16 +381,16 @@ def load_setuptools_entrypoints( return count def list_plugin_distinfo(self) -> List[Tuple[_Plugin, DistFacade]]: - """return list of distinfo/plugin tuples for all setuptools registered - plugins.""" + """Return a list of (plugin, distinfo) pairs for all + setuptools-registered plugins.""" return list(self._plugin_distinfo) def list_name_plugin(self) -> List[Tuple[str, _Plugin]]: - """return list of name/plugin pairs.""" + """Return a list of (name, plugin) pairs for all registered plugins.""" return list(self._name2plugin.items()) def get_hookcallers(self, plugin: _Plugin) -> Optional[List[_HookCaller]]: - """get all hook callers for the specified plugin.""" + """Get all hook callers for the specified plugin.""" if self.get_name(plugin) is None: return None hookcallers = [] @@ -389,16 +403,16 @@ def get_hookcallers(self, plugin: _Plugin) -> Optional[List[_HookCaller]]: def add_hookcall_monitoring( self, before: _BeforeTrace, after: _AfterTrace ) -> Callable[[], None]: - """add before/after tracing functions for all hooks - and return an undo function which, when called, - will remove the added tracers. + """Add before/after tracing functions for all hooks. + + Returns an undo function which, when called, removes the added tracers. ``before(hook_name, hook_impls, kwargs)`` will be called ahead of all hook calls and receive a hookcaller instance, a list of HookImpl instances and the keyword arguments for the hook call. ``after(outcome, hook_name, hook_impls, kwargs)`` receives the - same arguments as ``before`` but also a :py:class:`pluggy._callers._Result` object + same arguments as ``before`` but also a :class:`_Result` object which represents the result of the overall hook call. """ oldcall = self._inner_hookexec @@ -424,7 +438,10 @@ def undo() -> None: return undo def enable_tracing(self) -> Callable[[], None]: - """enable tracing of hook calls and return an undo function.""" + """Enable tracing of hook calls. + + Returns an undo function which, when called, removes the added tracing. + """ hooktrace = self.trace.root.get("hook") def before( diff --git a/src/pluggy/_result.py b/src/pluggy/_result.py index 230345f1..286e9074 100644 --- a/src/pluggy/_result.py +++ b/src/pluggy/_result.py @@ -34,7 +34,7 @@ def _raise_wrapfail( class HookCallError(Exception): - """Hook was called wrongly.""" + """Hook was called incorrectly.""" class _Result(Generic[_T]): @@ -67,7 +67,7 @@ def force_result(self, result: _T) -> None: """Force the result(s) to ``result``. If the hook was marked as a ``firstresult`` a single value should - be set otherwise set a (modified) list of results. Any exceptions + be set, otherwise set a (modified) list of results. Any exceptions found during invocation will be deleted. """ self._result = result @@ -77,7 +77,7 @@ def get_result(self) -> _T: """Get the result(s) for this hook call. If the hook was marked as a ``firstresult`` only a single value - will be returned otherwise a list of results. + will be returned, otherwise a list of results. """ __tracebackhide__ = True if self._excinfo is None: