diff --git a/docs/introduction/key-concepts.md b/docs/introduction/key-concepts.md index 1a52b4c..5202e36 100644 --- a/docs/introduction/key-concepts.md +++ b/docs/introduction/key-concepts.md @@ -59,6 +59,9 @@ More about providers: - Can have dependencies only of the same or more long-lived scopes: - **APP**-scoped providers can have only **APP**-scoped dependencies; - **SESSION**-scoped providers can have APP and **SESSION**-scoped dependencies, etc.; +- Providers can be resolved by name or by type: + - Resolving by name uses the provider attribute name in the Group; + - Resolving by type uses the return type annotation of the creator function or class. ## Container @@ -70,6 +73,14 @@ All states live in containers: - Assembled objects; - Overrides for tests; +Container provides two methods for resolving dependencies: + +1. `resolve_provider(provider)` - Resolve a specific provider instance +2. `resolve(dependency_type=None, dependency_name=None)` - Resolve by type or name + +When resolving by type, the container looks for a provider that was registered with a matching `bound_type`. +When resolving by name, the container looks for a provider with a matching attribute name in the Group. + ## Group A Group is a collection of providers. They cannot be instantiated. diff --git a/docs/providers/context-providers.md b/docs/providers/context-providers.md index b7ed0e6..f748748 100644 --- a/docs/providers/context-providers.md +++ b/docs/providers/context-providers.md @@ -39,6 +39,16 @@ app = fastapi.FastAPI() modern_di_fastapi.setup_di(app, Container(groups=ALL_GROUPS)) ``` +You can resolve the context provider by type or by name: + +```python +# Resolving by type +request_info_dict = container.resolve(dependency_type=dict) + +# Resolving by name +request_info_dict = container.resolve(dependency_name="request_info") +``` + ### Manual ContextProvider Usage You may still need to define ContextProviders manually in cases where you want to inject custom context objects that are not automatically provided by the integration: diff --git a/docs/providers/factories.md b/docs/providers/factories.md index be5c6f1..2e4ea14 100644 --- a/docs/providers/factories.md +++ b/docs/providers/factories.md @@ -32,10 +32,32 @@ class Dependencies(Group): 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(dependency_type=IndependentFactory) +assert isinstance(instance2, IndependentFactory) + +# Resolve by name (uses the attribute name in the Group) +instance3 = container.resolve(dependency_name="independent_factory") +assert isinstance(instance3, IndependentFactory) ``` +### Resolving Dependencies + +Dependencies can be resolved in three ways: + +1. **By provider reference** - Using `container.resolve_provider(provider)` +2. **By type** - Using `container.resolve(dependency_type=SomeType)` +3. **By name** - Using `container.resolve(dependency_name="provider_name")` + +When resolving by type, the container looks for a provider whose `bound_type` matches the requested type. +By default, the `bound_type` is automatically inferred from the creator's return type annotation. + +When resolving by name, the container looks for a provider with a matching attribute name in the Group. + ## Cached Factories Cached factories resolve the dependency only once and cache the resolved instance for future injections. @@ -94,3 +116,78 @@ class Dependencies(Group): ) ) ``` + +## Creator Parsing and Manual Dependency Injection + +By default, Modern-DI automatically parses the creator function or class to determine: +1. The return type (used for `bound_type`) +2. The parameter types (used for automatic dependency resolution) + +However, there are cases where you might want to disable this automatic parsing: + +### Skipping Creator Parsing + +You can disable automatic creator parsing by setting `skip_creator_parsing=True`: + +```python +from modern_di import Group, Container, Scope, providers + +@dataclasses.dataclass +class MyService: + config_value: str + computed_value: int + +class Dependencies(Group): + # Skip parsing - you must provide all dependencies manually + my_service = providers.Factory( + scope=Scope.APP, + creator=MyService, + kwargs={"config_value": "example", "computed_value": 42}, + skip_creator_parsing=True + ) + +container = Container(groups=[Dependencies]) +service = container.resolve_provider(Dependencies.my_service) +``` + +When `skip_creator_parsing=True`: +- No automatic dependency resolution occurs +- All parameters must be provided via the `kwargs` parameter +- The `bound_type` will not be automatically inferred and defaults to `None` + +### Manual Dependency Injection + +Even when not skipping creator parsing, you can still override automatic dependency resolution by providing explicit values in `kwargs`: + +```python +from modern_di import Group, Container, Scope, providers + +@dataclasses.dataclass +class DatabaseConfig: + host: str + port: int + +@dataclasses.dataclass +class DatabaseConnection: + config: DatabaseConfig + timeout: int = 30 + +class Dependencies(Group): + db_config = providers.Factory( + scope=Scope.APP, + creator=DatabaseConfig, + kwargs={"host": "localhost", "port": 5432} + ) + + # Override automatic dependency resolution for timeout + db_connection = providers.Factory( + scope=Scope.APP, + creator=DatabaseConnection, + kwargs={"timeout": 60} # Override the default timeout + ) + +container = Container(groups=[Dependencies]) +connection = container.resolve_provider(Dependencies.db_connection) +``` + +In this example, the `db_connection` provider will automatically resolve the `config` parameter from the `db_config` provider, but will use the manually provided value of `60` for the `timeout` parameter instead of the default `30`.