Advanced / low-level API¶
Lower-level public surface for library authors and advanced use-cases.
What is public¶
Import public names from four modules, and only from them:
modern_di:Container,Group,Scope,OverrideHandle(whatoverride()returns),Suggestion(an element of an error's.suggestions),UNSETand its typeUnsetType, plus the three submodules below.modern_di.providers: the provider types, their baseAbstractProvider,CacheSettingsandcontainer_provider.modern_di.exceptions: every error class.modern_di.integrations: the building blocks for framework integrations; see Writing an integration.
Every other module is internal and can change in any release, even one that defines an exported
name. That covers modern_di.container, modern_di.group, modern_di.scope, modern_di.types,
modern_di.suggester, the modern_di.registries and modern_di.providers submodules, and the
rest. Names that start with an underscore are internal wherever they are.
Supported extension points¶
Group.get_providers()¶
Group.get_providers() is a classmethod that traverses the MRO (excluding Group and
object) and collects every class attribute that is an AbstractProvider instance, respecting
MRO override order: a subclass attribute shadows a parent attribute of the same name. Shadowing
with a non-provider value (repo = None) removes the inherited provider. Use it to inspect or
iterate all providers declared on a group hierarchy. Group.get_named_providers() returns the same
providers as a dict keyed by attribute name.
A subclass already includes its parents' providers. Passing a group together with its subclass to
Container(groups=[...]), or one group twice, is allowed: each provider object is registered once.
The provider set is closed: AbstractProvider is not an extension point
Factory, Alias, ContextProvider, and the pre-built container_provider are the
only provider types. AbstractProvider is their shared base and the type that appears
in public signatures (resolve_provider, resolve_dependency, override,
reset_override, find_provider, add_providers), but it is not a hook for adding your
own: resolution compiles a resolver per known provider type, so defining a subclass of
AbstractProvider (or of Factory) raises TypeError when the class is created. Compose
behavior in a creator function, or use Alias, instead of introducing a provider type.
Container navigation¶
parent_containeris a read-only property: the container a child was built from, orNonefor a root.scopeis read-only too.Container(...)builds a root; children come only frombuild_child_container(), which raisesInvalidChildScopeErrorfor ascopethat is not deeper (see Building child containers).build_child_container()on aContainersubclass returns an instance of that subclass. It does not call the subclass's__init__, so state that__init__sets exists on the root only.find_container(scope)returnsselfwhenscopeis the container's own scope, otherwise the ancestor container at that scope, and raisesScopeNotInitializedErrororScopeSkippedErrorwhen no ancestor has it. Each container records its ancestors by scope when it is built, and the compiled resolvers read that record directly on every cross-scope hop. They callfind_containeronly when the scope is missing from it, so overridingfind_containerin aContainersubclass does not redirect navigation.resolveandresolve_providerare entry points, not hooks either: a compiled resolver calls its dependencies' resolvers directly, so an override of either sees only the top-level call.
Cached factory locking¶
Each cached Factory gets its own threading.RLock in each container that caches it, created
with the cache item on the first resolve there. Building a child allocates no lock. On a cold
cache miss the factory holds its lock while it resolves its dependencies and calls the creator,
so one instance is created per cache key. A warm resolve does not take the lock. The guarantee
this gives callers is in Cached factories.
See also¶
- Container: building containers and a map of their methods.
- Writing an integration: the contract built on
modern_di.integrations. - Design decisions: why the public surface is small and
Containersubclassing is limited.