Repository: pytest-dev/pluggy
Pull request #429 (2023-08-05 07:22:22)

Some steps towards exporting our typings
Handles some items from #428, but not finished yet.


Patch:
diff --git a/.readthedocs.yml b/.readthedocs.yml
index ac7528c..d751431 100644
--- a/.readthedocs.yml
+++ b/.readthedocs.yml
@@ -5,6 +5,7 @@ python:
      # Without this, sphinx can't find pluggy's version.
      - method: pip
        path: .
+     - requirements: docs/requirements.txt
 
 build:
   os: ubuntu-22.04
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 15f936a..2ee5dcc 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -51,7 +51,7 @@ Features
 
 - `#260 <https://github.com/pytest-dev/pluggy/issues/260>`_: Added "new-style" hook wrappers, a simpler but equally powerful alternative to the existing ``hookwrapper=True`` wrappers.
 
-  New-style wrappers are generator functions, similarly to ``hookwrapper``, but do away with the :class:`result <pluggy._result._Result>` object.
+  New-style wrappers are generator functions, similarly to ``hookwrapper``, but do away with the :class:`result <pluggy.Result>` object.
   Instead, the return value is sent directly to the ``yield`` statement, or, if inner calls raised an exception, it is raised from the ``yield``.
   The wrapper is expected to return a value or raise an exception, which will become the result of the hook call.
 
@@ -64,7 +64,7 @@ Features
 - `#364 <https://github.com/pytest-dev/pluggy/issues/364>`_: Python 3.11 and 3.12 are now officially supported.
 
 
-- `#394 <https://github.com/pytest-dev/pluggy/issues/394>`_: Added the :meth:`~pluggy._result._Result.force_exception` method to ``_Result``.
+- `#394 <https://github.com/pytest-dev/pluggy/issues/394>`_: Added the :meth:`~pluggy.Result.force_exception` method to ``_Result``.
 
   ``force_exception`` allows (old-style) hookwrappers to force an exception or override/adjust an existing exception of a hook invocation,
   in a properly behaving manner. Using ``force_exception`` is preferred over raising an exception from the hookwrapper,
diff --git a/docs/api_reference.rst b/docs/api_reference.rst
index 4e73fba..b00009c 100644
--- a/docs/api_reference.rst
+++ b/docs/api_reference.rst
@@ -3,17 +3,34 @@
 API Reference
 =============
 
-.. automodule:: pluggy
+.. autoclass:: pluggy.PluginManager
     :members:
-    :undoc-members:
 
-.. autoclass:: pluggy._result._Result
-.. automethod:: pluggy._result._Result.get_result
-.. automethod:: pluggy._result._Result.force_result
-.. automethod:: pluggy._result._Result.force_exception
+.. autoclass:: pluggy.PluginValidationError
+    :show-inheritance:
+    :members:
+
+.. autodecorator:: pluggy.HookspecMarker
+
+.. autodecorator:: pluggy.HookimplMarker
+
+.. autoclass:: pluggy.Result()
+    :show-inheritance:
+    :members:
+
+.. autoclass:: pluggy.HookCaller()
+    :members:
+    :special-members: __call__
+
+.. autoclass:: pluggy.HookCallError()
+    :show-inheritance:
+    :members:
+
+.. autoclass:: pluggy.HookRelay()
+    :members:
+
+    .. data:: <hook name>
 
-.. autoclass:: pluggy._hooks._HookCaller
-.. automethod:: pluggy._hooks._HookCaller.call_extra
-.. automethod:: pluggy._hooks._HookCaller.call_historic
+        :type: HookCaller
 
-.. autoclass:: pluggy._hooks._HookRelay
+        The caller for the hook with the given name.
diff --git a/docs/conf.py b/docs/conf.py
index da873ca..02ef01d 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -47,6 +47,7 @@ html_theme_options = {
     "github_type": "star",
     "badge_branch": "master",
     "page_width": "1080px",
+    "sidebar_width": "300px",
     "fixed_sidebar": "false",
 }
 html_sidebars = {
@@ -58,7 +59,7 @@ html_static_path = ["_static"]
 # (source start file, name, description, authors, manual section).
 man_pages = [(master_doc, "pluggy", "pluggy Documentation", [author], 1)]
 
-autodoc_typehints = "none"
+autodoc_member_order = "bysource"
 
 # -- Options for Texinfo output -------------------------------------------
 
diff --git a/docs/index.rst b/docs/index.rst
index 339fb9e..68b065c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -305,6 +305,8 @@ be matched and checked against the ``setup_project`` hookspec:
 
         return config
 
+.. _callorder:
+
 Call time order
 ^^^^^^^^^^^^^^^
 By default hooks are :ref:`called <calling>` in LIFO registered order, however,
@@ -422,6 +424,8 @@ to the hook caller.
 
 Also see the :ref:`pytest:hookwrapper` section in the ``pytest`` docs.
 
+.. _old_style_hookwrappers:
+
 Old-style wrappers
 ^^^^^^^^^^^^^^^^^^
 
@@ -468,21 +472,21 @@ execution of all corresponding non-wrappper *hookimpls*.
         if config.use_defaults:
             outcome.force_result(defaults)
 
-The generator is :py:meth:`sent <python:generator.send>` a :py:class:`pluggy._result._Result` object which can
+The generator is :py:meth:`sent <python:generator.send>` a :py:class:`pluggy.Result` object which can
 be assigned in the ``yield`` expression and used to inspect
 the final result(s) or exceptions returned back to the caller using the
-:py:meth:`~pluggy._result._Result.get_result` method, override the result
-using the :py:meth:`~pluggy._result._Result.force_result`, or override
-the exception using the :py:meth:`~pluggy._result._Result.force_exception`
+:py:meth:`~pluggy.Result.get_result` method, override the result
+using the :py:meth:`~pluggy.Result.force_result`, or override
+the exception using the :py:meth:`~pluggy.Result.force_exception`
 method.
 
 .. note::
     Old-style hook wrappers can **not** return results; they can only modify
-    them using the :py:meth:`~pluggy._result._Result.force_result` API.
+    them using the :py:meth:`~pluggy.Result.force_result` API.
 
     Old-style Hook wrappers should **not** raise exceptions; this will cause
     further hookwrappers to be skipped. They should use
-    :py:meth:`~pluggy._result._Result.force_exception` to adjust the
+    :py:meth:`~pluggy.Result.force_exception` to adjust the
     exception.
 
 .. _specs:
@@ -611,7 +615,7 @@ Also see the :ref:`pytest:firstresult` section in the ``pytest`` docs.
 Historic hooks
 ^^^^^^^^^^^^^^
 You can mark a *hookspec* as being *historic* meaning that the hook
-can be called with :py:meth:`~pluggy._hooks._HookCaller.call_historic()` **before**
+can be called with :py:meth:`~pluggy.HookCaller.call_historic()` **before**
 having been registered:
 
 .. code-block:: python
@@ -629,12 +633,14 @@ dynamically loaded plugins.
 For more info see :ref:`call_historic`.
 
 
+.. _warn_on_impl:
+
 Warnings on hook implementation
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 As projects evolve new hooks may be introduced and/or deprecated.
 
-if a hookspec specifies a ``warn_on_impl``, pluggy will trigger it for any plugin implementing the hook.
+If a hookspec specifies a ``warn_on_impl``, pluggy will trigger it for any plugin implementing the hook.
 
 
 .. code-block:: python
@@ -733,13 +739,13 @@ The core functionality of ``pluggy`` enables an extension provider
 to override function calls made at certain points throughout a program.
 
 A particular *hook* is invoked by calling an instance of
-a :py:class:`pluggy._hooks._HookCaller` which in turn *loops* through the
+a :py:class:`pluggy.HookCaller` which in turn *loops* through the
 ``1:N`` registered *hookimpls* and calls them in sequence.
 
 Every :py:class:`~pluggy.PluginManager` has a ``hook`` attribute
-which is an instance of this :py:class:`pluggy._hooks._HookRelay`.
-The :py:class:`~pluggy._hooks._HookRelay` itself contains references
-(by hook name) to each registered *hookimpl*'s :py:class:`~pluggy._hooks._HookCaller` instance.
+which is an instance of :py:class:`pluggy.HookRelay`.
+The :py:class:`~pluggy.HookRelay` itself contains references
+(by hook name) to each registered *hookimpl*'s :py:class:`~pluggy.HookCaller` instance.
 
 More practically you call a *hook* like so:
 
@@ -755,7 +761,7 @@ More practically you call a *hook* like so:
     pm.add_hookspecs(mypluginspec)
     pm.register(myplugin)
 
-    # we invoke the _HookCaller and thus all underlying hookimpls
+    # we invoke the HookCaller and thus all underlying hookimpls
     result_list = pm.hook.myhook(config=config, args=sys.argv)
 
 Note that you **must** call hooks using keyword :std:term:`python:argument` syntax!
@@ -874,7 +880,7 @@ only useful if you expect that some *hookimpls* may be registered **after** the
 hook is initially invoked.
 
 Historic hooks must be :ref:`specially marked <historic>` and called
-using the :py:meth:`~pluggy._hooks._HookCaller.call_historic()` method:
+using the :py:meth:`~pluggy.HookCaller.call_historic()` method:
 
 .. code-block:: python
 
@@ -895,8 +901,8 @@ using the :py:meth:`~pluggy._hooks._HookCaller.call_historic()` method:
     # historic callback is invoked here
     pm.register(mylateplugin)
 
-Note that if you :py:meth:`~pluggy._hooks._HookCaller.call_historic()`
-the :py:class:`~pluggy._hooks._HookCaller` (and thus your calling code)
+Note that if you :py:meth:`~pluggy.HookCaller.call_historic()`
+the :py:class:`~pluggy.HookCaller` (and thus your calling code)
 can not receive results back from the underlying *hookimpl* functions.
 Instead you can provide a *callback* for processing results (like the
 ``callback`` function above) which will be called as each new plugin
@@ -907,23 +913,28 @@ is registered.
     hooks since only the first registered plugin's hook(s) would
     ever be called.
 
+.. _call_extra:
+
 Calling with extras
 -------------------
 You can call a hook with temporarily participating *implementation* functions
 (that aren't in the registry) using the
-:py:meth:`pluggy._hooks._HookCaller.call_extra()` method.
+:py:meth:`pluggy.HookCaller.call_extra()` method.
 
 
 Calling with a subset of registered plugins
 -------------------------------------------
 You can make a call using a subset of plugins by asking the
 :py:class:`~pluggy.PluginManager` first for a
-:py:class:`~pluggy._hooks._HookCaller` with those plugins removed
+:py:class:`~pluggy.HookCaller` with those plugins removed
 using the :py:meth:`pluggy.PluginManager.subset_hook_caller()` method.
 
-You then can use that :py:class:`_HookCaller <pluggy._hooks._HookCaller>`
-to make normal, :py:meth:`~pluggy._hooks._HookCaller.call_historic`, or
-:py:meth:`~pluggy._hooks._HookCaller.call_extra` calls as necessary.
+You then can use that :py:class:`~pluggy.HookCaller`
+to make normal, :py:meth:`~pluggy.HookCaller.call_historic`, or
+:py:meth:`~pluggy.HookCaller.call_extra` calls as necessary.
+
+
+.. _tracing:
 
 Built-in tracing
 ****************
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..7d0b87a
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,4 @@
+# Higher bound for safety, can bump it if builds fine with new major versions.
+sphinx>=6,<8
+pygments
+towncrier
diff --git a/src/pluggy/__init__.py b/src/pluggy/__init__.py
index c530bff..ae5a18f 100644
--- a/src/pluggy/__init__.py
+++ b/src/pluggy/__init__.py
@@ -8,11 +8,23 @@ except ImportError:
 __all__ = [
     "PluginManager",
     "PluginValidationError",
+    "HookCaller",
     "HookCallError",
+    "HookSpecOpts",
+    "HookImplOpts",
+    "HookRelay",
     "HookspecMarker",
     "HookimplMarker",
+    "Result",
 ]
 
 from ._manager import PluginManager, PluginValidationError
-from ._result import HookCallError
-from ._hooks import HookspecMarker, HookimplMarker
+from ._result import HookCallError, Result
+from ._hooks import (
+    HookspecMarker,
+    HookimplMarker,
+    HookCaller,
+    HookRelay,
+    HookSpecOpts,
+    HookImplOpts,
+)
diff --git a/src/pluggy/_callers.py b/src/pluggy/_callers.py
index 3d63a2c..6498eae 100644
--- a/src/pluggy/_callers.py
+++ b/src/pluggy/_callers.py
@@ -12,14 +12,14 @@ from typing import Union
 
 from ._hooks import HookImpl
 from ._result import _raise_wrapfail
-from ._result import _Result
 from ._result import HookCallError
+from ._result import Result
 
 
 # Need to distinguish between old- and new-style hook wrappers.
 # Wrapping one a singleton tuple is the fastest type-safe way I found to do it.
 Teardown = Union[
-    Tuple[Generator[None, _Result[object], None]],
+    Tuple[Generator[None, Result[object], None]],
     Generator[None, object, object],
 ]
 
@@ -33,7 +33,7 @@ def _multicall(
     """Execute a call into multiple python functions/methods and return the
     result(s).
 
-    ``caller_kwargs`` comes from _HookCaller.__call__().
+    ``caller_kwargs`` comes from HookCaller.__call__().
     """
     __tracebackhide__ = True
     results: list[object] = []
@@ -58,7 +58,7 @@ def _multicall(
                         # If this cast is not valid, a type error is raised below,
                         # which is the desired response.
                         res = hook_impl.function(*args)
-                        wrapper_gen = cast(Generator[None, _Result[object], None], res)
+                        wrapper_gen = cast(Generator[None, Result[object], None], res)
                         next(wrapper_gen)  # first yield
                         teardowns.append((wrapper_gen,))
                     except StopIteration:
@@ -82,7 +82,7 @@ def _multicall(
         except BaseException as exc:
             exception = exc
     finally:
-        # Fast path - only new-style wrappers, no _Result.
+        # Fast path - only new-style wrappers, no Result.
         if only_new_style_wrappers:
             if firstresult:  # first result hooks return a single value
                 result = results[0] if results else None
@@ -117,11 +117,11 @@ def _multicall(
         # Slow path - need to support old-style wrappers.
         else:
             if firstresult:  # first result hooks return a single value
-                outcome: _Result[object | list[object]] = _Result(
+                outcome: Result[object | list[object]] = Result(
                     results[0] if results else None, exception
                 )
             else:
-                outcome = _Result(results, exception)
+                outcome = Result(results, exception)
 
             # run all wrapper post-yield blocks
             for teardown in reversed(teardowns):
diff --git a/src/pluggy/_hooks.py b/src/pluggy/_hooks.py
index af3ceef..09907a3 100644
--- a/src/pluggy/_hooks.py
+++ b/src/pluggy/_hooks.py
@@ -23,7 +23,7 @@ from typing import TypedDict
 from typing import TypeVar
 from typing import Union
 
-from ._result import _Result
+from ._result import Result
 
 
 _T = TypeVar("_T")
@@ -34,21 +34,38 @@ _HookExec = Callable[
     [str, Sequence["HookImpl"], Mapping[str, object], bool],
     Union[object, List[object]],
 ]
-_HookImplFunction = Callable[..., Union[_T, Generator[None, _Result[_T], None]]]
+_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>`.
     firstresult: bool
+    #: Whether the hook is :ref:`historic <historic>`.
     historic: bool
+    #: Whether the hook :ref:`warns when implemented <warn_on_impl>`.
     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>`.
     wrapper: bool
+    #: Whether the hook implementation is an :ref:`old-style wrapper
+    #: <old_style_hookwrappers>`.
     hookwrapper: bool
+    #: Whether validation against a hook specification is :ref:`optional
+    #: <optionalhook>`.
     optionalhook: bool
+    #: Whether to try to order this hook implementation :ref:`first
+    #: <callorder>`.
     tryfirst: bool
+    #: Whether to try to order this hook implementation :ref:`last
+    #: <callorder>`.
     trylast: bool
+    #: The name of the hook specification to match, see :ref:`specname`.
     specname: str | None
 
 
@@ -57,7 +74,7 @@ class HookspecMarker:
 
     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.
+    functions if the :class:`PluginManager` uses the same project name.
     """
 
     __slots__ = ("project_name",)
@@ -98,18 +115,24 @@ class HookspecMarker:
         If passed no function, returns a decorator which can be applied to a
         function later using the attributes supplied.
 
-        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.
+        :param firstresult:
+            If ``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. See :ref:`firstresult`.
+
+        :param historic:
+            If ``True``, every call to the hook will be memorized and replayed
+            on plugins registered after the call was made. See :ref:`historic`.
 
-        If ``historic`` is ``True``, every call to the hook will be memorized
-        and replayed on plugins registered after the call was made.
+        :param warn_on_impl:
+            If given, every implementation of this hook will trigger the given
+            warning. See :ref:`warn_on_impl`.
         """
 
         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,
@@ -128,7 +151,7 @@ class HookimplMarker:
 
     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.
+    functions if the :class:`PluginManager` uses the same project name.
     """
 
     __slots__ = ("project_name",)
@@ -178,43 +201,53 @@ class HookimplMarker:
         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 ``trylast`` is ``True``, this hook implementation will run as late as
-        possible in the chain of N hook implementations.
-
-        If ``wrapper`` is ``True``("new-style hook wrapper"), the hook
-        implementation needs to execute exactly one ``yield``. The code before
-        the ``yield`` is run early before any non-hook-wrapper function is run.
-        The code after the ``yield`` is run after all non-hook-wrapper functions
-        have run. The ``yield`` receives the result value of the inner calls, or
-        raises the exception of inner calls (including earlier hook wrapper
-        calls). The return value of the function becomes the return value of the
-        hook, and a raised exception becomes the exception of the hook.
-
-        If ``hookwrapper`` is ``True`` ("old-style hook wrapper"), the hook
-        implementation needs to execute exactly one ``yield``. The code before
-        the ``yield`` is run early before any non-hook-wrapper function is run.
-        The code after the ``yield`` is run after all non-hook-wrapper function
-        have run  The ``yield`` receives a :class:`_Result` object representing
-        the exception or result outcome of the inner calls (including earlier
-        hook wrapper calls). This option is mutually exclusive with ``wrapper``.
-
-        If ``specname`` is provided, it will be used instead of the function
-        name when matching this hook implementation to a hook specification
-        during registration.
+        :param optionalhook:
+            If ``True``, a missing matching hook specification will not result
+            in an error (by default it is an error if no matching spec is
+            found). See :ref:`optionalhook`.
+
+        :param tryfirst:
+            If ``True``, this hook implementation will run as early as possible
+            in the chain of N hook implementations for a specification. See
+            :ref:`callorder`.
+
+        :param trylast:
+            If ``True``, this hook implementation will run as late as possible
+            in the chain of N hook implementations for a specification. See
+            :ref:`callorder`.
+
+        :param wrapper:
+            If ``True`` ("new-style hook wrapper"), the hook implementation
+            needs to execute exactly one ``yield``. The code before the
+            ``yield`` is run early before any non-hook-wrapper function is run.
+            The code after the ``yield`` is run after all non-hook-wrapper
+            functions have run. The ``yield`` receives the result value of the
+            inner calls, or raises the exception of inner calls (including
+            earlier hook wrapper calls). The return value of the function
+            becomes the return value of the hook, and a raised exception becomes
+            the exception of the hook. See :ref:`hookwrapper`.
+
+        :param hookwrapper:
+            If ``True`` ("old-style hook wrapper"), the hook implementation
+            needs to execute exactly one ``yield``. The code before the
+            ``yield`` is run early before any non-hook-wrapper function is run.
+            The code after the ``yield`` is run after all non-hook-wrapper
+            function have run  The ``yield`` receives a :class:`Result` object
+            representing the exception or result outcome of the inner calls
+            (including earlier hook wrapper calls). This option is mutually
+            exclusive with ``wrapper``. See :ref:`old_style_hookwrapper`.
+
+        :param specname:
+            If provided, the given name will be used instead of the function
+            name when matching this hook implementation to a hook specification
+            during registration. See :ref:`specname`.
 
         .. versionadded:: 1.2.0
             The ``wrapper`` parameter.
         """
 
         def setattr_hookimpl_opts(func: _F) -> _F:
-            opts: _HookImplOpts = {
+            opts: HookImplOpts = {
                 "wrapper": wrapper,
                 "hookwrapper": hookwrapper,
                 "optionalhook": optionalhook,
@@ -231,7 +264,7 @@ class HookimplMarker:
             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)
@@ -308,22 +341,31 @@ def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]:
     return args, kwargs
 
 
-class _HookRelay:
+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:
+        def __getattr__(self, name: str) -> HookCaller:
             ...
 
 
+# Historical name (pluggy<=1.2), kept for backward compatibility.
+_HookRelay = HookRelay
+
+
 _CallHistory = List[Tuple[Mapping[str, object], Optional[Callable[[Any], None]]]]
 
 
-class _HookCaller:
+class HookCaller:
+    """A caller of all registered implementations of a hook specification."""
+
     __slots__ = (
         "name",
         "spec",
@@ -337,8 +379,9 @@ class _HookCaller:
         name: str,
         hook_execute: _HookExec,
         specmodule_or_class: _Namespace | None = None,
-        spec_opts: _HookSpecOpts | None = None,
+        spec_opts: HookSpecOpts | None = None,
     ) -> None:
+        """:meta private:"""
         self.name: Final = name
         self._hookexec: Final = hook_execute
         self._hookimpls: Final[list[HookImpl]] = []
@@ -348,13 +391,15 @@ class _HookCaller:
             assert spec_opts is not None
             self.set_specification(specmodule_or_class, spec_opts)
 
+    # TODO: Document, or make private.
     def has_spec(self) -> bool:
         return self.spec is not None
 
+    # TODO: Document, or make private.
     def set_specification(
         self,
         specmodule_or_class: _Namespace,
-        spec_opts: _HookSpecOpts,
+        spec_opts: HookSpecOpts,
     ) -> None:
         if self.spec is not None:
             raise ValueError(
@@ -366,6 +411,7 @@ class _HookCaller:
             self._call_history = []
 
     def is_historic(self) -> bool:
+        """Whether this caller is :ref:`historic <historic>`."""
         return self._call_history is not None
 
     def _remove_plugin(self, plugin: _Plugin) -> None:
@@ -376,6 +422,7 @@ class _HookCaller:
         raise ValueError(f"plugin {plugin!r} not found")
 
     def get_hookimpls(self) -> list[HookImpl]:
+        """Get all registered hook implementations for this hook."""
         return self._hookimpls.copy()
 
     def _add_hookimpl(self, hookimpl: HookImpl) -> None:
@@ -403,7 +450,7 @@ class _HookCaller:
             self._hookimpls.insert(i + 1, hookimpl)
 
     def __repr__(self) -> str:
-        return f"<_HookCaller {self.name!r}>"
+        return f"<HookCaller {self.name!r}>"
 
     def _verify_all_args_are_provided(self, kwargs: Mapping[str, object]) -> None:
         # This is written to avoid expensive operations when not needed.
@@ -424,6 +471,14 @@ class _HookCaller:
                     break
 
     def __call__(self, **kwargs: object) -> Any:
+        """Call the hook.
+
+        Only accepts keyword arguments, which should match the hook
+        specification.
+
+        Returns the result(s) of calling all registered plugins, see
+        :ref:`calling`.
+        """
         assert (
             not self.is_historic()
         ), "Cannot directly call a historic hook - use call_historic instead."
@@ -437,10 +492,12 @@ class _HookCaller:
         kwargs: Mapping[str, object] | None = None,
     ) -> None:
         """Call the hook with given ``kwargs`` for all registered plugins and
-        for all plugins which will be registered afterwards.
+        for all plugins which will be registered afterwards, see
+        :ref:`historic`.
 
-        If ``result_callback`` is provided, it will be called for each
-        non-``None`` result obtained from a hook implementation.
+        :param result_callback:
+            If provided, will be called for each non-``None`` result obtained
+            from a hook implementation.
         """
         assert self._call_history is not None
         kwargs = kwargs or {}
@@ -459,12 +516,13 @@ class _HookCaller:
         self, methods: Sequence[Callable[..., object]], kwargs: Mapping[str, object]
     ) -> Any:
         """Call the hook with some additional temporarily participating
-        methods using the specified ``kwargs`` as call parameters."""
+        methods using the specified ``kwargs`` as call parameters, see
+        :ref:`call_extra`."""
         assert (
             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,
@@ -499,11 +557,15 @@ class _HookCaller:
                     result_callback(res[0])
 
 
-class _SubsetHookCaller(_HookCaller):
+# Historical name (pluggy<=1.2), kept for backward compatibility.
+_HookCaller = HookCaller
+
+
+class _SubsetHookCaller(HookCaller):
     """A proxy to another HookCaller which manages calls to all registered
     plugins except the ones from remove_plugins."""
 
-    # This class is unusual: in inhertits from `_HookCaller` so all of
+    # This class is unusual: in inhertits from `HookCaller` so all of
     # the *code* runs in the class, but it delegates all underlying *data*
     # to the original HookCaller.
     # `subset_hook_caller` used to be implemented by creating a full-fledged
@@ -518,7 +580,7 @@ class _SubsetHookCaller(_HookCaller):
         "_remove_plugins",
     )
 
-    def __init__(self, orig: _HookCaller, remove_plugins: AbstractSet[_Plugin]) -> None:
+    def __init__(self, orig: HookCaller, remove_plugins: AbstractSet[_Plugin]) -> None:
         self._orig = orig
         self._remove_plugins = remove_plugins
         self.name = orig.name  # type: ignore[misc]
@@ -564,7 +626,7 @@ class HookImpl:
         plugin: _Plugin,
         plugin_name: str,
         function: _HookImplFunction[object],
-        hook_impl_opts: _HookImplOpts,
+        hook_impl_opts: HookImplOpts,
     ) -> None:
         self.function: Final = function
         self.argnames, self.kwargnames = varnames(self.function)
@@ -592,7 +654,7 @@ class HookSpec:
         "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
diff --git a/src/pluggy/_manager.py b/src/pluggy/_manager.py
index 3015328..070ad83 100644
--- a/src/pluggy/_manager.py
+++ b/src/pluggy/_manager.py
@@ -14,22 +14,22 @@ from typing import Sequence
 
 from . import _tracing
 from ._callers import _multicall
-from ._hooks import _HookCaller
 from ._hooks import _HookImplFunction
-from ._hooks import _HookImplOpts
-from ._hooks import _HookRelay
-from ._hooks import _HookSpecOpts
 from ._hooks import _Namespace
 from ._hooks import _Plugin
 from ._hooks import _SubsetHookCaller
+from ._hooks import HookCaller
 from ._hooks import HookImpl
+from ._hooks import HookImplOpts
+from ._hooks import HookRelay
 from ._hooks import HookSpec
+from ._hooks import HookSpecOpts
 from ._hooks import normalize_hookimpl_opts
-from ._result import _Result
+from ._result import Result
 
 
 _BeforeTrace = Callable[[str, Sequence[HookImpl], Mapping[str, Any]], None]
-_AfterTrace = Callable[[_Result[Any], str, Sequence[HookImpl], Mapping[str, Any]], None]
+_AfterTrace = Callable[[Result[Any], str, Sequence[HookImpl], Mapping[str, Any]], None]
 
 
 def _warn_for_function(warning: Warning, function: Callable[..., object]) -> None:
@@ -46,10 +46,12 @@ class PluginValidationError(Exception):
     """Plugin failed validation.
 
     :param plugin: The plugin which failed validation.
+    :param message: Error message.
     """
 
     def __init__(self, plugin: _Plugin, message: str) -> None:
         super().__init__(message)
+        #: The plugin which failed validation.
         self.plugin = plugin
 
 
@@ -83,14 +85,23 @@ class PluginManager:
 
     For debugging purposes you can call :meth:`PluginManager.enable_tracing`
     which will subsequently send debug information to the trace helper.
+
+    :param project_name:
+        The short project name. Prefer snake case. Make sure it's unique!
     """
 
     def __init__(self, project_name: str) -> None:
-        self.project_name: Final = project_name
+        #: The project name.
+        self.project_name: Final[str] = project_name
         self._name2plugin: Final[dict[str, _Plugin]] = {}
         self._plugin_distinfo: Final[list[tuple[_Plugin, DistFacade]]] = []
-        self.trace: Final = _tracing.TagTracer().get("pluginmanage")
-        self.hook: Final = _HookRelay()
+        #: The "hook relay", used to call a hook on all registered plugins.
+        #: See :ref:`calling`.
+        self.hook: Final[HookRelay] = HookRelay()
+        #: The tracing entry point. See :ref:`tracing`.
+        self.trace: Final[_tracing.TagTracerSub] = _tracing.TagTracer().get(
+            "pluginmanage"
+        )
         self._inner_hookexec = _multicall
 
     def _hookexec(
@@ -107,10 +118,13 @@ class PluginManager:
     def register(self, plugin: _Plugin, name: str | None = None) -> str | None:
         """Register a plugin and return its name.
 
-        If a name is not specified, a name is generated using
-        :func:`get_canonical_name`.
+        :param name:
+            The name under which to register the plugin. If not specified, a
+            name is generated using :func:`get_canonical_name`.
 
-        If the name is blocked from registering, returns ``None``.
+        :returns:
+            The plugin name. If the name is blocked from registering, returns
+            ``None``.
 
         If the plugin is already registered, raises a :exc:`ValueError`.
         """
@@ -142,9 +156,9 @@ class PluginManager:
                 method: _HookImplFunction[object] = getattr(plugin, name)
                 hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)
                 name = hookimpl_opts.get("specname") or name
-                hook: _HookCaller | None = getattr(self.hook, name, None)
+                hook: HookCaller | None = getattr(self.hook, name, None)
                 if hook is None:
-                    hook = _HookCaller(name, self._hookexec)
+                    hook = HookCaller(name, self._hookexec)
                     setattr(self.hook, name, hook)
                 elif hook.has_spec():
                     self._verify_hook(hook, hookimpl)
@@ -152,12 +166,22 @@ class PluginManager:
                 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.
+
+        :returns:
+            The parsed hookimpl options, or None to skip the given item.
+
+        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`.
+        """
         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:
@@ -169,11 +193,13 @@ class PluginManager:
 
     def unregister(
         self, plugin: _Plugin | None = None, name: str | None = None
-    ) -> _Plugin:
+    ) -> Any | None:
         """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.
+
+        Returns the unregistered plugin, or ``None`` if not found.
         """
         if name is None:
             assert plugin is not None, "one of name or plugin needs to be specified"
@@ -182,6 +208,8 @@ class PluginManager:
 
         if plugin is None:
             plugin = self.get_plugin(name)
+            if plugin is None:
+                return None
 
         hookcallers = self.get_hookcallers(plugin)
         if hookcallers:
@@ -214,9 +242,9 @@ class PluginManager:
         for name in dir(module_or_class):
             spec_opts = self.parse_hookspec_opts(module_or_class, name)
             if spec_opts is not None:
-                hc: _HookCaller | None = getattr(self.hook, name, None)
+                hc: HookCaller | None = getattr(self.hook, name, None)
                 if hc is None:
-                    hc = _HookCaller(name, self._hookexec, module_or_class, spec_opts)
+                    hc = HookCaller(name, self._hookexec, module_or_class, spec_opts)
                     setattr(self.hook, name, hc)
                 else:
                     # Plugins registered this hook without knowing the spec.
@@ -232,9 +260,20 @@ class PluginManager:
 
     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.
+
+        :returns:
+            The parsed hookspec options for defining a hook, or None to skip the
+            given item.
+
+        This method can be overridden by ``PluginManager`` subclasses to
+        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)
+        opts: HookSpecOpts | None = getattr(method, self.project_name + "_spec", None)
         return opts
 
     def get_plugins(self) -> set[Any]:
@@ -250,7 +289,7 @@ class PluginManager:
 
         Note that a plugin may be registered under a different name
         specified by the caller of :meth:`register(plugin, name) <register>`.
-        To obtain the name of n registered plugin use :meth:`get_name(plugin)
+        To obtain the name of a registered plugin use :meth:`get_name(plugin)
         <get_name>` instead.
         """
         name: str | None = getattr(plugin, "__name__", None)
@@ -272,7 +311,7 @@ class PluginManager:
                 return name
         return None
 
-    def _verify_hook(self, hook: _HookCaller, hookimpl: HookImpl) -> None:
+    def _verify_hook(self, hook: HookCaller, hookimpl: HookImpl) -> None:
         if hook.is_historic() and (hookimpl.hookwrapper or hookimpl.wrapper):
             raise PluginValidationError(
                 hookimpl.plugin,
@@ -325,7 +364,7 @@ class PluginManager:
         :exc:`PluginValidationError`."""
         for name in self.hook.__dict__:
             if name[0] != "_":
-                hook: _HookCaller = getattr(self.hook, name)
+                hook: HookCaller = getattr(self.hook, name)
                 if not hook.has_spec():
                     for hookimpl in hook.get_hookimpls():
                         if not hookimpl.optionalhook:
@@ -338,10 +377,13 @@ class PluginManager:
     def load_setuptools_entrypoints(self, group: str, name: str | None = None) -> 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``.
-        :rtype: int
-        :return: The number of plugins loaded by this call.
+        :param group:
+            Entry point group to load plugins.
+        :param name:
+            If given, loads only plugins with the given ``name``.
+
+        :return:
+            The number of plugins loaded by this call.
         """
         count = 0
         for dist in list(importlib.metadata.distributions()):
@@ -369,8 +411,13 @@ class PluginManager:
         """Return a list of (name, plugin) pairs for all registered plugins."""
         return list(self._name2plugin.items())
 
-    def get_hookcallers(self, plugin: _Plugin) -> list[_HookCaller] | None:
-        """Get all hook callers for the specified plugin."""
+    def get_hookcallers(self, plugin: _Plugin) -> list[HookCaller] | None:
+        """Get all hook callers for the specified plugin.
+
+        :returns:
+            The hook callers, or ``None`` if ``plugin`` is not registered in
+            this plugin manager.
+        """
         if self.get_name(plugin) is None:
             return None
         hookcallers = []
@@ -392,7 +439,7 @@ class PluginManager:
         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 :class:`~pluggy._result._Result` object
+        same arguments as ``before`` but also a :class:`~pluggy.Result` object
         which represents the result of the overall hook call.
         """
         oldcall = self._inner_hookexec
@@ -404,7 +451,7 @@ class PluginManager:
             firstresult: bool,
         ) -> object | list[object]:
             before(hook_name, hook_impls, caller_kwargs)
-            outcome = _Result.from_call(
+            outcome = Result.from_call(
                 lambda: oldcall(hook_name, hook_impls, caller_kwargs, firstresult)
             )
             after(outcome, hook_name, hook_impls, caller_kwargs)
@@ -431,7 +478,7 @@ class PluginManager:
             hooktrace(hook_name, kwargs)
 
         def after(
-            outcome: _Result[object],
+            outcome: Result[object],
             hook_name: str,
             methods: Sequence[HookImpl],
             kwargs: Mapping[str, object],
@@ -444,11 +491,11 @@ class PluginManager:
 
     def subset_hook_caller(
         self, name: str, remove_plugins: Iterable[_Plugin]
-    ) -> _HookCaller:
-        """Return a proxy :py:class:`._hooks._HookCaller` instance for the named
+    ) -> HookCaller:
+        """Return a proxy :class:`~pluggy.HookCaller` instance for the named
         method which manages calls to all registered plugins except the ones
         from remove_plugins."""
-        orig: _HookCaller = getattr(self.hook, name)
+        orig: HookCaller = getattr(self.hook, name)
         plugins_to_remove = {plug for plug in remove_plugins if hasattr(plug, name)}
         if plugins_to_remove:
             return _SubsetHookCaller(orig, plugins_to_remove)
diff --git a/src/pluggy/_result.py b/src/pluggy/_result.py
index 47eb461..af26a75 100644
--- a/src/pluggy/_result.py
+++ b/src/pluggy/_result.py
@@ -16,12 +16,12 @@ from typing import TypeVar
 
 
 _ExcInfo = Tuple[Type[BaseException], BaseException, Optional[TracebackType]]
-_T = TypeVar("_T")
+ResultType = TypeVar("ResultType")
 
 
 def _raise_wrapfail(
     wrap_controller: (
-        Generator[None, _Result[_T], None] | Generator[None, object, object]
+        Generator[None, Result[ResultType], None] | Generator[None, object, object]
     ),
     msg: str,
 ) -> NoReturn:
@@ -36,19 +36,24 @@ class HookCallError(Exception):
     """Hook was called incorrectly."""
 
 
-class _Result(Generic[_T]):
+class Result(Generic[ResultType]):
+    """An object used to inspect and set the result in a :ref:`hook wrapper
+    <hookwrappers>`."""
+
     __slots__ = ("_result", "_exception")
 
     def __init__(
         self,
-        result: _T | None,
+        result: ResultType | None,
         exception: BaseException | None,
     ) -> None:
+        """:meta private:"""
         self._result = result
         self._exception = exception
 
     @property
     def excinfo(self) -> _ExcInfo | None:
+        """:meta private:"""
         exc = self._exception
         if exc is None:
             return None
@@ -57,10 +62,12 @@ class _Result(Generic[_T]):
 
     @property
     def exception(self) -> BaseException | None:
+        """:meta private:"""
         return self._exception
 
     @classmethod
-    def from_call(cls, func: Callable[[], _T]) -> _Result[_T]:
+    def from_call(cls, func: Callable[[], ResultType]) -> Result[ResultType]:
+        """:meta private:"""
         __tracebackhide__ = True
         result = exception = None
         try:
@@ -69,7 +76,7 @@ class _Result(Generic[_T]):
             exception = exc
         return cls(result, exception)
 
-    def force_result(self, result: _T) -> None:
+    def force_result(self, result: ResultType) -> None:
         """Force the result(s) to ``result``.
 
         If the hook was marked as a ``firstresult`` a single value should
@@ -91,7 +98,7 @@ class _Result(Generic[_T]):
         self._result = None
         self._exception = exception
 
-    def get_result(self) -> _T:
+    def get_result(self) -> ResultType:
         """Get the result(s) for this hook call.
 
         If the hook was marked as a ``firstresult`` only a single value
@@ -100,6 +107,10 @@ class _Result(Generic[_T]):
         __tracebackhide__ = True
         exc = self._exception
         if exc is None:
-            return cast(_T, self._result)
+            return cast(ResultType, self._result)
         else:
             raise exc.with_traceback(exc.__traceback__)
+
+
+# Historical name (pluggy<=1.2), kept for backward compatibility.
+_Result = Result
diff --git a/tox.ini b/tox.ini
index 3f50fed..de464a0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -22,9 +22,7 @@ deps=
 
 [testenv:docs]
 deps =
-  sphinx
-  pygments
-  towncrier
+    -r{toxinidir}/docs/requirements.txt
 commands =
   python scripts/towncrier-draft-to-file.py
   # the '-t changelog_towncrier_draft' tags makes sphinx include the draft


Test patch:
diff --git a/testing/test_hookcaller.py b/testing/test_hookcaller.py
index ce1cc83..88ed131 100644
--- a/testing/test_hookcaller.py
+++ b/testing/test_hookcaller.py
@@ -9,7 +9,7 @@ from pluggy import HookimplMarker
 from pluggy import HookspecMarker
 from pluggy import PluginManager
 from pluggy import PluginValidationError
-from pluggy._hooks import _HookCaller
+from pluggy._hooks import HookCaller
 from pluggy._hooks import HookImpl
 
 hookspec = HookspecMarker("example")
@@ -17,7 +17,7 @@ hookimpl = HookimplMarker("example")
 
 
 @pytest.fixture
-def hc(pm: PluginManager) -> _HookCaller:
+def hc(pm: PluginManager) -> HookCaller:
     class Hooks:
         @hookspec
         def he_method1(self, arg: object) -> None:
@@ -31,7 +31,7 @@ FuncT = TypeVar("FuncT", bound=Callable[..., object])
 
 
 class AddMeth:
-    def __init__(self, hc: _HookCaller) -> None:
+    def __init__(self, hc: HookCaller) -> None:
         self.hc = hc
 
     def __call__(
@@ -57,7 +57,7 @@ class AddMeth:
 
 
 @pytest.fixture
-def addmeth(hc: _HookCaller) -> AddMeth:
+def addmeth(hc: HookCaller) -> AddMeth:
     return AddMeth(hc)
 
 
@@ -65,7 +65,7 @@ def funcs(hookmethods: Sequence[HookImpl]) -> List[Callable[..., object]]:
     return [hookmethod.function for hookmethod in hookmethods]
 
 
-def test_adding_nonwrappers(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_nonwrappers(hc: HookCaller, addmeth: AddMeth) -> None:
     @addmeth()
     def he_method1() -> None:
         pass
@@ -81,7 +81,7 @@ def test_adding_nonwrappers(hc: _HookCaller, addmeth: AddMeth) -> None:
     assert funcs(hc.get_hookimpls()) == [he_method1, he_method2, he_method3]
 
 
-def test_adding_nonwrappers_trylast(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_nonwrappers_trylast(hc: HookCaller, addmeth: AddMeth) -> None:
     @addmeth()
     def he_method1_middle() -> None:
         pass
@@ -97,7 +97,7 @@ def test_adding_nonwrappers_trylast(hc: _HookCaller, addmeth: AddMeth) -> None:
     assert funcs(hc.get_hookimpls()) == [he_method1, he_method1_middle, he_method1_b]
 
 
-def test_adding_nonwrappers_trylast3(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_nonwrappers_trylast3(hc: HookCaller, addmeth: AddMeth) -> None:
     @addmeth()
     def he_method1_a() -> None:
         pass
@@ -122,7 +122,7 @@ def test_adding_nonwrappers_trylast3(hc: _HookCaller, addmeth: AddMeth) -> None:
     ]
 
 
-def test_adding_nonwrappers_trylast2(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_nonwrappers_trylast2(hc: HookCaller, addmeth: AddMeth) -> None:
     @addmeth()
     def he_method1_middle() -> None:
         pass
@@ -138,7 +138,7 @@ def test_adding_nonwrappers_trylast2(hc: _HookCaller, addmeth: AddMeth) -> None:
     assert funcs(hc.get_hookimpls()) == [he_method1, he_method1_middle, he_method1_b]
 
 
-def test_adding_nonwrappers_tryfirst(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_nonwrappers_tryfirst(hc: HookCaller, addmeth: AddMeth) -> None:
     @addmeth(tryfirst=True)
     def he_method1() -> None:
         pass
@@ -154,7 +154,7 @@ def test_adding_nonwrappers_tryfirst(hc: _HookCaller, addmeth: AddMeth) -> None:
     assert funcs(hc.get_hookimpls()) == [he_method1_middle, he_method1_b, he_method1]
 
 
-def test_adding_wrappers_ordering(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_wrappers_ordering(hc: HookCaller, addmeth: AddMeth) -> None:
     @addmeth(hookwrapper=True)
     def he_method1():
         yield
@@ -184,7 +184,7 @@ def test_adding_wrappers_ordering(hc: _HookCaller, addmeth: AddMeth) -> None:
     ]
 
 
-def test_adding_wrappers_ordering_tryfirst(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_wrappers_ordering_tryfirst(hc: HookCaller, addmeth: AddMeth) -> None:
     @addmeth(hookwrapper=True, tryfirst=True)
     def he_method1():
         yield
@@ -200,7 +200,7 @@ def test_adding_wrappers_ordering_tryfirst(hc: _HookCaller, addmeth: AddMeth) ->
     assert funcs(hc.get_hookimpls()) == [he_method2, he_method1, he_method3]
 
 
-def test_adding_wrappers_complex(hc: _HookCaller, addmeth: AddMeth) -> None:
+def test_adding_wrappers_complex(hc: HookCaller, addmeth: AddMeth) -> None:
     assert funcs(hc.get_hookimpls()) == []
 
     @addmeth(hookwrapper=True, trylast=True)
diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py
index c021ca1..81b86b6 100644
--- a/testing/test_pluginmanager.py
+++ b/testing/test_pluginmanager.py
@@ -276,7 +276,7 @@ def test_historic_with_subset_hook_caller(pm: PluginManager) -> None:
 
 @pytest.mark.parametrize("result_callback", [True, False])
 def test_with_result_memorized(pm: PluginManager, result_callback: bool) -> None:
-    """Verify that ``_HookCaller._maybe_apply_history()`
+    """Verify that ``HookCaller._maybe_apply_history()`
     correctly applies the ``result_callback`` function, when provided,
     to the result from calling each newly registered hook.
     """