Factories¶
Factories are providers that create instances of dependencies.
Parameters¶
When creating a Factory provider, you can configure several parameters:
scope¶
Defines the lifecycle of the dependency. Available scopes are:
- Scope.APP - Tied to the entire application lifetime (default)
- Scope.SESSION - For websocket session lifetime
- Scope.REQUEST - For dependencies created for each user request
- Scope.ACTION - For lifetime less than request
- Scope.STEP - For lifetime less than ACTION
Providers can have dependencies only of the same or more long-lived scopes.
creator¶
The callable (function or class) that will be invoked to create instances of the dependency.
Modern-DI analyzes the creator's signature to:
1. Determine the return type (used for bound_type if not explicitly set)
2. Identify parameter names and types for automatic dependency resolution
bound_type¶
Explicitly sets the type for resolving by type. By default, this is automatically inferred from the creator's return type annotation.
Set to None to make the provider unresolvable by type.
kwargs¶
Manual values for creator parameters that override automatic dependency resolution. Use this to provide specific values for parameters or override automatically resolved dependencies.
cache_settings¶
Configuration for caching instances. Only applicable for cached factories.
Use providers.CacheSettings() to enable caching with optional cleanup configuration.
Union type parameters¶
When a parameter is annotated with a union type (e.g. dep: A | B), Modern-DI resolves the first registered type that matches. The order is determined by how types appear in the union left-to-right. If you rely on a specific type being injected, prefer a concrete type annotation over a union.
skip_creator_parsing¶
Disables automatic dependency resolution. When True:
- No automatic dependency resolution occurs
- All parameters must be provided via the kwargs parameter
- The bound_type will not be automatically inferred from the creator's return type; unless bound_type is explicitly provided, it defaults to None
Types of factories¶
There are two types of factories:
- Regular Factories - Create a new instance on every call
- Cached Factories - Create an instance once and cache it for future calls
Regular Factories¶
Regular factories are initialized on every call.
import dataclasses
from modern_di import Group, Container, Scope, providers
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
class IndependentFactory:
dep1: str
dep2: int
class Dependencies(Group):
independent_factory = providers.Factory(
scope=Scope.APP,
creator=IndependentFactory,
kwargs={"dep1": "text", "dep2": 123}
)
container = Container(groups=[Dependencies])
# Resolve by provider reference
instance = container.resolve_provider(Dependencies.independent_factory)
assert isinstance(instance, IndependentFactory)
# Resolve by type (uses the return type of the creator function/class)
instance2 = container.resolve(IndependentFactory)
assert isinstance(instance2, IndependentFactory)
Cached Factories¶
Cached factories resolve the dependency only once and cache the resolved instance for future injections.
The caching mechanism is thread-safe by default, ensuring that even when multiple threads attempt to resolve the same cached factory simultaneously, only one instance will be created.
If your application is single-threaded, you can disable the lock for a small performance gain:
Do not set use_lock=False in multi-threaded applications — it removes the guarantee that only one instance is created per cached factory.
import random
from modern_di import Group, Container, Scope, providers
def generate_random_number() -> float:
return random.random()
class Dependencies(Group):
singleton = providers.Factory(
scope=Scope.APP,
creator=generate_random_number,
cache_settings=providers.CacheSettings()
)
container = Container(groups=[Dependencies])
singleton_instance1 = container.resolve_provider(Dependencies.singleton)
singleton_instance2 = container.resolve_provider(Dependencies.singleton)
# If resolved in the same container, the instance will be the same
assert singleton_instance1 is singleton_instance2
Cache Settings¶
You can customize caching behavior with CacheSettings:
import contextlib
from modern_di import Group, Scope, providers
class SomeResource:
def close(self) -> None: ...
def create_resource() -> SomeResource:
# Create and return resource
return SomeResource()
class Dependencies(Group):
# Cache with cleanup — clear_cache=True (the default) ensures the closed
# resource is evicted from cache so it cannot be returned again after close
resource = providers.Factory(
scope=Scope.APP,
creator=create_resource,
cache_settings=providers.CacheSettings(
finalizer=lambda res: res.close(), # Cleanup function
)
)