diff options
author | robot-piglet <robot-piglet@yandex-team.com> | 2025-05-05 12:31:52 +0300 |
---|---|---|
committer | robot-piglet <robot-piglet@yandex-team.com> | 2025-05-05 12:41:33 +0300 |
commit | 6ff49ec58061f642c3a2f83c61eba12820787dfc (patch) | |
tree | c733ec9bdb15ed280080d31dea8725bfec717acd /contrib/python/pytest/py3/_pytest/nodes.py | |
parent | eefca8305c6a545cc6b16dca3eb0d91dcef2adcd (diff) | |
download | ydb-6ff49ec58061f642c3a2f83c61eba12820787dfc.tar.gz |
Intermediate changes
commit_hash:8b3bb826b17db8329ed1221f545c0645f12c552d
Diffstat (limited to 'contrib/python/pytest/py3/_pytest/nodes.py')
-rw-r--r-- | contrib/python/pytest/py3/_pytest/nodes.py | 80 |
1 files changed, 55 insertions, 25 deletions
diff --git a/contrib/python/pytest/py3/_pytest/nodes.py b/contrib/python/pytest/py3/_pytest/nodes.py index a5313cb7656..73efe156e2f 100644 --- a/contrib/python/pytest/py3/_pytest/nodes.py +++ b/contrib/python/pytest/py3/_pytest/nodes.py @@ -1,6 +1,8 @@ -import os -import warnings +import abc +from functools import cached_property from inspect import signature +import os +import pathlib from pathlib import Path from typing import Any from typing import Callable @@ -17,16 +19,19 @@ from typing import Type from typing import TYPE_CHECKING from typing import TypeVar from typing import Union +import warnings + +import pluggy import _pytest._code from _pytest._code import getfslineno from _pytest._code.code import ExceptionInfo from _pytest._code.code import TerminalRepr from _pytest._code.code import Traceback -from _pytest.compat import cached_property from _pytest.compat import LEGACY_PATH from _pytest.config import Config from _pytest.config import ConftestImportFailure +from _pytest.config.compat import _check_path from _pytest.deprecated import FSCOLLECTOR_GETHOOKPROXY_ISINITPATH from _pytest.deprecated import NODE_CTOR_FSPATH_ARG from _pytest.mark.structures import Mark @@ -38,10 +43,11 @@ from _pytest.pathlib import commonpath from _pytest.stash import Stash from _pytest.warning_types import PytestWarning + if TYPE_CHECKING: # Imported here due to circular import. - from _pytest.main import Session from _pytest._code.code import _TracebackStyle + from _pytest.main import Session SEP = "/" @@ -94,14 +100,6 @@ def iterparentnodeids(nodeid: str) -> Iterator[str]: yield nodeid -def _check_path(path: Path, fspath: LEGACY_PATH) -> None: - if Path(fspath) != path: - raise ValueError( - f"Path({fspath!r}) != {path!r}\n" - "if both path and fspath are given they need to be equal" - ) - - def _imply_path( node_type: Type["Node"], path: Optional[Path], @@ -126,7 +124,21 @@ def _imply_path( _NodeType = TypeVar("_NodeType", bound="Node") -class NodeMeta(type): +class NodeMeta(abc.ABCMeta): + """Metaclass used by :class:`Node` to enforce that direct construction raises + :class:`Failed`. + + This behaviour supports the indirection introduced with :meth:`Node.from_parent`, + the named constructor to be used instead of direct construction. The design + decision to enforce indirection with :class:`NodeMeta` was made as a + temporary aid for refactoring the collection tree, which was diagnosed to + have :class:`Node` objects whose creational patterns were overly entangled. + Once the refactoring is complete, this metaclass can be removed. + + See https://github.com/pytest-dev/pytest/projects/3 for an overview of the + progress on detangling the :class:`Node` classes. + """ + def __call__(self, *k, **kw): msg = ( "Direct construction of {name} has been deprecated, please use {name}.from_parent.\n" @@ -156,7 +168,7 @@ class NodeMeta(type): return super().__call__(*k, **known_kw) -class Node(metaclass=NodeMeta): +class Node(abc.ABC, metaclass=NodeMeta): r"""Base class of :class:`Collector` and :class:`Item`, the components of the test collection tree. @@ -167,9 +179,9 @@ class Node(metaclass=NodeMeta): # 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. + #: :meth:`Item.reportinfo <pytest.Item.reportinfo>`. Will be deprecated in fspath: LEGACY_PATH + #: a future release, prefer using :attr:`path` instead. # Use __slots__ to make attribute access faster. # Note that __dict__ is still available. @@ -219,7 +231,7 @@ class Node(metaclass=NodeMeta): if path is None and fspath is None: path = getattr(parent, "path", None) #: Filesystem path where this node was collected from (can be None). - self.path: Path = _imply_path(type(self), path, fspath=fspath) + self.path: pathlib.Path = _imply_path(type(self), path, fspath=fspath) # The explicit annotation is to avoid publicly exposing NodeKeywords. #: Keywords/markers collected from all scopes. @@ -264,7 +276,7 @@ class Node(metaclass=NodeMeta): return cls._create(parent=parent, **kw) @property - def ihook(self): + def ihook(self) -> pluggy.HookRelay: """fspath-sensitive hook proxy used to call pytest hooks.""" return self.session.gethookproxy(self.path) @@ -295,9 +307,7 @@ class Node(metaclass=NodeMeta): # enforce type checks here to avoid getting a generic type error later otherwise. if not isinstance(warning, Warning): raise ValueError( - "warning must be an instance of Warning or subclass, got {!r}".format( - warning - ) + f"warning must be an instance of Warning or subclass, got {warning!r}" ) path, lineno = get_fslocation_from_item(self) assert lineno is not None @@ -525,7 +535,7 @@ def get_fslocation_from_item(node: "Node") -> Tuple[Union[str, Path], Optional[i return getattr(node, "fspath", "unknown location"), -1 -class Collector(Node): +class Collector(Node, abc.ABC): """Base class of all collectors. Collector create children through `collect()` and thus iteratively build @@ -535,6 +545,7 @@ class Collector(Node): class CollectError(Exception): """An error during collection, contains a custom message.""" + @abc.abstractmethod def collect(self) -> Iterable[Union["Item", "Collector"]]: """Collect children (items and collectors) for this collector.""" raise NotImplementedError("abstract") @@ -579,7 +590,7 @@ def _check_initialpaths_for_relpath(session: "Session", path: Path) -> Optional[ return None -class FSCollector(Collector): +class FSCollector(Collector, abc.ABC): """Base class for filesystem collectors.""" def __init__( @@ -657,14 +668,32 @@ class FSCollector(Collector): return self.session.isinitpath(path) -class File(FSCollector): +class File(FSCollector, abc.ABC): """Base class for collecting tests from a file. :ref:`non-python tests`. """ -class Item(Node): +class Directory(FSCollector, abc.ABC): + """Base class for collecting files from a directory. + + A basic directory collector does the following: goes over the files and + sub-directories in the directory and creates collectors for them by calling + the hooks :hook:`pytest_collect_directory` and :hook:`pytest_collect_file`, + after checking that they are not ignored using + :hook:`pytest_ignore_collect`. + + The default directory collectors are :class:`~pytest.Dir` and + :class:`~pytest.Package`. + + .. versionadded:: 8.0 + + :ref:`custom directory collectors`. + """ + + +class Item(Node, abc.ABC): """Base class of all test invocation items. Note that for a single function there might be multiple test invocation items. @@ -730,6 +759,7 @@ class Item(Node): PytestWarning, ) + @abc.abstractmethod def runtest(self) -> None: """Run the test case for this item. |