Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/introduction/key-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
10 changes: 10 additions & 0 deletions docs/providers/context-providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
97 changes: 97 additions & 0 deletions docs/providers/factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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`.