A run that dies during collection¶
pytest 8.1.1 - 9.1.1 - What pytest does when a hook implementation raises.
These 20 releases produced an identical flow, so they share one page. Identical as far as this scenario can measure - a scenario exercising more hooks may tell them apart.
The conftest.py here raises from pytest_collection_modifyitems. Nothing
catches it, so pytest abandons the session and reports an internal error.
No test ever runs, so the whole run-test phase is missing from this diagram - which is the point. It shows how little of a session happens when a hook implementation fails early, and which hooks still get their chance to clean up.
How this was produced¶
- Source:
scenarios/internal-error/- the project this diagram was traced from - Invocation:
pytest . -q - Traced against: pytest 9.1.1, pluggy 1.6.0, Python 3.12.3
- Covers: 20 releases - pytest 8.1.1 - 9.1.1, which produced an identical flow
- Observed: 116 hook calls, 22 distinct hooks
The project is conftest.py, test_never_runs.py. Browse it in the repository rather than inline here - scenarios grow directory trees, and GitHub renders them better than a collapsed block can.
The whole run¶
Phases left to right, steps top to bottom. Colour says which phase a hook belongs to; how dark a step is says how often it was called. Click a column heading to jump to that phase in full detail, or any hook to open its pytest documentation. Why it looks like this
Startup and configuration¶
Everything before collection begins: initial conftests are loaded, plugins register, and the session is configured.
Collection¶
Finding tests. Note that pytest_make_collect_report recurses - directories contain directories contain files.
Session finish¶
Summary reporting and teardown of the session.
Every hook observed¶
| Hook | Calls | Semantics | Implemented by, in call order |
|---|---|---|---|
pytest_cmdline_main |
1 | firstresult | _pytest.setupplan [tryfirst]_pytest.setuponly [tryfirst]_pytest.mark [tryfirst]_pytest.helpconfig_pytest.fixtures_pytest.main1 |
pytest_collect_directory |
1 | firstresult | _pytest.python_pytest.main |
pytest_collect_file |
4 | _pytest.doctest_pytest.python |
|
pytest_collection |
1 | firstresult | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper, tryfirst]_pytest.warnings [wrapper, tryfirst]_pytest.config.Config (pytestconfig) [wrapper]_pytest.terminal.TerminalReporter_pytest.assertion_pytest.main |
pytest_collection_finish |
1 | _pytest.fixtures.FixtureManager (funcmanage)_pytest.terminal.TerminalReporter2 |
|
pytest_collection_modifyitems |
1 | _pytest.fixtures.FixtureManager (funcmanage)conftest.py_pytest.main_pytest.mark |
|
pytest_collectreport |
3 | _pytest.main.Session.pytest_runtest_logreport (session) [tryfirst]_pytest.terminal.TerminalReporter |
|
pytest_collectstart |
3 | _pytest.main.Session [tryfirst] |
|
pytest_configure |
1 | historic | _pytest.subtests_pytest.faulthandler_pytest.warnings_pytest.threadexception_pytest.unraisableexception_pytest.junitxml_pytest.assertion_pytest.tmpdir_pytest.legacypath_pytest.skipping_pytest.unittest_pytest.debugging_pytest.terminal_pytest.python_pytest.mark_pytest.config.PytestPluginManager (<anonymous>)_pytest.pastebin [trylast]_pytest.logging [trylast]3 |
pytest_generate_tests |
1 | _pytest.fixtures.FixtureManager (funcmanage)_pytest.python |
|
pytest_ignore_collect |
5 | firstresult | _pytest.main |
pytest_internalerror |
1 | _pytest.capture.CaptureManager [tryfirst]_pytest.terminal.TerminalReporter |
|
pytest_itemcollected |
1 | - | |
pytest_load_initial_conftests |
1 | _pytest.warnings [wrapper]_pytest.capture [wrapper]_pytest.legacypath [tryfirst]_pytest.config.Config (pytestconfig) [trylast]4 |
|
pytest_make_collect_report |
3 | firstresult | _pytest.fixtures.FixtureManager (funcmanage) [wrapper]_pytest.capture.CaptureManager [wrapper]_pytest.runner5 |
pytest_plugin_registered |
80 | historic | _pytest.legacypath_pytest.terminal.TerminalReporter_pytest.fixtures.FixtureManager (funcmanage) |
pytest_pycollect_makeitem |
3 | firstresult | _pytest.unittest_pytest.python [trylast] |
pytest_pycollect_makemodule |
1 | firstresult | _pytest.python |
pytest_report_collectionfinish |
1 | - | |
pytest_sessionfinish |
1 | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper, tryfirst]_pytest.terminal.TerminalReporter [wrapper]_pytest.warnings [wrapper]_pytest.assertion_pytest.tmpdir_pytest.runner |
|
pytest_sessionstart |
1 | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper, tryfirst]_pytest.fixtures_pytest.runner_pytest.terminal.TerminalReporter [trylast] |
|
pytest_unconfigure |
1 | _pytest.logging.LoggingPlugin (logging-plugin)_pytest.terminal.TerminalReporter_pytest.faulthandler_pytest.unraisableexception_pytest.doctest_pytest.junitxml_pytest.pastebin_pytest.mark6 |
6 of these changed implementer somewhere in pytest 8.1.1 - 9.1.1 without changing the flow - pytest moves implementations between its own plugins. The table shows the newest; what changed along the way is below.
-
8.2.0 gained
_pytest.fixturesand lost_pytest.python↩ -
9.1.0 gained
_pytest.fixtures.FixtureManager (funcmanage)↩ -
8.4.0 gained
_pytest.threadexception,_pytest.unraisableexception; 8.4.1 gained_pytest.unittest; 9.0.0 gained_pytest.subtests; 9.1.0 gained_pytest.assertion↩ -
8.4.0 lost
_pytest.python_path↩ -
9.1.0 gained
_pytest.fixtures.FixtureManager (funcmanage)↩ -
8.4.0 lost
_pytest.python_path; 9.1.0 gained_pytest.unraisableexception↩