main.py


Overview

`main.py` contains the core implementation of the testing process for the pytest framework. It handles the initialization of the testing session, test collection, the main test execution loop, and command-line option parsing. This file is central to pytest’s operation as it defines how tests are gathered from the filesystem or Python packages, how the test session lifecycle is managed, and how test execution is controlled.

Main responsibilities include:


Classes and Functions

pytest_addoption(parser: Parser) -> None

Registers command-line options and ini-file configurations used for controlling test runs, warnings, collection, and debugging.

parser = Parser()
pytest_addoption(parser)

validate_basetemp(path: str) -> str

Validates the `--basetemp` directory path passed as a command-line argument. Ensures the path is not empty, the current working directory, or any of its ancestor directories.


wrap_session(config: Config, doit: Callable[[Config, Session], int | ExitCode | None]) -> int | ExitCode

The main wrapper for running a test session. It initializes the `Session` object, manages configuration, runs the test session lifecycle hooks, and handles exceptions and exit codes.

exit_status = wrap_session(config, _main)

pytest_cmdline_main(config: Config) -> int | ExitCode

Entrypoint for the pytest command line main function. Delegates to `wrap_session` with `_main` as the test execution function.


_main(config: Config, session: Session) -> int | ExitCode | None

Core command-line protocol performing test collection and running the test loop.


pytest_collection(session: Session) -> None

Default hook implementation to perform test collection by calling `session.perform_collect()`.


pytest_runtestloop(session: Session) -> bool

Runs the main test execution loop over collected test items.


_in_venv(path: Path) -> bool

Helper to detect whether a directory is a Python virtual environment root.


pytest_ignore_collect(collection_path: Path, config: Config) -> bool | None

Determines if a given path should be ignored during test collection.


pytest_collect_directory(path: Path, parent: nodes.Collector) -> nodes.Collector | None

Collects a directory as a `Dir` collector node.


pytest_collection_modifyitems(items: list[nodes.Item], config: Config) -> None

Modifies the list of collected test items by deselecting any items whose node ID starts with prefixes supplied by `--deselect`.


class FSHookProxy

A proxy for pytest hook calls that excludes certain plugins (modules). Used internally to isolate hook calls for specific filesystem paths.


class Interrupted(KeyboardInterrupt)

Exception indicating that the test run was interrupted by the user or system.


class Failed(Exception)

Exception indicating the test run failed and should stop.


class _bestrelpath_cache(dict[Path, str])

A cache to optimize repeated calls to `bestrelpath()` for paths relative to a base path.


class Dir(nodes.Directory)

Collector representing a directory in the filesystem. Collects both subdirectories and files as test nodes.


class Session(nodes.Collector)

Represents the root of the test collection tree and manages the entire lifecycle of the test run.

session = Session.from_config(config)
collected_items = session.perform_collect()
for item in session.genitems(session):
    # run or analyze test item

search_pypath(module_name: str, *, consider_namespace_packages: bool = False) -> str | None

Searches `sys.path` for a given dotted Python module name and returns its file system path if found.


@dataclasses.dataclass(frozen=True)

class CollectionArgument

Represents a resolved collection argument from the command line.


resolve_collection_argument(invocation_path: Path, arg: str, *, as_pypath: bool = False, consider_namespace_packages: bool = False) -> CollectionArgument

Parses command-line test path arguments (possibly including test selection parts like `::TestClass::test_method`) and resolves them to absolute filesystem paths or module paths.


Important Implementation Details


Interaction with Other System Components


Visual Diagram

classDiagram
    class Session {
        +testsfailed: int
        +testscollected: int
        +shouldstop: bool | str
        +shouldfail: bool | str
        +perform_collect(args: Sequence[str] | None, genitems: bool) Sequence[nodes.Item | nodes.Collector]
        +collect() Iterator[nodes.Item | nodes.Collector]
        +genitems(node: nodes.Item | nodes.Collector) Iterator[nodes.Item]
        +gethookproxy(fspath: os.PathLike) pluggy.HookRelay
    }

    class Dir {
        +from_parent(parent: nodes.Collector, path: Path) Dir
        +collect() Iterable[nodes.Item | nodes.Collector]
    }

    class FSHookProxy {
        -pm: PytestPluginManager
        -remove_mods: AbstractSet[object]
        +__getattr__(name: str) pluggy.HookCaller
    }

    class CollectionArgument {
        +path: Path
        +parts: Sequence[str]
        +module_name: str | None
    }

    Session --> Dir : collects directories
    Session --> CollectionArgument : resolves cli args
    Session ..> FSHookProxy : uses for hook calls
    Dir --> nodes.Item : collects test items

Summary

`main.py` is the heart of pytest's test run process, orchestrating the command-line interface, test discovery, and execution lifecycle. It defines key classes such as `Session` and `Dir` for managing test collection and running, handles configuration options, and ensures correct handling of test outcomes and user interruptions. This file interacts closely with pytest’s plugin system and node hierarchy, making it critical to the framework’s extensibility and core functioning.