nodes.py
Overview
The [nodes.py](/projects/286/67419) file defines the core abstraction and base classes for pytest's **test collection tree nodes**. This includes both **collectors** (nodes that contain other nodes, typically directories, packages, modules, classes) and **items** (leaf nodes representing runnable test cases). It establishes a hierarchy of node types to represent the structure of a test suite as discovered by pytest.
Key responsibilities of this file include:
Defining the fundamental Node class that all test collection nodes inherit from.
Implementing the Collector class, which serves as a base for nodes that collect children nodes.
Implementing the Item class, which represents an individual test that can be executed.
Providing filesystem-specific collectors (
FSCollector,File,Directory) specialized for collecting tests from filesystem entities.Managing node construction patterns via a metaclass (
NodeMeta) that enforces usage of thefrom_parentfactory method.Handling markers, keywords, and warnings on nodes.
Providing utility functions for location extraction and traceback filtering.
Defining error handling and failure representation related to collection and test execution.
This file plays a foundational role in how pytest builds the **collection tree**, which represents the entire test suite structure, enabling discovery, parametrization, setup/teardown management, and test execution orchestration.
Detailed Explanation of Classes and Functions
Helper Functions
_imply_path(node_type: type[Node], path: Path | None, fspath: LEGACY_PATH | None) -> Path
Purpose: Resolves the filesystem path for a node, handling legacy
fspatharguments for backward compatibility and warning about deprecated usage.Parameters:
node_type: The class of the node being constructed.path: A modernpathlib.Pathobject orNone.fspath: A legacy path object orNone.
Returns: A
Pathobject representing the node's filesystem location.Notes: Raises a deprecation warning if
fspathis used and ensures consistency if bothpathandfspathare provided.
Class NodeMeta(abc.ABCMeta)
Purpose: Metaclass used by
Nodeto enforce that nodes are constructed via thefrom_parentclass method, preventing direct instantiation.Key behavior:
Overrides
__call__to raise an error if direct instantiation is attempted.Provides a
_createhelper for subclasses to internally create instances bypassing this restriction.
Rationale: Enforces a clean and uniform construction pattern during pytest's refactoring of node creation.
Class Node(abc.ABC, metaclass=NodeMeta)
Purpose: Abstract base class for all nodes in the test collection tree.
Key attributes:
name: Unique name of the node within its parent.parent: The parent node orNoneif root.config: Pytest configuration.session: The pytest test session.path: Filesystem path associated with the node.keywords: Mapping of keywords/markers associated with the node.own_markers: List of markers directly applied to this node.extra_keyword_matches: Additional keywords for matching.stash: Plugin storage space._nodeid: Unique identifier string for the node.
Construction: Use Node.from_parent(parent, **kwargs) as the public constructor.
Key Methods:
from_parent(cls, parent: Node, **kw): Factory constructor enforcing parent linkage.warn(warning: Warning): Issue a warning related to this node.iter_parents(): Iterator over self and all parents up to root.listchain(): Returns list of nodes from root to self.add_marker(marker, append=True): Dynamically add a marker.iter_markers(name=None): Iterate all markers, optionally filtered by name.get_closest_marker(name, default=None): Return the first marker matching name from closest to farthest parent.addfinalizer(fin): Register a finalizer function to be called during teardown.getparent(cls): Get the closest parent node of a given class.repr_failure(excinfo, style=None): Return a representation of a failure.nodeidproperty: Returns the unique node id string.
Usage example:
parent_node = some_collector_node
child_node = Node.from_parent(parent=parent_node, name="child")
child_node.add_marker("slow")
child_node.warn(UserWarning("This test is slow"))
Function get_fslocation_from_item(node: Node) -> tuple[str | Path, int | None]
Purpose: Extracts the source location (file path and line number) for a node.
Parameters:
node: The node to query location from.
Returns: Tuple of
(filename, lineno)with lineno zero-based;linenocan beNoneif unknown.Implementation details: Checks for
locationattribute, thenobj(Python object), then falls back topath, returning"unknown location"if none available.
Class Collector(Node, abc.ABC)
Purpose: Abstract base class for nodes that collect other nodes (internal nodes in the collection tree).
Key attributes: Inherits all from
Node.Key methods:
collect() -> Iterable[Item | Collector]: Abstract method to collect child nodes.repr_failure(excinfo): Return a representation of collection failures, with special handling forCollectError._traceback_filter(excinfo): Filters traceback to exclude irrelevant frames (e.g., internal pytest frames).
Nested Exception:
CollectError: Custom exception used to signal collection errors with user-friendly messages.
Usage example:
class MyCollector(Collector):
def collect(self):
# Collect child nodes here
yield some_item
Class FSCollector(Collector, abc.ABC)
Purpose: Base class for collectors representing filesystem entities (files, directories).
Constructor parameters:
fspath,path_or_parent,path,name,parent,config,session,nodeid
Special behavior:
Resolves node path intelligently from parameters.
Automatically determines
nodeidrelative to session root path.
Usage: Extended by
FileandDirectoryclasses.Usage example:
file_collector = File.from_parent(parent=some_directory_collector, path=Path("test_example.py"))
Class File(FSCollector, abc.ABC)
Purpose: Abstract base class for file-level collectors that collect tests from a single file.
Notes: Supports non-Python tests.
Usage: Extended by concrete file collectors like
Module.
Class Directory(FSCollector, abc.ABC)
Purpose: Abstract base class for directory-level collectors that collect files and subdirectories.
Behavior: Uses pytest hooks (
pytest_collect_directory,pytest_collect_file,pytest_ignore_collect) to decide what to collect.Usage: Extended by default directory collectors like
DirandPackage.
Class Item(Node, abc.ABC)
Purpose: Abstract base class for leaf nodes, representing individual test invocations.
Attributes:
nextitem: Reference to the next test item (used for test ordering)._report_sections: List of tuples(when, key, content)for adding extra report sections.user_properties: List of(name, value)tuples for user-defined properties.
Key methods:
runtest(): Abstract method to run the test case.add_report_section(when, key, content): Add content to the test report.reportinfo(): Returns(path, lineno, testname)for reporting.location: Cached property returning relative location(relfspath, lineno, testname).
Constructor note: Checks for diamond inheritance issues involving
CollectorandItemto warn users.Usage example:
class MyTest(Item):
def runtest(self):
assert 1 + 1 == 2
test_item = MyTest.from_parent(parent=some_collector, name="test_addition")
test_item.runtest()
Function _check_initialpaths_for_relpath(initial_paths: frozenset[Path], path: Path) -> str | None
Purpose: Checks if the given
pathis within any of theinitial_pathsand returns a relative path string or empty string.Used for: Generating node IDs relative to initial collection root paths.
Caching: Decorated with
lru_cachefor performance.
Important Implementation Details and Algorithms
Node Construction Enforcement:
TheNodeMetametaclass prevents direct instantiation ofNodesubclasses, enforcing the use offrom_parent. This centralizes node creation and aids in refactoring.Node Identification:
Nodes have uniquenodeids representing their address in the collection tree, constructed hierarchically using"::"separators.Marker and Keyword Management:
Nodes maintain markers and keywords that influence selection, skipping, or parametrization behaviors.Warning Emission:
Nodes can emit warnings tied to their location usingwarn(), which integrates with Python's warning system and pytest's reporting.Failure Representation:
Nodes implementrepr_failure()to provide rich, configurable traceback or error representations, including filtering internal frames and handling verbosity levels.Filesystem Node ID Resolution:
FSCollectorresolves node IDs relative to the root or initial paths, converting OS-dependent separators to slashes (/).Setup and Teardown Finalizers:
Nodes provide mechanisms (addfinalizer) to register cleanup callbacks, integrated with pytest's setup/teardown lifecycle.
Interaction with Other Parts of the System
Session:
Each node is linked to aSessionobject representing the entire test run lifecycle. Session provides hooks, configuration, and global context.Config:
Nodes reference the pytestConfigfor access to configuration options, which influence behaviors such as traceback style or verbosity.Hooks (pluggy):
Nodes interact with pytest's hook system viaihookto call filesystem-sensitive hooks likepytest_collect_fileorpytest_pycollect_makeitem.Legacy Path Compatibility:
Nodes support a legacyfspathattribute for backward compatibility, with a clear migration path topathlib.Path.Markers and Keywords:
Integration with themarksubsystem allows nodes to carry marker information used during collection and execution.Warnings and Reporting:
Warnings issued by nodes are integrated into pytest's reporting and can be suppressed or displayed after the test session.ExceptionInfo and Traceback:
Nodes use_pytest._codeutilities to manipulate exception tracebacks and format error reports.
Mermaid Class Diagram
classDiagram
class NodeMeta {
<<metaclass>>
+__call__(*args, **kwargs) : NoReturn
+_create(*args, **kwargs) : Node
}
class Node {
+name: str
+parent: Node | None
+config: Config
+session: Session
+path: pathlib.Path
+keywords: MutableMapping[str, Any]
+own_markers: list[Mark]
+extra_keyword_matches: set[str]
+stash: Stash
+nodeid: str
+from_parent(parent: Node, **kw) : Self
+warn(warning: Warning) : None
+iter_parents() : Iterator[Node]
+listchain() : list[Node]
+add_marker(marker: str | MarkDecorator, append: bool) : None
+iter_markers(name: str | None) : Iterator[Mark]
+get_closest_marker(name: str, default: Mark | None) : Mark | None
+addfinalizer(fin: Callable) : None
+getparent(cls) : Node | None
+repr_failure(excinfo: ExceptionInfo, style: TracebackStyle | None) : str | TerminalRepr
}
class Collector {
+collect() : Iterable[Item | Collector]
+repr_failure(excinfo: ExceptionInfo) : str | TerminalRepr
}
class FSCollector {
+__init__(...)
}
class File {
}
class Directory {
}
class Item {
+nextitem: Item | None
+_report_sections: list[tuple[str, str, str]]
+user_properties: list[tuple[str, object]]
+runtest() : None
+add_report_section(when: str, key: str, content: str) : None
+reportinfo() : tuple
+location() : tuple
}
NodeMeta <|-- Node
Node <|-- Collector
Collector <|-- FSCollector
FSCollector <|-- File
FSCollector <|-- Directory
Node <|-- Item
Summary
The [nodes.py](/projects/286/67419) file defines the **abstract syntax and behavior of pytest's collection tree nodes**. It provides base classes and foundational utilities that enable pytest to structure discovered tests in a hierarchical and extensible manner. The strict node construction pattern, marker and keyword management, detailed failure representation, and seamless integration with the pytest session and configuration make it a cornerstone of pytest’s test discovery and execution framework.
By defining collectors and items with clear responsibilities and interfaces, this module allows pytest to flexibly build and traverse the test suite tree, supporting powerful features like parametrization, test selection, reporting, and plugin extensibility.