This document describes the changes required to migrate from modern-di 1.x versions to modern-di 2.x.
The migration to modern-di 2.x involves several key changes in the API, including:
- Simplified container architecture with a single
Containerclass - Updated provider API with keyword-only arguments
- Removal of several provider types (
Singleton,Resource,Dict,List) - Changes in caching mechanism
- Updated integration packages
The AsyncContainer and SyncContainer classes have been merged into a single Container class. The new container supports both synchronous and asynchronous operations.
Before (1.x):
from modern_di import AsyncContainer, SyncContainer
# Asynchronous container
async_container = AsyncContainer(groups=ALL_GROUPS)
async_container.enter()
# Synchronous container
sync_container = SyncContainer(groups=ALL_GROUPS)
sync_container.enter()After (2.x):
from modern_di import Container
# Single container for both sync and async operations
container = Container(groups=ALL_GROUPS)
# No need to explicitly enter the container
# For async cleanup
await container.close_async()
# For sync cleanup
container.close_sync()All providers now use keyword-only arguments for better clarity and consistency.
Before (1.x):
from modern_di import Scope, providers
factory = providers.Factory(Scope.REQUEST, MyClass, arg1="value1", arg2="value2")After (2.x):
from modern_di import Scope, providers
factory = providers.Factory(scope=Scope.REQUEST, creator=MyClass, kwargs={"arg1": "value1", "arg2": "value2"})Several provider types have been removed and their functionality consolidated into the Factory provider:
Before (1.x):
singleton = providers.Singleton(Scope.APP, create_singleton)After (2.x):
# Use Factory with cache settings
singleton = providers.Factory(
scope=Scope.APP,
creator=create_singleton,
cache_settings=providers.CacheSettings()
)Before (1.x):
resource = providers.Resource(Scope.REQUEST, create_resource)After (2.x):
# Resources can be replaced with Factory with cache_settings with finalizer defined
resource = providers.Factory(
scope=Scope.REQUEST,
creator=create_resource,
cache_settings=providers.CacheSettings(
finalizer=lambda resource: resource.close(),
clear_cache=False
)
)These providers have been removed entirely. Use Factory providers with appropriate creator functions instead.
Before (1.x):
my_dict = providers.Dict(Scope.REQUEST, key1=provider1, key2=provider2)
my_list = providers.List(Scope.REQUEST, provider1, provider2, provider3)After (2.x):
from dataclasses import dataclass
from typing import List
@dataclass(kw_only=True, slots=True, frozen=True)
class UserService:
name: str
age: int
@dataclass(kw_only=True, slots=True, frozen=True)
class AuthService:
token: str
expiry: int
# Define providers for UserService and AuthService first
user_service_provider = providers.Factory(creator=UserService)
auth_service_provider = providers.Factory(creator=AuthService)
# For dictionaries
def create_services_dict(user_service: UserService, auth_service: AuthService) -> dict[str, object]:
return {
"user": user_service,
"auth": auth_service
}
my_dict = providers.Factory(creator=create_services_dict)
# For lists
def create_service_list(user_service: UserService, auth_service: AuthService) -> List[object]:
return [user_service, auth_service]
my_list = providers.Factory(creator=create_service_list)Caching is now handled through CacheSettings in Factory providers.
Before (1.x):
# Singleton was automatically cached
singleton = providers.Singleton(Scope.APP, create_singleton)After (2.x):
# Explicit cache settings
singleton = providers.Factory(
creator=create_singleton,
cache_settings=providers.CacheSettings()
)
# Cache settings with finalizer
cached_with_cleanup = providers.Factory(
creator=create_resource,
cache_settings=providers.CacheSettings(
finalizer=lambda resource: resource.close(),
clear_cache=False
)
)Child container creation has changed: context managers have been removed and explicit close methods have been added.
Before (1.x):
# Async container
async with container.build_child_container(context=context, scope=Scope.REQUEST) as request_container:
# Use request_container
# Sync container
with container.build_child_container(context=context, scope=Scope.REQUEST) as request_container:
# Use request_containerAfter (2.x):
# Container building remains the same, but now requires explicit cleanup
request_container = container.build_child_container(context=context, scope=Scope.REQUEST)
# Use request_container
# Cleanup now requires explicit calls:
# For async cleanup
await request_container.close_async()
# For sync cleanup
request_container.close_sync()Resolution methods have been simplified.
Before (1.x):
# Async resolution
instance = await container.resolve_provider(provider)
instance = await container.resolve(SomeType)
# Sync resolution
instance = container.sync_resolve_provider(provider)
instance = container.sync_resolve(SomeType)After (2.x):
# now resolving is sync only
instance = container.resolve_provider(provider)
instance = container.resolve(SomeType)!!! note "Async finalizers are still supported"
Only resolution became sync-only in 2.x. Async finalizers (cleanup functions) are still fully supported via CacheSettings(finalizer=async_cleanup_fn) and await container.close_async(). The distinction: you cannot await during dependency resolution, but you can use async functions to clean up resources when a container is closed.
- Update Dependencies: Ensure all modern-di packages are updated to 2.x versions
- Update Container Initialization: Replace
AsyncContainer/SyncContainerwithContainer - Update Provider Definitions:
- Replace positional arguments with keyword arguments
- Replace
SingletonandResourcewithFactoryusingCacheSettings - Remove
DictandListproviders, replace withFactorycreators
- Update Container Building: Replace context managers with try/finally blocks
- Update Provider Resolution: Remove
sync_prefixes andawaitkeywords
AsyncContainerandSyncContainerclasses removed (useContainerinstead)Singleton,Resource,Dict, andListprovider types removed- All provider constructors now use keyword-only arguments
- Container building no longer uses context managers
- Provider resolution methods simplified (no
sync_prefix) - Integration packages updated with new APIs
- Automatic container entry/exit removed (manual cleanup required)
- Provider casting mechanism changed (
.castattribute removed)