A conftest implementing every hook¶
pytest 8.1.1 - 9.1.1 - What actually happens when a conftest.py implements all 52 declared hooks.
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 is generated: it implements every hook pytest declares.
Deprecated hooks are implemented too, which pytest would normally refuse - the run suppresses its own removal warnings so that the conftest really does cover everything the version declares.
This is the scenario that shows which hooks a conftest.py can and cannot
serve. Some hooks have already fired by the time a conftest is imported, so
implementing them there does nothing at all - the implementation is registered,
and never called.
How this was produced¶
- Source:
scenarios/every-hook-conftest/- the project this diagram was traced from - Invocation:
pytest . -q -W ignore::pytest.PytestDeprecationWarning - 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: 324 hook calls, 40 distinct hooks
The project is test_dummy.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.
The run-test protocol¶
pytest_runtestloop runs this once per collected test. Note the rhythm: setup, call and teardown are each followed by their own makereport and logreport pair - three reports per test, not one.
The path 5 of 8 took.
The others differed:
- 1 test also called
pytest_exception_interact - 1 test skipped
pytest_fixture_post_finalizer,pytest_fixture_setup,pytest_pyfunc_call,pytest_runtest_call - 1 test also called
pytest_warning_recordedand skippedpytest_fixture_post_finalizer,pytest_fixture_setup
Session finish¶
Summary reporting and teardown of the session.
Hooks a conftest cannot serve¶
This scenario's conftest.py implements every hook pytest declares. The hooks below still fired without it: the implementation was registered and never invoked, because the conftest had not been imported yet when the hook ran, or sits below the level the hook applies to.
Every hook observed¶
| Hook | Calls | Semantics | Implemented by, in call order |
|---|---|---|---|
pytest_addhooks |
1 | historic | conftest.py |
pytest_addoption |
1 | historic | conftest.py |
pytest_cmdline_main |
1 | firstresult | _pytest.setupplan [tryfirst]_pytest.setuponly [tryfirst]_pytest.mark [tryfirst]conftest.py_pytest.helpconfig_pytest.fixtures_pytest.main1 |
pytest_collect_directory |
1 | firstresult | _pytest.python_pytest.main |
pytest_collect_file |
5 | conftest.py_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.TerminalReporterconftest.py_pytest.assertion_pytest.main |
pytest_collection_finish |
1 | _pytest.fixtures.FixtureManager (funcmanage)_pytest.terminal.TerminalReporterconftest.py2 |
|
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.TerminalReporterconftest.py |
|
pytest_collectstart |
3 | _pytest.main.Session [tryfirst]conftest.py |
|
pytest_configure |
1 | historic | conftest.py_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_exception_interact |
1 | _pytest.faulthandler [tryfirst]conftest.py |
|
pytest_fixture_post_finalizer |
11 | conftest.py_pytest.setuponly |
|
pytest_fixture_setup |
11 | firstresult | _pytest.setuponly [wrapper]_pytest.setupplan [tryfirst]conftest.py_pytest.fixtures |
pytest_generate_tests |
4 | _pytest.fixtures.FixtureManager (funcmanage)conftest.py_pytest.python |
|
pytest_ignore_collect |
6 | firstresult | conftest.py_pytest.main |
pytest_itemcollected |
8 | conftest.py |
|
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]conftest.py_pytest.runner5 |
pytest_make_parametrize_id |
5 | firstresult | conftest.py |
pytest_plugin_registered |
112 | historic | _pytest.legacypathconftest.py_pytest.terminal.TerminalReporter_pytest.fixtures.FixtureManager (funcmanage) |
pytest_pycollect_makeitem |
10 | firstresult | conftest.py_pytest.unittest_pytest.python [trylast] |
pytest_pycollect_makemodule |
1 | firstresult | conftest.py_pytest.python |
pytest_pyfunc_call |
7 | firstresult | conftest.py_pytest.python [trylast] |
pytest_report_collectionfinish |
1 | conftest.py |
|
pytest_report_teststatus |
24 | firstresult | _pytest.subtests [tryfirst]conftest.py_pytest.skipping_pytest.runner_pytest.terminal [trylast]6 |
pytest_runtest_call |
7 | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper]_pytest.capture.CaptureManager [wrapper]_pytest.skipping [wrapper]conftest.py_pytest.runner_pytest.unraisableexception [trylast]_pytest.threadexception [trylast] |
|
pytest_runtest_logfinish |
8 | _pytest.logging.LoggingPlugin (logging-plugin)conftest.py7 |
|
pytest_runtest_logreport |
23 | _pytest.main.Session [tryfirst]_pytest.logging.LoggingPlugin (logging-plugin)_pytest.terminal.TerminalReporterconftest.py |
|
pytest_runtest_logstart |
8 | _pytest.logging.LoggingPlugin (logging-plugin)_pytest.terminal.TerminalReporterconftest.py |
|
pytest_runtest_makereport |
23 | firstresult | _pytest.tmpdir [wrapper, tryfirst]_pytest.skipping [wrapper]_pytest.unittest [tryfirst]conftest.py_pytest.runner |
pytest_runtest_protocol |
8 | firstresult | _pytest.warnings [wrapper, tryfirst]_pytest.assertion [wrapper, tryfirst]_pytest.unittest [wrapper]_pytest.faulthandler [wrapper, trylast]conftest.py_pytest.runner |
pytest_runtest_setup |
8 | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper]_pytest.capture.CaptureManager [wrapper]_pytest.skipping [tryfirst]conftest.py_pytest.runner_pytest.unraisableexception [trylast]_pytest.threadexception [trylast] |
|
pytest_runtest_teardown |
8 | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper]_pytest.capture.CaptureManager [wrapper]conftest.py_pytest.runner_pytest.unraisableexception [trylast]_pytest.threadexception [trylast] |
|
pytest_runtestloop |
1 | firstresult | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper]_pytest.terminal.TerminalReporter [wrapper]conftest.py_pytest.main8 |
pytest_sessionfinish |
1 | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper, tryfirst]_pytest.terminal.TerminalReporter [wrapper]_pytest.warnings [wrapper]conftest.py_pytest.assertion_pytest.tmpdir_pytest.runner |
|
pytest_sessionstart |
1 | _pytest.logging.LoggingPlugin (logging-plugin) [wrapper, tryfirst]conftest.py_pytest.fixtures_pytest.runner_pytest.terminal.TerminalReporter [trylast] |
|
pytest_terminal_summary |
1 | _pytest.terminal.TerminalReporter [wrapper]_pytest.warnings [wrapper]conftest.py_pytest.pastebin_pytest.runner |
|
pytest_unconfigure |
1 | _pytest.logging.LoggingPlugin (logging-plugin)_pytest.terminal.TerminalReporterconftest.py_pytest.faulthandler_pytest.unraisableexception_pytest.doctest_pytest.junitxml_pytest.pastebin_pytest.mark9 |
|
pytest_warning_recorded |
2 | historic | _pytest.terminal.TerminalReporterconftest.py |
9 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)↩ -
9.0.0 gained
_pytest.subtests↩ -
8.3.0 lost
_pytest.terminal.TerminalReporter↩ -
8.3.0 gained
_pytest.terminal.TerminalReporter↩ -
8.4.0 lost
_pytest.python_path; 9.1.0 gained_pytest.unraisableexception↩