feat!: tighten the provider surface (#566) - #589
Merged
Merged
Conversation
There was a problem hiding this comment.
Benchmark
Details
| Benchmark suite | Current: ff75f76 | Previous: b217d41 | Ratio |
|---|---|---|---|
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type |
3085529.3301762324 iter/sec (stddev: 2.130845158709925e-8) |
3051290.1739530643 iter/sec (stddev: 1.899336899848768e-8) |
0.99 |
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry |
3097385.0698529356 iter/sec (stddev: 2.5331446362372795e-8) |
3098533.3928946564 iter/sec (stddev: 1.612550876831143e-8) |
1.00 |
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve |
20216.977278792583 iter/sec (stddev: 0.00006168690542843038) |
19342.161015171005 iter/sec (stddev: 0.00005195636868147634) |
0.96 |
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached |
16391.372285013345 iter/sec (stddev: 0.00016604891809457185) |
15557.072043312739 iter/sec (stddev: 0.00012133655653996784) |
0.95 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] |
345.0137135095071 iter/sec (stddev: 0.00004906592870982926) |
336.9942257637145 iter/sec (stddev: 0.00004241326422012053) |
0.98 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] |
330.54735455227495 iter/sec (stddev: 0.00006537614475280445) |
324.65693933586215 iter/sec (stddev: 0.000032423015870784556) |
0.98 |
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] |
324.64274120639305 iter/sec (stddev: 0.0000635695184015499) |
307.00338088231155 iter/sec (stddev: 0.00004861079528143017) |
0.95 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] |
1910.2172730281966 iter/sec (stddev: 0.00017228315982151856) |
1720.6321677132655 iter/sec (stddev: 0.0001616368065884017) |
0.90 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] |
1470.2521853616379 iter/sec (stddev: 0.00017825413620022683) |
1366.9873934254185 iter/sec (stddev: 0.0002060694942949656) |
0.93 |
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] |
1019.9230493526372 iter/sec (stddev: 0.00020853974535277775) |
951.1811204375468 iter/sec (stddev: 0.0002041261068180413) |
0.93 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] |
1879.6957443009046 iter/sec (stddev: 0.00021099943696166516) |
1772.3003457138411 iter/sec (stddev: 0.00018834625443126438) |
0.94 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] |
1298.5410750355734 iter/sec (stddev: 0.00019526047371561336) |
1258.922957418992 iter/sec (stddev: 0.0001848620837711849) |
0.97 |
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] |
742.4152353868143 iter/sec (stddev: 0.0016055646704167862) |
711.6081277463601 iter/sec (stddev: 0.0013285930653377468) |
0.96 |
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container |
811452.3022552484 iter/sec (stddev: 3.5675749009103553e-7) |
934598.7426451171 iter/sec (stddev: 9.666444604239457e-8) |
1.15 |
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope |
822023.3706422871 iter/sec (stddev: 2.0585459882145103e-7) |
864093.1222795943 iter/sec (stddev: 4.6211436587660205e-8) |
1.05 |
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch |
2465.6858198341574 iter/sec (stddev: 0.000014504964028720355) |
2454.641585404389 iter/sec (stddev: 0.000019664538636602372) |
1.00 |
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control |
56380.55131830674 iter/sec (stddev: 0.0000026768609331507602) |
56319.68717451932 iter/sec (stddev: 0.0000019232483710720968) |
1.00 |
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale |
42594.422005397035 iter/sec (stddev: 0.0000026906114611533046) |
42261.349899341985 iter/sec (stddev: 0.0000021383037041670694) |
0.99 |
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers |
539.372050148053 iter/sec (stddev: 0.00003292677896071759) |
558.1719296431494 iter/sec (stddev: 0.00002707394828739131) |
1.03 |
benchmarks/test_guard_resolve.py::test_g1_transient_resolve |
2014331.3632315653 iter/sec (stddev: 2.6340150261360775e-8) |
2017593.8216257344 iter/sec (stddev: 2.369375232643272e-8) |
1.00 |
benchmarks/test_guard_resolve.py::test_g2_cached_resolve |
2878107.816790032 iter/sec (stddev: 8.52047336737628e-9) |
2750958.076187929 iter/sec (stddev: 1.5809560276093075e-8) |
0.96 |
benchmarks/test_guard_resolve.py::test_g3_deep_chain |
697430.8287190774 iter/sec (stddev: 5.276837847892658e-8) |
679984.6996619223 iter/sec (stddev: 4.0089485670710627e-8) |
0.97 |
benchmarks/test_guard_resolve.py::test_g4_wide_resolve |
435944.58106957027 iter/sec (stddev: 5.241114692500745e-7) |
453519.71663432627 iter/sec (stddev: 4.868584238541877e-7) |
1.04 |
benchmarks/test_guard_resolve.py::test_g5_cross_scope |
1865139.1550462772 iter/sec (stddev: 2.6149272761389316e-8) |
1850823.0170494418 iter/sec (stddev: 5.328416278809819e-8) |
0.99 |
benchmarks/test_guard_resolve.py::test_g9_context_resolve |
1334236.5670705687 iter/sec (stddev: 1.788862946434627e-7) |
1324481.0319031694 iter/sec (stddev: 1.5210061635352964e-7) |
0.99 |
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve |
671262.4200752133 iter/sec (stddev: 2.2511773521939864e-7) |
693200.0060204576 iter/sec (stddev: 5.332537180219424e-8) |
1.03 |
benchmarks/test_guard_resolve.py::test_g18_alias_hop |
2861188.168426083 iter/sec (stddev: 8.911754541475025e-9) |
2913071.244886299 iter/sec (stddev: 1.1246686611147843e-8) |
1.02 |
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain |
26198.973078367526 iter/sec (stddev: 0.000021141571691406735) |
26007.765589254235 iter/sec (stddev: 0.00002273436297771503) |
0.99 |
benchmarks/test_guard_validate.py::test_g11_validate_wide |
15630.522947399479 iter/sec (stddev: 0.000025578226035471673) |
15614.049915556076 iter/sec (stddev: 0.000026879403282835526) |
1.00 |
This comment was automatically generated by workflow using github-action-benchmark.
Make internal provider methods private, narrow Factory(cache=) to bool | CacheSettings, drop abc.ABC from AbstractProvider, and import providers in modern_di/__init__.
lesnik512
force-pushed
the
feat/provider-surface
branch
from
October 5, 2026 08:47
2aaedfb to
ff75f76
Compare
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.
Closes #566.
Summary
AbstractProvider.get_dependencies,redirect_target,iter_validation_issues;Factory.wiring_plan,can_call_positionally,resolution_step;Alias.find_source;CacheSettings.coerce.Factory(cache=)acceptsbool | CacheSettingsonly. The default is nowFalse.cache=None, or any other value, raisesTypeErrornaming the value and the fix.AbstractProvideris a plaintyping.Genericclass;abc.ABCis gone.modern_di/__init__.pyimportsprovidersitself, somodern_di.providersno longer depends oncontainer.pyimporting it first.docs/providers/factories.mdanddocs/migration/to-4.x.mdupdated, with one migration entry per removed name.Design decisions
resolver_compiler.py(exempt from SLF001) need at most one# noqa: SLF001. Counts:get_dependencies,redirect_target,iter_validation_issues: one site each, all independency_graph.py(lines with anoqa).wiring_plan,can_call_positionally,resolution_step: onlyresolver_compiler.py.find_source: onlyAliasitself.CacheSettings.coerce: one site,Factory.__init__.mark_registered: two sites, so it stays public (see Open questions).bound_type,scope,provider_id,display_name,definition_site,cache_settings,ContextProvider.context_type/default. Integrations only useAbstractProvideras a type (modern-di-pytest,-fastapi,-faststream,-litestar,-taskiq); none call any renamed method.cache=rejects every value that is not a bool orCacheSettings, not justNone. Before,cache=1was silently stored ascache_settings. The error is a plainTypeError, like Python's own error for a bad keyword, so it has nodocs_slugor troubleshooting page.Factory(cache=None|1|"yes")raises;type(AbstractProvider) is type; every name inmodern_di.__all__is bound by an import in__init__(AST check, which failed before the change).Timings (Python 3.14.7, best of 7 x 2M calls, lambda overhead included)
isinstance(factory, AbstractProvider)isinstance(SomeClass, AbstractProvider)Container.resolve_dependency(cached_factory)Open questions
AbstractProvider.mark_registeredstays public. Underscoring it would need two# noqa: SLF001, atmodern_di/registries/providers_registry.py:145and:165(both inProvidersRegistry). Should it be private anyway, or shouldproviders_registry.pyjoin the SLF001 exemption?Test plan
just lintandjust lint-cicleanjust test-ci: 575 passed, 100% line coveragemkdocs build --strict