Repository navigation
docs: rework the migration guides and dev pages - #666
Merged
Merged
Conversation
Split to-4.x into 4.0 and 4.1 changes and add the 4.1 breaking changes. Fix the that-depends and dependency-injector mappings that were wrong (ContextResource, bind, override_providers_sync, List/Dict, unwired-dependency errors, which integrations close the APP container), add missing rows, and bring the contributing page in line with the justfile and CI.
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: f13575e | Previous: 395e8fb | Ratio |
|---|---|---|---|
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type |
5169839.3022681 iter/sec (stddev: 4.425254602258383e-9) |
6139935.138982263 iter/sec (stddev: 1.8605923146541302e-8) |
1.19 |
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry |
5154900.64031136 iter/sec (stddev: 5.702025651807938e-9) |
6205832.315667191 iter/sec (stddev: 1.3805592208773412e-8) |
1.20 |
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve |
32893.51072521343 iter/sec (stddev: 0.0000361364057830406) |
32024.886577255867 iter/sec (stddev: 0.000030238944431934) |
0.97 |
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached |
26230.058956643566 iter/sec (stddev: 0.00012132664932644568) |
24960.133332489408 iter/sec (stddev: 0.00011886986138363566) |
0.95 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] |
665.39170411924 iter/sec (stddev: 0.00004861146509406188) |
775.0388204206938 iter/sec (stddev: 0.00004473754687217169) |
1.16 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] |
655.2699858865042 iter/sec (stddev: 0.000026732336225453076) |
764.2436132277375 iter/sec (stddev: 0.0001144571715558005) |
1.17 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] |
621.4330792500076 iter/sec (stddev: 0.00006664653470079753) |
680.0345534095648 iter/sec (stddev: 0.0000803400475442327) |
1.09 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] |
18234.962044817225 iter/sec (stddev: 0.000006875067808448918) |
10419.935559673875 iter/sec (stddev: 0.00001230746171334532) |
0.57 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] |
9673.657550270613 iter/sec (stddev: 0.000014824374514855715) |
5634.100392630293 iter/sec (stddev: 0.00003435893765767916) |
0.58 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] |
4895.516221173688 iter/sec (stddev: 0.000027825837771945206) |
2968.9836953557647 iter/sec (stddev: 0.000061919224501018) |
0.61 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] |
13567.69567841955 iter/sec (stddev: 0.000008300568735721143) |
12389.480670083722 iter/sec (stddev: 0.000019500905757513246) |
0.91 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] |
6033.6308550285985 iter/sec (stddev: 0.000013859482592709325) |
4843.535831204409 iter/sec (stddev: 0.00003981695650002605) |
0.80 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] |
2745.805918751064 iter/sec (stddev: 0.00003145825320348441) |
1868.0290700812566 iter/sec (stddev: 0.00007765838735564648) |
0.68 |
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[1] |
42809.86853034172 iter/sec (stddev: 0.000006065038202597864) |
24896.9783783031 iter/sec (stddev: 0.000017881784016521047) |
0.58 |
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[2] |
22660.11210345477 iter/sec (stddev: 0.00001162303295389256) |
11645.85197075752 iter/sec (stddev: 0.000015482573556939863) |
0.51 |
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[4] |
8276.138320722099 iter/sec (stddev: 0.000023757237062621398) |
5052.235696391264 iter/sec (stddev: 0.000048475453192008314) |
0.61 |
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container |
2266176.605977312 iter/sec (stddev: 2.6020883951608016e-8) |
2335541.2567186337 iter/sec (stddev: 3.860763352243666e-8) |
1.03 |
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope |
2414926.0785190356 iter/sec (stddev: 1.1403947233581225e-8) |
2505907.6773532387 iter/sec (stddev: 3.905850495221895e-8) |
1.04 |
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch |
4217.685640241885 iter/sec (stddev: 0.00002724028431485555) |
3317.2697581338502 iter/sec (stddev: 0.000052891745120707064) |
0.79 |
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control |
75136.86162271425 iter/sec (stddev: 0.0000011893100322501417) |
83100.58991551855 iter/sec (stddev: 0.0000014527703096612033) |
1.11 |
benchmarks/test_guard_lifecycle.py::test_g7b_request_cycle_sync |
535100.2056729948 iter/sec (stddev: 8.408794150494762e-8) |
468518.9908558271 iter/sec (stddev: 8.515899938405845e-8) |
0.88 |
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale |
81535.10581732563 iter/sec (stddev: 0.0000010080793015740608) |
78785.45603292529 iter/sec (stddev: 0.0000017671089125152717) |
0.97 |
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers |
740.1784440005631 iter/sec (stddev: 0.0011776885015094584) |
586.9668417253587 iter/sec (stddev: 0.0013708543619692236) |
0.79 |
benchmarks/test_guard_resolve.py::test_g1_transient_resolve |
2921995.930676916 iter/sec (stddev: 2.1795041229018354e-8) |
3091773.339921797 iter/sec (stddev: 3.1905368666696455e-8) |
1.06 |
benchmarks/test_guard_resolve.py::test_g2_cached_resolve |
4933662.224392176 iter/sec (stddev: 7.3950286904590865e-9) |
5585002.481125255 iter/sec (stddev: 1.7326975696337195e-8) |
1.13 |
benchmarks/test_guard_resolve.py::test_g3_deep_chain |
1059482.555607536 iter/sec (stddev: 3.4479317273665386e-8) |
1053634.7223852123 iter/sec (stddev: 4.376268176758002e-8) |
0.99 |
benchmarks/test_guard_resolve.py::test_g4_wide_resolve |
561142.8077300378 iter/sec (stddev: 5.018835021512661e-7) |
602475.4027370019 iter/sec (stddev: 3.669391702377282e-7) |
1.07 |
benchmarks/test_guard_resolve.py::test_g5_cross_scope |
2721174.883579432 iter/sec (stddev: 1.7480110450337723e-8) |
2722743.1964451196 iter/sec (stddev: 2.2608711961763022e-8) |
1.00 |
benchmarks/test_guard_resolve.py::test_g9_context_resolve |
2078673.3158664652 iter/sec (stddev: 1.364837651904162e-7) |
2095884.4060996436 iter/sec (stddev: 1.4539795764318154e-7) |
1.01 |
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve |
1069069.7073013578 iter/sec (stddev: 3.366384516111828e-8) |
1056924.0255533508 iter/sec (stddev: 4.539064934432262e-8) |
0.99 |
benchmarks/test_guard_resolve.py::test_g18_alias_hop |
5092566.563938978 iter/sec (stddev: 5.475000374878718e-9) |
5564689.374851157 iter/sec (stddev: 1.595292015837515e-8) |
1.09 |
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain |
55160.02751374086 iter/sec (stddev: 0.000014435754251702033) |
23951.926949647954 iter/sec (stddev: 0.000031960404140896095) |
0.43 |
benchmarks/test_guard_validate.py::test_g11_validate_wide |
30499.222869642017 iter/sec (stddev: 0.00002256969011531934) |
21724.253571453744 iter/sec (stddev: 0.00003130808206721572) |
0.71 |
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.
Summary
This reworks the two "from another library" migration guides, the 4.x upgrade guide and the two dev pages, using the same approach as the Quickstart, Introduction and Providers pages. I checked each behavioural claim with a script against this checkout. For 4.0 claims I ran an extracted
git archive 4.0.0copy ofmodern_di. that-depends claims were read from its source, and dependency-injector claims were run against 4.49.1, the version inbenchmarks/comparative.What was wrong
to-4.x.mdgit log 4.0.0..4.1.0: feat: reject provider subclasses at definition, name both duplicate providers, summarize finalizer errors, document the providers (#618) #631, fix: reject an Alias bound to its own source type #646, feat: require bound_type on Alias #650). In 4.0.0,Alias(X)still defaultedbound_typetoX(validate reportsCircularDependencyError). The 4.0.0FinalizerErrormessage was stillErrors during sync cleanup: [ValueError('bad')].FinalizerErrormessage was described as "one line". It also has aSee:link line.TypeErrorat class definition. On 4.0.0 the class statement succeeded and the first resolve raisedTypeError: no compiled resolver for provider type MyFactory. feat: reject provider subclasses at definition, name both duplicate providers, summarize finalizer errors, document the providers (#618) #631 also rewrote theDuplicateProviderTypeErrormessage and added.first_provider/.second_provider.from-that-depends.mdcontainer.override_providers_sync({...})as a statement. In that-depends it is a@contextmanager, so that call overrides nothing. It now useswith Container.override_providers_sync(...).ContextResourcewas mapped to an uncached REQUESTFactory, which builds a new instance on every resolve and runs no teardown. It now maps to REQUEST pluscache=CacheSettings(finalizer=...)..bind(T)was mapped toAlias.bindreplaces the bindings, so the direct equivalent isFactory(..., bound_type=T). I checked that it resolves byRepositoryand no longer byPostgresRepository.Aliasis now described as the way to answer for extra types.Provide[T]()does not exist in that-depends, becauseProvide[...]takes a provider or a string. That row now usesProvide[Container.x],Provide(),Depends(Container.x)andlitestar.di.Provide(Container.x).list.resolve(list[object])raisesProviderNotRegisteredError, alist[X]consumer parameter raisesUnsupportedCreatorParameterError, and a second list factory raisesDuplicateProviderTypeError. The example now passes the list provider to its consumer throughkwargsand explains all three behaviours. The dependency-injector page had the same problem and gets the same fix.settingson the that-depends side and dropped it on the modern-di side. Both sides now declareSettings, andcreate_sa_engine(settings: Settings)wires by type.ContextScopesanddefault_scope.providers/lifecycle.md). Typer's per-command container also comes from@modern_di_typer.inject, not from its setup call.from-dependency-injector.mdUnsupportedCreatorParameterError)". That error only covers unsupported parameter shapes.Factory(Needs)with an unregistered parameter type declares fine and fails on resolve withArgumentResolutionError, or invalidate().RecursionErroron the first plain call into the cycle. In modern-di withoutvalidate(), theCircularDependencyErroris chained to aRecursionError, and the traceback ran to 2,341 lines in my run.Factory.creatoris not a public attribute. The cache is always lock-guarded, so "by default" was wrong. The asyncResourceandCoroutinerows pointed at a section with no async content, and now link to the async-lifespan recipe.dev/contributing.mdtest-ciandlint-ci. It now lists everything_checks.ymlruns.dev/performance-history.mdWhat was added
TypeError", "FinalizerErrorsummarizes its errors" and "DuplicateProviderTypeErrornames both providers".connect_containers,ContextScopes/default_scope,Provide()and string markers,LazyProvider,BaseContainer.resolve/resolverandoverride_context_sync. Also a group-level default scope in step 2, a warning aboutasync defcreators (see below), a concrete per-request async example, and a fuller description of whatvalidate()checks and what it misses (context values).ContextLocalSingleton,Container/DependenciesContainer,Self,Delegateand theDelegated*providers,AbstractSingleton/AbstractCallable/AbstractCoroutineandExternalDependency. A script lists theProvidersubclasses in 4.49.1. Every class it reports is mapped or is a base or helper class covered by its parent row. There is also a note thatvalidate()does not report a missingContextProvidervalue.test-docs,test-docs-integrations,docs-build, anddocs/agents/docs-style.md), plustest-race,benchandbench-compare.Structure and anchors
## Changes in 4.0(replacing## Key changes, which had no inbound links) and## Changes in 4.1. All H3 slugs are unchanged, including the two linked from outside the page (#resolving-on-a-closed-container-raises,#a-missing-context-value-for-a-required-parameter-raises-contextvaluenotseterror).Checks
just test-docs -q -p no:cacheprovider --no-cov: 152 passed, 21 skippedjust docs-build: strict build, no WARNING linesjust lint-ci: passeslychee --offlineoverdocsandREADME.md: 0 errorsSettings,bound_typeandAliasbinding, list viakwargs, the per-requestset_contextexample, multi-group containers,ContextLocalSingleton,Containerinjection, and the 4.0.0 and current behaviour in the to-4.x 4.1 section.Library behaviour found (not fixed here)
Factoryaccepts anasync defcreator, and resolving it returns an un-awaited coroutine:The only signal is
RuntimeWarning: coroutine ... was never awaitedat garbage collection. The that-depends page now documents this as current behaviour. Whether to reject or warn atFactory(...)time is a library decision.Out-of-batch follow-ups
docs/introduction/comparison.md:137says the that-depends guide "maps every provider and concept", and:103says the dependency-injector guide maps it "provider by provider". Both hold after this PR. They would need softening if the new rows are ever removed.Aliasrequiresbound_type, provider subclassing raises). The new "Changes in 4.1" section is the only place that records them.