summaryrefslogtreecommitdiff
path: root/src/_pytest/nodes.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/_pytest/nodes.py')
-rw-r--r--src/_pytest/nodes.py153
1 files changed, 75 insertions, 78 deletions
diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py
index d53d591e7..cc1cc7ebd 100644
--- a/src/_pytest/nodes.py
+++ b/src/_pytest/nodes.py
@@ -66,19 +66,23 @@ def _splitnode(nodeid: str) -> Tuple[str, ...]:
['testing', 'code', 'test_excinfo.py', 'TestFormattedExcinfo']
"""
if nodeid == "":
- # If there is no root node at all, return an empty list so the caller's logic can remain sane
+ # If there is no root node at all, return an empty list so the caller's
+ # logic can remain sane.
return ()
parts = nodeid.split(SEP)
- # Replace single last element 'test_foo.py::Bar' with multiple elements 'test_foo.py', 'Bar'
+ # Replace single last element 'test_foo.py::Bar' with multiple elements
+ # 'test_foo.py', 'Bar'.
parts[-1:] = parts[-1].split("::")
- # Convert parts into a tuple to avoid possible errors with caching of a mutable type
+ # Convert parts into a tuple to avoid possible errors with caching of a
+ # mutable type.
return tuple(parts)
def ischildnode(baseid: str, nodeid: str) -> bool:
"""Return True if the nodeid is a child node of the baseid.
- E.g. 'foo/bar::Baz' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz', but not of 'foo/blorp'
+ E.g. 'foo/bar::Baz' is a child of 'foo', 'foo/bar' and 'foo/bar::Baz',
+ but not of 'foo/blorp'.
"""
base_parts = _splitnode(baseid)
node_parts = _splitnode(nodeid)
@@ -100,8 +104,11 @@ class NodeMeta(type):
class Node(metaclass=NodeMeta):
- """ base class for Collector and Item the test collection tree.
- Collector subclasses have children, Items are terminal nodes."""
+ """Base class for Collector and Item, the components of the test
+ collection tree.
+
+ Collector subclasses have children; Items are leaf nodes.
+ """
# Use __slots__ to make attribute access faster.
# Note that __dict__ is still available.
@@ -125,13 +132,13 @@ class Node(metaclass=NodeMeta):
fspath: Optional[py.path.local] = None,
nodeid: Optional[str] = None,
) -> None:
- #: a unique name within the scope of the parent node
+ #: A unique name within the scope of the parent node.
self.name = name
- #: the parent collector node.
+ #: The parent collector node.
self.parent = parent
- #: the pytest config object
+ #: The pytest config object.
if config:
self.config = config # type: Config
else:
@@ -139,7 +146,7 @@ class Node(metaclass=NodeMeta):
raise TypeError("config or parent must be provided")
self.config = parent.config
- #: the session this node is part of
+ #: The pytest session this node is part of.
if session:
self.session = session
else:
@@ -147,19 +154,19 @@ class Node(metaclass=NodeMeta):
raise TypeError("session or parent must be provided")
self.session = parent.session
- #: filesystem path where this node was collected from (can be None)
+ #: Filesystem path where this node was collected from (can be None).
self.fspath = fspath or getattr(parent, "fspath", None)
- #: keywords/markers collected from all scopes
+ #: Keywords/markers collected from all scopes.
self.keywords = NodeKeywords(self)
- #: the marker objects belonging to this node
+ #: The marker objects belonging to this node.
self.own_markers = [] # type: List[Mark]
- #: allow adding of extra keywords to use for matching
+ #: Allow adding of extra keywords to use for matching.
self.extra_keyword_matches = set() # type: Set[str]
- # used for storing artificial fixturedefs for direct parametrization
+ # Used for storing artificial fixturedefs for direct parametrization.
self._name2pseudofixturedef = {} # type: Dict[str, FixtureDef]
if nodeid is not None:
@@ -178,15 +185,15 @@ class Node(metaclass=NodeMeta):
@classmethod
def from_parent(cls, parent: "Node", **kw):
- """
- Public Constructor for Nodes
+ """Public constructor for Nodes.
This indirection got introduced in order to enable removing
the fragile logic from the node constructors.
- Subclasses can use ``super().from_parent(...)`` when overriding the construction
+ Subclasses can use ``super().from_parent(...)`` when overriding the
+ construction.
- :param parent: the parent node of this test Node
+ :param parent: The parent node of this Node.
"""
if "config" in kw:
raise TypeError("config is not a valid argument for from_parent")
@@ -196,27 +203,27 @@ class Node(metaclass=NodeMeta):
@property
def ihook(self):
- """ fspath sensitive hook proxy used to call pytest hooks"""
+ """fspath-sensitive hook proxy used to call pytest hooks."""
return self.session.gethookproxy(self.fspath)
def __repr__(self) -> str:
return "<{} {}>".format(self.__class__.__name__, getattr(self, "name", None))
def warn(self, warning: "PytestWarning") -> None:
- """Issue a warning for this item.
+ """Issue a warning for this Node.
- Warnings will be displayed after the test session, unless explicitly suppressed
+ Warnings will be displayed after the test session, unless explicitly suppressed.
- :param Warning warning: the warning instance to issue. Must be a subclass of PytestWarning.
+ :param Warning warning:
+ The warning instance to issue. Must be a subclass of PytestWarning.
- :raise ValueError: if ``warning`` instance is not a subclass of PytestWarning.
+ :raises ValueError: If ``warning`` instance is not a subclass of PytestWarning.
Example usage:
.. code-block:: python
node.warn(PytestWarning("some message"))
-
"""
from _pytest.warning_types import PytestWarning
@@ -232,10 +239,11 @@ class Node(metaclass=NodeMeta):
warning, category=None, filename=str(path), lineno=lineno + 1,
)
- # methods for ordering nodes
+ # Methods for ordering nodes.
+
@property
def nodeid(self) -> str:
- """ a ::-separated string denoting its collection tree address. """
+ """A ::-separated string denoting its collection tree address."""
return self._nodeid
def __hash__(self) -> int:
@@ -248,8 +256,8 @@ class Node(metaclass=NodeMeta):
pass
def listchain(self) -> List["Node"]:
- """ return list of all parent collectors up to self,
- starting from root of collection tree. """
+ """Return list of all parent collectors up to self, starting from
+ the root of collection tree."""
chain = []
item = self # type: Optional[Node]
while item is not None:
@@ -261,12 +269,10 @@ class Node(metaclass=NodeMeta):
def add_marker(
self, marker: Union[str, MarkDecorator], append: bool = True
) -> None:
- """dynamically add a marker object to the node.
+ """Dynamically add a marker object to the node.
- :type marker: ``str`` or ``pytest.mark.*`` object
- :param marker:
- ``append=True`` whether to append the marker,
- if ``False`` insert at position ``0``.
+ :param append:
+ Whether to append the marker, or prepend it.
"""
from _pytest.mark import MARK_GEN
@@ -283,21 +289,19 @@ class Node(metaclass=NodeMeta):
self.own_markers.insert(0, marker_.mark)
def iter_markers(self, name: Optional[str] = None) -> Iterator[Mark]:
- """
- :param name: if given, filter the results by the name attribute
+ """Iterate over all markers of the node.
- iterate over all markers of the node
+ :param name: If given, filter the results by the name attribute.
"""
return (x[1] for x in self.iter_markers_with_node(name=name))
def iter_markers_with_node(
self, name: Optional[str] = None
) -> Iterator[Tuple["Node", Mark]]:
- """
- :param name: if given, filter the results by the name attribute
+ """Iterate over all markers of the node.
- iterate over all markers of the node
- returns sequence of tuples (node, mark)
+ :param name: If given, filter the results by the name attribute.
+ :returns: An iterator of (node, mark) tuples.
"""
for node in reversed(self.listchain()):
for mark in node.own_markers:
@@ -315,16 +319,16 @@ class Node(metaclass=NodeMeta):
def get_closest_marker( # noqa: F811
self, name: str, default: Optional[Mark] = None
) -> Optional[Mark]:
- """return the first marker matching the name, from closest (for example function) to farther level (for example
- module level).
+ """Return the first marker matching the name, from closest (for
+ example function) to farther level (for example module level).
- :param default: fallback return value of no marker was found
- :param name: name to filter by
+ :param default: Fallback return value if no marker was found.
+ :param name: Name to filter by.
"""
return next(self.iter_markers(name=name), default)
def listextrakeywords(self) -> Set[str]:
- """ Return a set of all extra keywords in self and any parents."""
+ """Return a set of all extra keywords in self and any parents."""
extra_keywords = set() # type: Set[str]
for item in self.listchain():
extra_keywords.update(item.extra_keyword_matches)
@@ -334,7 +338,7 @@ class Node(metaclass=NodeMeta):
return [x.name for x in self.listchain()]
def addfinalizer(self, fin: Callable[[], object]) -> None:
- """ register a function to be called when this node is finalized.
+ """Register a function to be called when this node is finalized.
This method can only be called when this node is active
in a setup chain, for example during self.setup().
@@ -342,8 +346,8 @@ class Node(metaclass=NodeMeta):
self.session._setupstate.addfinalizer(fin, self)
def getparent(self, cls: "Type[_NodeType]") -> Optional[_NodeType]:
- """ get the next parent node (including ourself)
- which is an instance of the given class"""
+ """Get the next parent node (including self) which is an instance of
+ the given class."""
current = self # type: Optional[Node]
while current and not isinstance(current, cls):
current = current.parent
@@ -411,8 +415,7 @@ class Node(metaclass=NodeMeta):
excinfo: ExceptionInfo[BaseException],
style: "Optional[_TracebackStyle]" = None,
) -> Union[str, TerminalRepr]:
- """
- Return a representation of a collection or test failure.
+ """Return a representation of a collection or test failure.
:param excinfo: Exception information for the failure.
"""
@@ -422,13 +425,13 @@ class Node(metaclass=NodeMeta):
def get_fslocation_from_item(
node: "Node",
) -> Tuple[Union[str, py.path.local], Optional[int]]:
- """Tries to extract the actual location from a node, depending on available attributes:
+ """Try to extract the actual location from a node, depending on available attributes:
* "location": a pair (path, lineno)
* "obj": a Python object that the node wraps.
* "fspath": just a path
- :rtype: a tuple of (str|LocalPath, int) with filename and line number.
+ :rtype: A tuple of (str|py.path.local, int) with filename and line number.
"""
# See Item.location.
location = getattr(
@@ -443,25 +446,22 @@ def get_fslocation_from_item(
class Collector(Node):
- """ Collector instances create children through collect()
- and thus iteratively build a tree.
- """
+ """Collector instances create children through collect() and thus
+ iteratively build a tree."""
class CollectError(Exception):
- """ an error during collection, contains a custom message. """
+ """An error during collection, contains a custom message."""
def collect(self) -> Iterable[Union["Item", "Collector"]]:
- """ returns a list of children (items and collectors)
- for this collection node.
- """
+ """Return a list of children (items and collectors) for this
+ collection node."""
raise NotImplementedError("abstract")
# TODO: This omits the style= parameter which breaks Liskov Substitution.
def repr_failure( # type: ignore[override]
self, excinfo: ExceptionInfo[BaseException]
) -> Union[str, TerminalRepr]:
- """
- Return a representation of a collection failure.
+ """Return a representation of a collection failure.
:param excinfo: Exception information for the failure.
"""
@@ -538,24 +538,22 @@ class FSCollector(Collector):
@classmethod
def from_parent(cls, parent, *, fspath, **kw):
- """
- The public constructor
- """
+ """The public constructor."""
return super().from_parent(parent=parent, fspath=fspath, **kw)
def _gethookproxy(self, fspath: py.path.local):
- # check if we have the common case of running
- # hooks with all conftest.py files
+ # Check if we have the common case of running
+ # hooks with all conftest.py files.
pm = self.config.pluginmanager
my_conftestmodules = pm._getconftestmodules(
fspath, self.config.getoption("importmode")
)
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
if remove_mods:
- # one or more conftests are not in use at this fspath
+ # One or more conftests are not in use at this fspath.
proxy = FSHookProxy(pm, remove_mods)
else:
- # all plugins are active for this fspath
+ # All plugins are active for this fspath.
proxy = self.config.hook
return proxy
@@ -605,12 +603,13 @@ class FSCollector(Collector):
class File(FSCollector):
- """ base class for collecting tests from a file. """
+ """Base class for collecting tests from a file."""
class Item(Node):
- """ a basic test invocation item. Note that for a single function
- there might be multiple test invocation items.
+ """A basic test invocation item.
+
+ Note that for a single function there might be multiple test invocation items.
"""
nextitem = None
@@ -626,17 +625,16 @@ class Item(Node):
super().__init__(name, parent, config, session, nodeid=nodeid)
self._report_sections = [] # type: List[Tuple[str, str, str]]
- #: user properties is a list of tuples (name, value) that holds user
- #: defined properties for this test.
+ #: A list of tuples (name, value) that holds user defined properties
+ #: for this test.
self.user_properties = [] # type: List[Tuple[str, object]]
def runtest(self) -> None:
raise NotImplementedError("runtest must be implemented by Item subclass")
def add_report_section(self, when: str, key: str, content: str) -> None:
- """
- Adds a new report section, similar to what's done internally to add stdout and
- stderr captured output::
+ """Add a new report section, similar to what's done internally to add
+ stdout and stderr captured output::
item.add_report_section("call", "stdout", "report section contents")
@@ -645,7 +643,6 @@ class Item(Node):
:param str key:
Name of the section, can be customized at will. Pytest uses ``"stdout"`` and
``"stderr"`` internally.
-
:param str content:
The full contents as a string.
"""