Improve reference and path/fspath docs

Closes #9283
This commit is contained in:
Florian Bruhin 2021-11-26 15:40:17 +01:00
parent a335ade1f5
commit 70b989d517
5 changed files with 64 additions and 16 deletions

View File

@ -5,3 +5,8 @@ The following hooks now receive an additional ``pathlib.Path`` argument, equival
- :func:`pytest_pycollect_makemodule <_pytest.hookspec.pytest_pycollect_makemodule>` - The ``module_path`` parameter (equivalent to existing ``path`` parameter). - :func:`pytest_pycollect_makemodule <_pytest.hookspec.pytest_pycollect_makemodule>` - The ``module_path`` parameter (equivalent to existing ``path`` parameter).
- :func:`pytest_report_header <_pytest.hookspec.pytest_report_header>` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter). - :func:`pytest_report_header <_pytest.hookspec.pytest_report_header>` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter).
- :func:`pytest_report_collectionfinish <_pytest.hookspec.pytest_report_collectionfinish>` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter). - :func:`pytest_report_collectionfinish <_pytest.hookspec.pytest_report_collectionfinish>` - The ``start_path`` parameter (equivalent to existing ``startdir`` parameter).
.. note::
The name of the ``Node`` arguments and attributes (the new attribute being
``path``) is **the opposite** of the situation for hooks (the old argument
being ``path``).

View File

@ -1 +1,6 @@
Implement ``Node.path`` as a ``pathlib.Path``. Implement ``Node.path`` as a ``pathlib.Path``. This attribute gets set no matter whether ``path`` or (deprecated) ``fspath`` is passed to the constructor. It is a replacement for the ``fspath`` attribute (which represents the same path as ``py.path.local``). While ``fspath`` is not deprecated yet
due to the ongoing migration of methods like :meth:`~_pytest.Item.reportinfo`, we expect to deprecate it in a future release.
.. note::
The name of the attributes (old ``fspath``, new ``path``) is **the opposite**
of the situation for hooks.

View File

@ -53,9 +53,18 @@ Plugins which construct nodes should pass the ``path`` argument, of type
:class:`pathlib.Path`, instead of the ``fspath`` argument. :class:`pathlib.Path`, instead of the ``fspath`` argument.
Plugins which implement custom items and collectors are encouraged to replace Plugins which implement custom items and collectors are encouraged to replace
``py.path.local`` ``fspath`` parameters with ``pathlib.Path`` parameters, and ``fspath`` parameters (``py.path.local``) with ``path`` parameters
drop any other usage of the ``py`` library if possible. (``pathlib.Path``), and drop any other usage of the ``py`` library if possible.
.. note::
The name of the arguments (old ``fspath``, new ``path``) is **the opposite**
of the situation for hooks, :ref:`outlined below <_legacy-path-hooks-deprecated>`.
Due to the ongoing migration of methods like :meth:`~_pytest.Item.reportinfo`
which still is expected to return a ``py.path.local`` object, nodes still have
both ``fspath`` (``py.path.local``) and ``path`` (``pathlib.Path``) attributes,
no matter what argument was used in the constructor. We expect to deprecate the
``fspath`` attribute in a future release.
.. _legacy-path-hooks-deprecated: .. _legacy-path-hooks-deprecated:
@ -74,6 +83,10 @@ In order to support the transition from ``py.path.local`` to :mod:`pathlib`, the
The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments. The accompanying ``py.path.local`` based paths have been deprecated: plugins which manually invoke those hooks should only pass the new ``pathlib.Path`` arguments, and users should change their hook implementations to use the new ``pathlib.Path`` arguments.
.. note::
The name of the arguments (old ``path``, new ``fspath``) is **the opposite**
of the situation for the :class:`~_pytest.nodes.Node` class, :ref:`outlined above <_node-ctor-fspath-deprecation>`.
Directly constructing internal classes Directly constructing internal classes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -159,6 +159,13 @@ class Node(metaclass=NodeMeta):
Collector subclasses have children; Items are leaf nodes. Collector subclasses have children; Items are leaf nodes.
""" """
# Implemented in the legacypath plugin.
#: A ``LEGACY_PATH`` copy of the :attr:`path` attribute. Intended for usage
#: for methods not migrated to ``pathlib.Path`` yet, such as
#: :meth:`Item.reportinfo`. Will be deprecated in a future release, prefer
#: using :attr:`path` instead.
fspath: LEGACY_PATH
# Use __slots__ to make attribute access faster. # Use __slots__ to make attribute access faster.
# Note that __dict__ is still available. # Note that __dict__ is still available.
__slots__ = ( __slots__ = (
@ -188,26 +195,26 @@ class Node(metaclass=NodeMeta):
#: The parent collector node. #: The parent collector node.
self.parent = parent self.parent = parent
#: The pytest config object.
if config: if config:
#: The pytest config object.
self.config: Config = config self.config: Config = config
else: else:
if not parent: if not parent:
raise TypeError("config or parent must be provided") raise TypeError("config or parent must be provided")
self.config = parent.config self.config = parent.config
#: The pytest session this node is part of.
if session: if session:
#: The pytest session this node is part of.
self.session = session self.session = session
else: else:
if not parent: if not parent:
raise TypeError("session or parent must be provided") raise TypeError("session or parent must be provided")
self.session = parent.session self.session = parent.session
#: Filesystem path where this node was collected from (can be None).
if path is None and fspath is None: if path is None and fspath is None:
path = getattr(parent, "path", None) path = getattr(parent, "path", None)
self.path = _imply_path(type(self), path, fspath=fspath) #: Filesystem path where this node was collected from (can be None).
self.path: Path = _imply_path(type(self), path, fspath=fspath)
# The explicit annotation is to avoid publicly exposing NodeKeywords. # The explicit annotation is to avoid publicly exposing NodeKeywords.
#: Keywords/markers collected from all scopes. #: Keywords/markers collected from all scopes.
@ -478,6 +485,8 @@ class Node(metaclass=NodeMeta):
) -> Union[str, TerminalRepr]: ) -> Union[str, TerminalRepr]:
"""Return a representation of a collection or test failure. """Return a representation of a collection or test failure.
.. seealso:: :ref:`non-python tests`
:param excinfo: Exception information for the failure. :param excinfo: Exception information for the failure.
""" """
return self._repr_failure_py(excinfo, style) return self._repr_failure_py(excinfo, style)
@ -686,6 +695,12 @@ class Item(Node):
self.user_properties: List[Tuple[str, object]] = [] self.user_properties: List[Tuple[str, object]] = []
def runtest(self) -> None: def runtest(self) -> None:
"""Run the test case for this item.
Must be implemented by subclasses.
.. seealso:: :ref:`non-python tests`
"""
raise NotImplementedError("runtest must be implemented by Item subclass") raise NotImplementedError("runtest must be implemented by Item subclass")
def add_report_section(self, when: str, key: str, content: str) -> None: def add_report_section(self, when: str, key: str, content: str) -> None:
@ -706,6 +721,16 @@ class Item(Node):
self._report_sections.append((when, key, content)) self._report_sections.append((when, key, content))
def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]: def reportinfo(self) -> Tuple[Union["os.PathLike[str]", str], Optional[int], str]:
"""Get location information for this item for test reports.
Returns a tuple with three elements:
- The path of the test (default ``self.path``)
- The line number of the test (default ``None``)
- A name of the test to be shown (default ``""``)
.. seealso:: :ref:`non-python tests`
"""
return self.path, None, "" return self.path, None, ""
@cached_property @cached_property

View File

@ -1578,26 +1578,26 @@ def write_docstring(tw: TerminalWriter, doc: str, indent: str = " ") -> None:
class Function(PyobjMixin, nodes.Item): class Function(PyobjMixin, nodes.Item):
"""An Item responsible for setting up and executing a Python test function. """An Item responsible for setting up and executing a Python test function.
param name: :param name:
The full function name, including any decorations like those The full function name, including any decorations like those
added by parametrization (``my_func[my_param]``). added by parametrization (``my_func[my_param]``).
param parent: :param parent:
The parent Node. The parent Node.
param config: :param config:
The pytest Config object. The pytest Config object.
param callspec: :param callspec:
If given, this is function has been parametrized and the callspec contains If given, this is function has been parametrized and the callspec contains
meta information about the parametrization. meta information about the parametrization.
param callobj: :param callobj:
If given, the object which will be called when the Function is invoked, If given, the object which will be called when the Function is invoked,
otherwise the callobj will be obtained from ``parent`` using ``originalname``. otherwise the callobj will be obtained from ``parent`` using ``originalname``.
param keywords: :param keywords:
Keywords bound to the function object for "-k" matching. Keywords bound to the function object for "-k" matching.
param session: :param session:
The pytest Session object. The pytest Session object.
param fixtureinfo: :param fixtureinfo:
Fixture information already resolved at this fixture node.. Fixture information already resolved at this fixture node..
param originalname: :param originalname:
The attribute name to use for accessing the underlying function object. The attribute name to use for accessing the underlying function object.
Defaults to ``name``. Set this if name is different from the original name, Defaults to ``name``. Set this if name is different from the original name,
for example when it contains decorations like those added by parametrization for example when it contains decorations like those added by parametrization