Skip to content

fix: wire a parameterized generic class creator from its origin - #725

Merged
lesnik512 merged 1 commit into
mainfrom
fix/generic-alias-creator
Oct 18, 2026
Merged

lesnik512 merged 1 commit into
mainfrom
fix/generic-alias-creator

Conversation

@lesnik512

Copy link
Copy Markdown
Member

Closes #722.

Factory(Repo[int]) wired nothing. inspect.signature(Repo[int]) is (*args, **kwargs), so the parser saw no parameters and no return type. The provider got no bound type and an empty wiring plan. Nothing warned at declaration, validate() passed, and the first resolve called Repo[int]() with no arguments and failed with a raw TypeError: missing 1 required positional argument, which carries no dependency path.

parse_creator now parses the origin class when the creator is a parameterized generic class: its origin is a class and the creator itself is not. Everything else is unchanged:

  • The factory still calls the alias, so the instance is what Repo[int](...) returns and keeps __orig_class__.
  • The bound type is the origin, Repo. A parameter annotated Repo[int] already raises UnsupportedCreatorParameterError at declaration, so binding to the parameterized type could never be used for wiring. Two parameterizations of one class collide with DuplicateProviderTypeError, and bound_type= registers them side by side.
  • Type variables in the origin's constructor are not substituted from the alias arguments. A parameter annotated with a bare type variable follows the existing rules.

The check sits in a one-line helper, because inline it pushed parse_creator over ruff's complexity limit.

Tests (all five fail on main):

  • wiring Repo's constructor by type, resolving by the origin, caching, and __orig_class__;
  • validate() reporting the origin's missing dependency;
  • an unknown kwargs key raising UnknownFactoryKwargError at declaration;
  • two parameterizations colliding on the origin;
  • explicit bound_type=None registering both.

The factories page gains a paragraph under "creator".

@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: 407060d Previous: b7a1775 Ratio
benchmarks/test_guard_by_type.py::test_g16_resolve_by_type 5187105.374725622 iter/sec (stddev: 4.675113086497605e-9) 6829021.182046608 iter/sec (stddev: 5.484705719035411e-9) 1.32
benchmarks/test_guard_by_type.py::test_g17_resolve_by_type_large_registry 5159848.228237314 iter/sec (stddev: 5.804366192175988e-9) 6697985.105419868 iter/sec (stddev: 4.150738470799008e-9) 1.30
benchmarks/test_guard_cold.py::test_g8_cold_first_resolve 23644.439877620593 iter/sec (stddev: 0.000039707337164419307) 30757.49282217767 iter/sec (stddev: 0.00003212381064992869) 1.30
benchmarks/test_guard_cold.py::test_g8b_cold_first_resolve_cached 18930.00198588922 iter/sec (stddev: 0.00011930725404257668) 24483.236846288204 iter/sec (stddev: 0.00015154400300924624) 1.29
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[1] 617.8830406974961 iter/sec (stddev: 0.000017562913629328847) 857.6505269844425 iter/sec (stddev: 0.000031478572667331435) 1.39
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[2] 646.9309949411571 iter/sec (stddev: 0.00008532716050045627) 852.2634799214209 iter/sec (stddev: 0.000029566400203879867) 1.32
benchmarks/test_guard_concurrency.py::test_g14_concurrent_cached_hit[4] 619.9891809583999 iter/sec (stddev: 0.000036320421329545754) 767.6217950190414 iter/sec (stddev: 0.0002570266758932922) 1.24
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[1] 19470.67035560946 iter/sec (stddev: 0.000007631477722694699) 8156.588001364661 iter/sec (stddev: 0.0001306104049620297) 0.42
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[2] 10029.982124059248 iter/sec (stddev: 0.000015061551367719702) 6062.480530611141 iter/sec (stddev: 0.00018017904286592358) 0.60
benchmarks/test_guard_concurrency.py::test_g15_concurrent_first_resolve[4] 4928.164001406593 iter/sec (stddev: 0.00002807400603853854) 3415.1846852251756 iter/sec (stddev: 0.0002481508400191217) 0.69
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[1] 15195.524614053787 iter/sec (stddev: 0.000007303460408533408) 19060.870891138744 iter/sec (stddev: 0.000010501867428911193) 1.25
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[2] 6757.8720339522415 iter/sec (stddev: 0.000014055413985486515) 7946.913031583689 iter/sec (stddev: 0.000014106057259191144) 1.18
benchmarks/test_guard_concurrency.py::test_g15b_concurrent_first_resolve_sibling_children[4] 3003.4616397106947 iter/sec (stddev: 0.000028765928369998383) 3882.0758637220474 iter/sec (stddev: 0.000042286664071694886) 1.29
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[1] 44012.567054620944 iter/sec (stddev: 0.0000018520544449844553) 57980.00559382983 iter/sec (stddev: 0.0000016583671333003195) 1.32
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[2] 21312.740489907523 iter/sec (stddev: 0.000007971843262345687) 26583.077655991503 iter/sec (stddev: 0.000028387436691761624) 1.25
benchmarks/test_guard_concurrency.py::test_g15c_worker_pool_floor_control[4] 8405.718830786544 iter/sec (stddev: 0.000020819603186621953) 10858.506065052692 iter/sec (stddev: 0.000019239849675808495) 1.29
benchmarks/test_guard_lifecycle.py::test_g6_build_child_container 2104456.1543457853 iter/sec (stddev: 2.2906604693815028e-8) 3109200.162117778 iter/sec (stddev: 1.6655170160438705e-8) 1.48
benchmarks/test_guard_lifecycle.py::test_g6b_build_child_container_auto_scope 2339328.3495912957 iter/sec (stddev: 1.1689665027933889e-8) 3355865.1714153057 iter/sec (stddev: 4.5860760432145475e-8) 1.43
benchmarks/test_guard_lifecycle.py::test_g7_request_lifecycle_batch 4397.495996686372 iter/sec (stddev: 0.000005634442786568012) 5518.268723840956 iter/sec (stddev: 0.000053690988201758585) 1.25
benchmarks/test_guard_lifecycle.py::test_g7c_event_loop_floor_control 76488.57790717836 iter/sec (stddev: 0.00000113199767516063) 111291.94431021478 iter/sec (stddev: 7.415236228873871e-7) 1.46
benchmarks/test_guard_lifecycle.py::test_g7b_request_cycle_sync 585705.3227483032 iter/sec (stddev: 5.867816568826105e-8) 811531.4726922724 iter/sec (stddev: 7.030419208342152e-8) 1.39
benchmarks/test_guard_lifecycle.py::test_g13_teardown_at_scale 94867.7513364454 iter/sec (stddev: 0.0000010874986364922402) 128880.30989858645 iter/sec (stddev: 7.515728812940163e-7) 1.36
benchmarks/test_guard_lifecycle.py::test_g13b_teardown_at_scale_async_no_finalizers 839.9358896998145 iter/sec (stddev: 0.001147331549902279) 1106.9222569933033 iter/sec (stddev: 0.0012045400255381931) 1.32
benchmarks/test_guard_resolve.py::test_g1_transient_resolve 3043272.6745751947 iter/sec (stddev: 2.2390440839253746e-8) 3927978.9350432907 iter/sec (stddev: 3.170569802722682e-8) 1.29
benchmarks/test_guard_resolve.py::test_g2_cached_resolve 5064693.8671086775 iter/sec (stddev: 5.645956278596063e-9) 6651633.973879918 iter/sec (stddev: 4.456018484664371e-9) 1.31
benchmarks/test_guard_resolve.py::test_g3_deep_chain 1150045.5518649842 iter/sec (stddev: 3.3729171182376916e-8) 1540870.274285195 iter/sec (stddev: 2.8074047812986162e-8) 1.34
benchmarks/test_guard_resolve.py::test_g4_wide_resolve 590488.7032137458 iter/sec (stddev: 4.087204054529287e-7) 866746.707836144 iter/sec (stddev: 3.635108057427795e-7) 1.47
benchmarks/test_guard_resolve.py::test_g5_cross_scope 2760183.7150602723 iter/sec (stddev: 2.123799726323165e-8) 3642649.193107508 iter/sec (stddev: 1.6992111188829588e-8) 1.32
benchmarks/test_guard_resolve.py::test_g9_context_resolve 2051279.0006545682 iter/sec (stddev: 1.1868600156599844e-7) 2725232.6428932473 iter/sec (stddev: 1.4077741255402713e-7) 1.33
benchmarks/test_guard_resolve.py::test_g12_override_active_resolve 1177522.2541397181 iter/sec (stddev: 5.993827506480015e-8) 1524730.519123093 iter/sec (stddev: 2.5341878937965144e-8) 1.29
benchmarks/test_guard_resolve.py::test_g18_alias_hop 5006160.3305959655 iter/sec (stddev: 7.0445623038025714e-9) 6463835.3904325925 iter/sec (stddev: 4.452053658239917e-9) 1.29
benchmarks/test_guard_validate.py::test_g10_validate_deep_chain 55263.796482697624 iter/sec (stddev: 0.000014217834373472191) 53458.71373762307 iter/sec (stddev: 0.00025732724667223246) 0.97
benchmarks/test_guard_validate.py::test_g11_validate_wide 31996.22973489546 iter/sec (stddev: 0.00001852097118407537) 41550.27511260399 iter/sec (stddev: 0.000015081427680084859) 1.30

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

@lesnik512
lesnik512 merged commit 15a5641 into main Oct 18, 2026
12 checks passed
@lesnik512
lesnik512 deleted the fix/generic-alias-creator branch October 18, 2026 10:02
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.

Factory(Repo[int]) with a generic alias creator wires nothing and has no bound type

1 participant