Skip to content

4.2.0

Choose a tag to compare

@github-actions github-actions released this 17 Oct 14:17
· 22 commits to main since this release
4.2.0
796d1d7

4.2.0 lets several apps share one container, rejects async creators where they are declared, and makes validate() and several error messages more accurate. Resolving a chain of factories is about 22% faster. Python 3.11 or newer is required, and modern-di still has no runtime dependencies.

Changes that can affect existing code

  • Factory(async_fn) raises AsyncCreatorError (#680). It is raised from the Factory(...) call, for an async def function or method, a functools.partial around one, and an instance with async def __call__. Before, the declaration was accepted and resolve() returned an un-awaited coroutine. Build async resources in the framework's lifespan instead; see Async resources via lifespan.
  • validate() checks reference-only providers (#686). A provider with bound_type=None, or with skip_creator_parsing=True and no bound_type, used to be checked only when a typed provider depended on it. A broken one now fails validate() with ValidationFailedError instead of failing on first resolve.
  • ResolutionStep.scope can be None (#693). An alias step whose source is not registered has no known scope. Error chains draw it as ?. Code that reads .dependency_path[i].scope or CircularDependencyError.steps[i].scope must handle None.
  • Error message text changed for InvalidChildScopeError (#682), DuplicateProviderTypeError (#688) and AliasSourceNotRegisteredError (#692). Code that matches the old text must change.

Fixed

  • Registering the same provider object more than once, for example from two groups, registers it once instead of raising DuplicateProviderTypeError. This also lets several apps share one container: calling setup_di on a second app with the same container no longer raises in the starlette, fastapi, aiohttp, litestar, flask, faststream, aiogram and taskiq integrations. The container is not reference-counted, so whichever app stops first closes it for the others; give it one owner, or use one container per app when apps stop independently. See Sharing a container. (#681)
  • A creator whose parameter types are only on its __signature__, such as a Pydantic model or a msgspec Struct, is now wired. Before, resolving it raised ArgumentResolutionError, and a function creator also lost its return type and was left out of by-type registration. (#685)
  • A provider whose bound type is a falsy class, for example one whose metaclass defines __len__ returning 0, is registered by type and named correctly in errors. (#687)
  • repr(alias) no longer reports a scope the alias doesn't have, and "did you mean" hints show an alias's effective scope. (#683)

Improvements

  • DuplicateProviderTypeError names each provider by its group attribute and call site, such as Dependencies.first: Factory (myapp.deps:12), so two factories with the same creator are told apart. (#688)
  • AliasSourceNotRegisteredError names the reference-only provider when the source type is only served with bound_type=None, and says how to fix it. (#692)
  • InvalidChildScopeError states the value rule instead of listing no scopes when the parent is the deepest built-in scope. (#682)

Performance

  • Each factory gets its own copy of its resolver code, so CPython's call-site caches stop missing between factories. A six-deep chain resolves 22% faster and a ten-wide one 22% faster. The first resolve of a cold container is about 35% slower, about 6 µs. (#694)
  • CacheItem is a plain slotted class. A request cycle is about 7% faster and closing a container with finalizers about 11% faster. (#690)

Docs

  • Most pages were reworked, including the lifecycle, factories, recipes, troubleshooting and integration pages (#651 to #675).
  • New recipe: Sharing a container, with manage_lifespan=False and one owner (#700, #702, #703).
  • The integration pages describe each plugin's current shutdown order: the root opens before user startup code and closes after user shutdown code (#704, #705, #706).
  • Every docs page runs in the test suite against the released integration packages (#654, #658).

What's Changed

  • docs: point integration scope-hierarchy links at the scope table by @lesnik512 in #651
  • test: run the writing-integrations examples in test-docs by @lesnik512 in #652
  • docs: rework the lifecycle page: finalizer forms, close order, and which integrations close the root by @lesnik512 in #653
  • docs: say every release from 2.15.0 on has a curated body by @lesnik512 in #655
  • test: run the framework integration pages against their packages by @lesnik512 in #654
  • docs: rework the factories page: kwargs errors, the skip-parsing warning, and the validate() gap by @lesnik512 in #657
  • test: run every docs page in the integration env and drop the last skip markers by @lesnik512 in #658
  • docs: declare Litestar handler dependencies with NamedDependency by @lesnik512 in #660
  • docs: rework the providers: context, container, alias, advanced api pages by @lesnik512 in #661
  • docs: rework the pytest and writing an integration pages by @lesnik512 in #662
  • docs: rework the wiring, alias, group and kwargs troubleshooting pages by @lesnik512 in #663
  • docs: rework the errors catalog and the scope, container and lifecycle troubleshooting pages by @lesnik512 in #664
  • docs: rework the web integrations pages by @lesnik512 in #665
  • docs: rework the migration guides and dev pages by @lesnik512 in #666
  • docs: rework the task, bot, rpc and cli integrations pages by @lesnik512 in #667
  • docs: rework the recipes pages by @lesnik512 in #668
  • docs: fix cross-page follow-ups from the docs rework by @lesnik512 in #675
  • fix: make _DependencyChainError._message abstract for ty 0.0.87 by @lesnik512 in #677
  • fix: drop ty ignore that ty 0.0.88 reports as unused by @lesnik512 in #679
  • fix: reject an async creator when a Factory is declared by @lesnik512 in #680
  • fix: register a repeated provider object once instead of raising by @lesnik512 in #681
  • fix: state the value rule in InvalidChildScopeError instead of an empty list by @lesnik512 in #682
  • fix: validate() checks reference-only providers by @lesnik512 in #686
  • fix: stop Alias repr from reporting a scope it does not have by @lesnik512 in #683
  • fix: name each provider in DuplicateProviderTypeError by its group attribute and call site by @lesnik512 in #688
  • fix: wire parameters typed only on the creator's signature by @lesnik512 in #685
  • fix: register and name a provider whose bound type is falsy by @lesnik512 in #687
  • docs: state that close does not wait for other threads and a finalizer runs at most once by @lesnik512 in #689
  • perf: build CacheItem as a plain slotted class by @lesnik512 in #690
  • fix: name the reference-only provider when an alias source is unbound by @lesnik512 in #692
  • fix: draw an unknown step scope as ? in error chains by @lesnik512 in #693
  • docs: document the Litestar, Flask and Celery integration fixes by @lesnik512 in #684
  • ci: list the installed modern-di packages before the integration docs run by @lesnik512 in #695
  • perf: give each factory its own copy of its shape's resolver code by @lesnik512 in #694
  • docs: align README and quickstart with the integration and pytest pages by @lesnik512 in #696
  • docs: document the aiogram, aiohttp, Starlette, gRPC, FastStream, Typer, pytest and FastAPI integration fixes by @lesnik512 in #697
  • docs: call modern-di-pytest an integration and note mounted apps in writing-integrations by @lesnik512 in #698
  • docs: mounted apps with middleware are found since starlette 4.1.1 and fastapi 4.0.2 by @lesnik512 in #699
  • docs: warn that apps sharing a root container close it for each other by @lesnik512 in #700
  • docs: share a container with manage_lifespan=False and one owner by @lesnik512 in #702
  • docs: FastStream start() and stop() skip the app lifespan by @lesnik512 in #703
  • docs: update integration pages for the new shutdown order by @lesnik512 in #704
  • docs: update aiogram, celery, flask and grpc pages for shutdown changes by @lesnik512 in #705
  • docs: flask streaming fixed in 4.2.1, aiogram webhook handlers outlive shutdown by @lesnik512 in #706

Full Changelog: 4.1.0...4.2.0