# Pytest source notes Repo: `pytest-dev/pytest` Local checkout: `/home/ubuntu/repos/rodin-sources/pytest` ## Why this repo is useful - Pytest is a strong source for package-facade patterns and lifecycle-heavy test helper patterns. - It is especially useful because the public package is deliberately small compared to the internal `_pytest.*` implementation tree. ## The public package is a curated facade over internal modules ### Repeated evidence - `src/pytest/__init__.py:6-92` re-exports the supported testing API from many `_pytest.*` implementation modules. - `src/pytest/__init__.py:98-186` defines `__all__` explicitly, turning the package root into a maintained public surface. - `src/pytest/__init__.py:23-30` includes compatibility-minded exports like `yield_fixture`, showing that the facade also absorbs historical API pressure. ### Why it matters Repeated signal: large libraries can keep internal structure fluid while giving users one stable import surface. The top-level package behaves like an API contract, not just a mirror of file layout. ### Caveat / counterexample This pattern does create maintenance pressure: once the facade exports a name, deprecating or removing it becomes a public compatibility event. Pytest accepts that tradeoff intentionally. ## Fixture cleanup is modeled as an explicit lifetime boundary ### Repeated evidence - `testing/test_monkeypatch.py:17-23` defines a fixture that snapshots global state, `yield`s the resource, then restores state after the test. - `testing/test_threadexception.py:84-90` uses a `yield` fixture where the teardown action intentionally runs after test execution. - `doc/en/how-to/fixtures.rst:551-553` marks `yield` fixtures as the recommended path. - `doc/en/how-to/fixtures.rst:669-677` contrasts them with `addfinalizer`, explicitly framing finalizers as the alternative when needed. ### Why it matters Repeated signal: pytest prefers resource lifetime that is legible in source order: setup before `yield`, teardown after `yield`. That shape scales better than hidden cleanup hooks. ### Caveat / counterexample `request.addfinalizer(...)` still exists for cases where teardown must be registered dynamically, but pytest's own docs present it as the less straightforward option. That is important evidence that `yield` fixtures are the convention, not just one possible style. ## Failure expectations are explicit context boundaries ### Repeated evidence - `testing/test_monkeypatch.py:31-32`, `testing/test_monkeypatch.py:50-51`, and `testing/test_monkeypatch.py:76-85` repeatedly use `with pytest.raises(...)` around the exact failing operation. - `testing/acceptance_test.py:513-514` and `testing/test_pluginmanager.py:254-255` show the same shape in broader integration tests. ### Why it matters Repeated signal: pytest encourages failure expectations that wrap the smallest relevant operation, keeping the expected failure boundary local and visible. ## Pattern candidates supported by this repo - expose a stable top-level facade over private implementation packages - use explicit `__all__`/re-export curation for public APIs - model test resource lifetime with `yield` fixtures - express expected failures with tight context-manager boundaries