Repository navigation
docs: rework the pytest and writing an integration pages - #662
Merged
Merged
Conversation
Fix claims that did not match modern-di-pytest or the integration repos, add the missing pitfalls and the pytest API table, and replace the Typer-centric decorator-path description with a runnable example and a per-integration table.
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: 05fc0d9 | Previous: 395e8fb | Ratio |
|---|---|---|---|
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type |
3633742.320721053 iter/sec (stddev: 2.6743465652381463e-8) |
6139935.138982263 iter/sec (stddev: 1.8605923146541302e-8) |
1.69 |
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry |
3838055.107031964 iter/sec (stddev: 1.6233043622956427e-8) |
6205832.315667191 iter/sec (stddev: 1.3805592208773412e-8) |
1.62 |
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve |
20301.625817705626 iter/sec (stddev: 0.00005174488444367482) |
32024.886577255867 iter/sec (stddev: 0.000030238944431934) |
1.58 |
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached |
17505.930338839313 iter/sec (stddev: 0.00010637886984754731) |
24960.133332489408 iter/sec (stddev: 0.00011886986138363566) |
1.43 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] |
426.19304004599456 iter/sec (stddev: 0.00018565260184396466) |
775.0388204206938 iter/sec (stddev: 0.00004473754687217169) |
1.82 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] |
348.0587875985199 iter/sec (stddev: 0.0001624017628154257) |
764.2436132277375 iter/sec (stddev: 0.0001144571715558005) |
2.20 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] |
389.39332429524495 iter/sec (stddev: 0.00008958136993440571) |
680.0345534095648 iter/sec (stddev: 0.0000803400475442327) |
1.75 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] |
13538.281861134541 iter/sec (stddev: 0.000007517052360933997) |
10419.935559673875 iter/sec (stddev: 0.00001230746171334532) |
0.77 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] |
6264.403887426599 iter/sec (stddev: 0.000021871396525719067) |
5634.100392630293 iter/sec (stddev: 0.00003435893765767916) |
0.90 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] |
3327.9523117689682 iter/sec (stddev: 0.00004360213784618463) |
2968.9836953557647 iter/sec (stddev: 0.000061919224501018) |
0.89 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] |
9387.254033458072 iter/sec (stddev: 0.00000981457678899777) |
12389.480670083722 iter/sec (stddev: 0.000019500905757513246) |
1.32 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] |
3875.2893145614207 iter/sec (stddev: 0.000022364554432668438) |
4843.535831204409 iter/sec (stddev: 0.00003981695650002605) |
1.25 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] |
1863.2880699227146 iter/sec (stddev: 0.0000622393980248703) |
1868.0290700812566 iter/sec (stddev: 0.00007765838735564648) |
1.00 |
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[1] |
40816.36817860615 iter/sec (stddev: 0.000004978127254251522) |
24896.9783783031 iter/sec (stddev: 0.000017881784016521047) |
0.61 |
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[2] |
14344.47540453622 iter/sec (stddev: 0.00001656036007116838) |
11645.85197075752 iter/sec (stddev: 0.000015482573556939863) |
0.81 |
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[4] |
5891.683750898387 iter/sec (stddev: 0.000033022475186569756) |
5052.235696391264 iter/sec (stddev: 0.000048475453192008314) |
0.86 |
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container |
1532094.565939957 iter/sec (stddev: 3.34624016698962e-8) |
2335541.2567186337 iter/sec (stddev: 3.860763352243666e-8) |
1.52 |
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope |
1604427.5783349432 iter/sec (stddev: 2.483855743404064e-8) |
2505907.6773532387 iter/sec (stddev: 3.905850495221895e-8) |
1.56 |
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch |
2969.0386384341928 iter/sec (stddev: 0.000010729315640809973) |
3317.2697581338502 iter/sec (stddev: 0.000052891745120707064) |
1.12 |
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control |
56326.94485432178 iter/sec (stddev: 0.0000020215509302001716) |
83100.58991551855 iter/sec (stddev: 0.0000014527703096612033) |
1.48 |
benchmarks/test_guard_lifecycle.py::test_g7b_request_cycle_sync |
383348.71935430914 iter/sec (stddev: 8.924633557404827e-8) |
468518.9908558271 iter/sec (stddev: 8.515899938405845e-8) |
1.22 |
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale |
57875.43762256828 iter/sec (stddev: 0.0000018041285325877292) |
78785.45603292529 iter/sec (stddev: 0.0000017671089125152717) |
1.36 |
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers |
532.350770309692 iter/sec (stddev: 0.0008816421868152388) |
586.9668417253587 iter/sec (stddev: 0.0013708543619692236) |
1.10 |
benchmarks/test_guard_resolve.py::test_g1_transient_resolve |
2202379.3956498425 iter/sec (stddev: 2.9977649897825856e-8) |
3091773.339921797 iter/sec (stddev: 3.1905368666696455e-8) |
1.40 |
benchmarks/test_guard_resolve.py::test_g2_cached_resolve |
3668664.156468773 iter/sec (stddev: 1.898877575634496e-8) |
5585002.481125255 iter/sec (stddev: 1.7326975696337195e-8) |
1.52 |
benchmarks/test_guard_resolve.py::test_g3_deep_chain |
675727.5195003218 iter/sec (stddev: 4.5018222594902464e-8) |
1053634.7223852123 iter/sec (stddev: 4.376268176758002e-8) |
1.56 |
benchmarks/test_guard_resolve.py::test_g4_wide_resolve |
432046.58913873957 iter/sec (stddev: 3.8583316343481493e-7) |
602475.4027370019 iter/sec (stddev: 3.669391702377282e-7) |
1.39 |
benchmarks/test_guard_resolve.py::test_g5_cross_scope |
1885822.3217778828 iter/sec (stddev: 3.698647839336208e-8) |
2722743.1964451196 iter/sec (stddev: 2.2608711961763022e-8) |
1.44 |
benchmarks/test_guard_resolve.py::test_g9_context_resolve |
1359065.748305402 iter/sec (stddev: 1.3262306748693935e-7) |
2095884.4060996436 iter/sec (stddev: 1.4539795764318154e-7) |
1.54 |
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve |
689875.6471561858 iter/sec (stddev: 5.2023927702619564e-8) |
1056924.0255533508 iter/sec (stddev: 4.539064934432262e-8) |
1.53 |
benchmarks/test_guard_resolve.py::test_g18_alias_hop |
3778890.753487168 iter/sec (stddev: 1.4162362727118295e-8) |
5564689.374851157 iter/sec (stddev: 1.595292015837515e-8) |
1.47 |
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain |
36130.10581175993 iter/sec (stddev: 0.000019095847874020287) |
23951.926949647954 iter/sec (stddev: 0.000031960404140896095) |
0.66 |
benchmarks/test_guard_validate.py::test_g11_validate_wide |
20993.1722856074 iter/sec (stddev: 0.00002697652799672106) |
21724.253571453744 iter/sec (stddev: 0.00003130808206721572) |
1.03 |
This comment was automatically generated by workflow using github-action-benchmark.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Reworks
docs/integrations/pytest.mdanddocs/integrations/writing-integrations.md, the same way the Quickstart, Introduction and Providers pages were reworked. Every behavioural claim was checked against the source ofmodern-di-pytestand the integration repos, and where possible by running a script.What was wrong
pytest page:
di_containerfixture plus a request child fixture. The recipe has neither.modern-di-pytest"the plugin". The package has nopytest11entry point; you importexposeandmodern_di_fixtureyourself.list_users() == [], which also passes with the real repo. The hiddenFakeReponow returns["fake"]and the examples assert that.override()/reset_override()and namedOverridesRegistry, which is not public. The page now leads withwith di_container.override(...), the spelling the recipe and the package README use.Scope.SESSION.writing-integrations page:
from_di,parse_markersandresolve_markers. It skips onlybindandclassify_connection.__signature__. Starlette, aiohttp, Flask, arq and gRPC build the child in middleware, a hook or an interceptor.RuntimeErrormatched none of the three integrations it cited (aiohttp, Starlette, aiogram).Usage example: [examples/](./examples)used a relative link; every repo uses an absolute GitHub URL, because PyPI does not rewrite relative links.Annotated[T, FromDI(...)]without mentioning that FastMCP refuses that form at startup._CONNECTION_PROVIDERStuple. FastStream and FastMCP register their single provider directly.How it was verified
conftest.pyand test modules (modern-di checkout plusmodern-di-pytestonPYTHONPATH): 8 passed. It coversexposein conftest withdi_containerredefined in a test module, the targetedcontainer_fixture=override, and an override that resets for later modules.expose()with no groups raisesTypeError, and a duplicate name raisesValueErrorwith nothing installed. A REQUEST-scoped provider resolved from the root fixture fails setup withScopeNotInitializedError. A session-scoped fixture on a function-scoped container gives pytestScopeMismatch. A syncwith Container(...)holding an async finalizer raisesFinalizerError(AsyncFinalizerInSyncCloseError) on exit. A failing session fixture errors every test that requests it.withblock ends. Overriding the cached provider itself works, and the original comes back after the block.modern-di-*/modern_di_*/main.py:__signature__,functools.wraps, the lifecycle hooks, theRuntimeErrormessages, the connection providers, andtest_public_surface.py(9 repos have it).What was added
## APItable; notes on the async-finalizer fixture, on why to usecontainer_fixture=, and thatexposeinstalls onto the calling module (module=) and overwrites an existing attribute; thepytest_scopelimit; and the cache caveat. A hidden block at the end now calls the test functions against a real container, so CI checks their assertions undertest-docs-integrations(before, they were only defined).setup_dinow wires a composed lifespan, and a hidden block runs two lifespan cycles.fetch_di_containerraises the missing-containerRuntimeError, now stated as a rule for both paths. The decorator path has a runnable example (middleware stores the child and deletes it infinally;@injectusesparse_markers/resolve_markers; there is araises: RuntimeErrorblock). There is a "Signature rewriting" subsection saying when rewriting is needed, including that aiogram cannot usefunctools.wraps.Structure changes
open()no-op and validate-ordering explanations are now stated once, in Lifecycle rules. Repo scaffolding points at the Starlette page as the model instead of restating its shape. Down from 532 to 481 lines; most of what remains is the runnable examples and the table.Anchors kept
#the-contract,#lifecycle-rules,#how-the-existing-integrations-realize-the-contract,#frameworks-without-native-di-the-decorator-path, and the numbered contract subsection headings. No inbound links target headings inpytest.md. Both page paths are unchanged.Checks
just test-docs -q -p no:cacheprovider --no-cov: 158 passed, 21 skippedpytest docs/integrations/pytest.md docs/integrations/writing-integrations.md --docs-integrationswithmodern-di-pytestonPYTHONPATH: 22 passedjust docs-build: no warningsjust lint-ci: passedLibrary bugs
None found. Not rebuilding cached dependents under an override is current behaviour, and the page now documents it.
Out-of-batch changes worth making
docs/recipes/testing-overrides.mdPattern 3 should state the cache caveat and showwith container.override(...)in a fixture, so it matches the pytest page.docs/integrations/litestar.mdcomments say "after the plugin is installed". The plugin'son_app_init(which callsadd_providers) runs atLitestar(...)construction, so check thatvalidate()comes after that call in those examples.mkdocs.ymlllms entry forintegrations/pytest.mdstill reads "with and without modern-di-pytest", which is fine, but the nav says "Pytest" while the H1 is "Usage withpytest".