Skip to content

fix: skip scope checks on Container injection in validate() (#612) - #620

Merged
lesnik512 merged 1 commit into
mainfrom
issue-612
Oct 9, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
issue-612

Conversation

@lesnik512

@lesnik512 lesnik512 commented Oct 7, 2026 •

Copy link
Copy Markdown
Member

Closes #612.

Summary

  • validate() no longer reports a scope error when a provider under a custom scope enum injects Container. Edges that end at container_provider skip the scope checks. Its resolver already ignores scope, so this only removes a false error.
  • container_provider shows as Container in error chains, and its repr is container_provider.
  • The AbstractProvider.scope docstring now calls the value the declared scope, matching CONTEXT.md.

Items 2 and 3 of the issue are dropped by maintainer decision. Item 2 (validating providers declared with bound_type=None): the failure still surfaces on the first resolve, and the change may return in a later 4.x release. Item 3 (reporting an Alias at its source's scope in suggestions): cosmetic, and not worth the extra indirection.

Design decisions

  • An _ignores_scope class flag on AbstractProvider, set on the container provider, marks a provider whose scope resolution ignores. dependency_graph must not import a concrete provider at runtime, so it reads the flag instead of comparing against container_provider.

Test plan

  • Failing tests first, seen failing: test_validate_accepts_container_injection_under_a_custom_scope_enum for both enum shapes (APP=1 raised ScopeEnumMismatchError, APP=0 raised InvalidScopeDependencyError), and test_container_provider_displays_as_container
  • just lint, just lint-ci
  • just test-ci (100% coverage)
  • mkdocs build --strict

@github-actions github-actions Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Details
Benchmark suite Current: 0209927 Previous: de2a43e Ratio
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type 2860065.4354286413 iter/sec (stddev: 2.522077142387517e-8) 6953327.8034167215 iter/sec (stddev: 7.347774794843538e-9) 2.43
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry 2977610.512816597 iter/sec (stddev: 1.383700817039296e-8) 6820290.062526142 iter/sec (stddev: 7.687344237009849e-9) 2.29
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve 21988.09561791311 iter/sec (stddev: 0.00004136473961510413) 42100.534410324486 iter/sec (stddev: 0.000025821223643735962) 1.91
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached 16769.382760382927 iter/sec (stddev: 0.00012035324544623004) 30662.64995213294 iter/sec (stddev: 0.0001303232765362364) 1.83
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] 412.27238764046035 iter/sec (stddev: 0.00007811471661383668) 864.0152101329635 iter/sec (stddev: 0.000012742934447058053) 2.10
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] 404.45864532890795 iter/sec (stddev: 0.0001288752831480329) 833.8257323842873 iter/sec (stddev: 0.000019388052122169534) 2.06
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] 353.52981473551637 iter/sec (stddev: 0.00009706005514297423) 809.5543689004884 iter/sec (stddev: 0.00003316413288121582) 2.29
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] 13151.69527011386 iter/sec (stddev: 0.000006875240927827625) 22617.48795638521 iter/sec (stddev: 0.000005531660203820173) 1.72
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] 6567.415505289867 iter/sec (stddev: 0.000027328159531621162) 11181.638259173254 iter/sec (stddev: 0.00001536690546092702) 1.70
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] 3503.896186568908 iter/sec (stddev: 0.00004088635898599192) 5991.079881170895 iter/sec (stddev: 0.00003190779404892748) 1.71
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] 8471.007201289052 iter/sec (stddev: 0.000009862675651642405) 15396.413380230697 iter/sec (stddev: 0.000006993947741288179) 1.82
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] 3768.0611413221745 iter/sec (stddev: 0.000020954621485599555) 6474.091226387491 iter/sec (stddev: 0.000013554268619887211) 1.72
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] 1762.364037474941 iter/sec (stddev: 0.000042427953312284364) 2192.6677009377127 iter/sec (stddev: 0.00028944344071642084) 1.24
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[1] 40569.72058882931 iter/sec (stddev: 0.000005799423324909702) 57084.03341001215 iter/sec (stddev: 0.000002009057853242719) 1.41
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[2] 15741.24953755745 iter/sec (stddev: 0.000018990684648167877) 10818.766014906349 iter/sec (stddev: 0.00017684134938746661) 0.69
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[4] 6260.100280462919 iter/sec (stddev: 0.000030233077889491833) 3733.4655311423226 iter/sec (stddev: 0.0002575851204949082) 0.60
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container 1614036.8914737578 iter/sec (stddev: 2.9462225238374492e-8) 3244225.5421589306 iter/sec (stddev: 2.2259191874324864e-8) 2.01
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope 1647238.1787177392 iter/sec (stddev: 2.2393071515128987e-8) 3505253.147689667 iter/sec (stddev: 1.256303171650541e-8) 2.13
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch 2750.134127754916 iter/sec (stddev: 0.00001211245346521454) 5540.987660072705 iter/sec (stddev: 0.000006914587023194074) 2.01
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control 58570.99799856714 iter/sec (stddev: 0.000001891438563294109) 108951.03969333507 iter/sec (stddev: 8.354844009513133e-7) 1.86
benchmarks/test_guard_lifecycle.py::test_g7b_request_cycle_sync 335072.9876080857 iter/sec (stddev: 7.974700705639591e-8) 658633.7932879232 iter/sec (stddev: 5.077480986867621e-8) 1.97
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale 49192.87672845806 iter/sec (stddev: 0.000001726648194440595) 88924.9373299253 iter/sec (stddev: 0.000001017499949312894) 1.81
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers 471.37146263331823 iter/sec (stddev: 0.0009274347122507575) 838.8958231043543 iter/sec (stddev: 0.0014733725984648455) 1.78
benchmarks/test_guard_resolve.py::test_g1_transient_resolve 1989942.828954846 iter/sec (stddev: 2.1323532792348652e-8) 3826429.3389259307 iter/sec (stddev: 1.901452074710719e-8) 1.92
benchmarks/test_guard_resolve.py::test_g2_cached_resolve 2826725.034379575 iter/sec (stddev: 8.475665060419803e-9) 6575124.279868452 iter/sec (stddev: 4.737930788770422e-9) 2.33
benchmarks/test_guard_resolve.py::test_g3_deep_chain 686123.1406093545 iter/sec (stddev: 3.815108734500415e-8) 1379517.6054377488 iter/sec (stddev: 3.64214282967687e-8) 2.01
benchmarks/test_guard_resolve.py::test_g4_wide_resolve 432870.2256799295 iter/sec (stddev: 4.012394489327122e-7) 824510.0452115666 iter/sec (stddev: 3.478514946570615e-7) 1.90
benchmarks/test_guard_resolve.py::test_g5_cross_scope 1703540.8010517939 iter/sec (stddev: 2.9479774196130235e-8) 3507850.042352171 iter/sec (stddev: 1.892634034106689e-8) 2.06
benchmarks/test_guard_resolve.py::test_g9_context_resolve 1237414.2978520612 iter/sec (stddev: 1.4864324394664855e-7) 2715030.776876608 iter/sec (stddev: 1.406394331673886e-7) 2.19
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve 689123.8816668179 iter/sec (stddev: 5.175283986847778e-8) 1392274.0485837904 iter/sec (stddev: 2.8420709969791e-8) 2.02
benchmarks/test_guard_resolve.py::test_g18_alias_hop 2843249.0363989235 iter/sec (stddev: 7.695695001137322e-9) 6778648.585518124 iter/sec (stddev: 3.663622363643567e-9) 2.38
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain 27736.372253746093 iter/sec (stddev: 0.000021719379527709725) 52427.08205493993 iter/sec (stddev: 0.000012546718743662436) 1.89
benchmarks/test_guard_validate.py::test_g11_validate_wide 16515.35863938067 iter/sec (stddev: 0.000026536277228622072) 26644.64658297612 iter/sec (stddev: 0.0002712935827922999) 1.61

This comment was automatically generated by workflow using github-action-benchmark.

container_provider resolves the current container whatever its scope, so
validate() no longer checks scope on edges that end at it. Its display name
in error chains is Container.
@lesnik512 lesnik512 changed the title fix!: validate Container injection under custom scopes and reference-only providers (#612) fix: skip scope checks on Container injection in validate() (#612) Oct 9, 2026
@lesnik512
lesnik512 merged commit 8ba2bd1 into main Oct 9, 2026
11 checks passed
@lesnik512
lesnik512 deleted the issue-612 branch October 9, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

validate() bugs: Container under a custom scope enum, reference-only providers, Alias scope

1 participant