Skip to content

Commit f694b00

Browse files
authored
Merge pull request #136 from modern-python/refactor
refactor container provider, remove object provider
2 parents 4ef07e5 + 0d0e79a commit f694b00

18 files changed

Lines changed: 100 additions & 78 deletions

File tree

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,10 @@
99
| modern-di-faststream | [![Supported versions](https://img.shields.io/pypi/pyversions/modern-di-faststream.svg)](https://pypi.python.org/pypi/modern-di-faststream) [![downloads](https://img.shields.io/pypi/dm/modern-di-faststream.svg)](https://pypistats.org/packages/modern-di-faststream) |
1010
| modern-di-litestar | [![Supported versions](https://img.shields.io/pypi/pyversions/modern-di-litestar.svg)](https://pypi.python.org/pypi/modern-di-litestar) [![downloads](https://img.shields.io/pypi/dm/modern-di-litestar.svg)](https://pypistats.org/packages/modern-di-litestar) |
1111

12-
`modern-di` is a python dependency injection framework which, among other things,
13-
supports the following:
12+
`modern-di` is a python dependency injection framework which supports the following:
1413

1514
- Automatic dependencies graph based on type annotations
16-
- Scopes and granular context management
15+
- Scopes and context management
1716
- Python 3.10+ support
1817
- Fully typed and tested
1918
- Integrations with `FastAPI`, `FastStream` and `LiteStar`

docs/dev/decisions.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Decisions
2-
1. Dependency resolving is sync only since 2.x version
3-
2. Singletons are thread-safe for concurrent resolving (`threading.Lock` is used)
4-
3. No global state -> all state lives in containers and registries:
5-
4. Focus on maximum compatibility with mypy:
2+
1. Dependency resolving is sync only (since 2.x version)
3+
2. Cached factories are thread-safe for concurrent resolving (`threading.Lock` is used)
4+
3. No global state -> all state lives in registries of containers:
5+
4. Focus on maximum type safety:
66
- No need for `# type: ignore`
77
- No need for `typing.cast`
88
5. No adding new features while tasks can be solved by default implementation.

docs/index.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@
22

33
Welcome to the `modern-di` documentation!
44

5-
`modern-di` is a Python dependency injection framework which, among other things,
6-
supports the following:
5+
`modern-di` is a Python dependency injection framework whichsupports the following:
76

87
- Automatic dependencies graph based on type annotations
9-
- Scopes and granular context management
8+
- Scopes and context management
109
- Python 3.10+ support
1110
- Fully typed and tested
1211
- Integrations with `FastAPI`, `FastStream` and `LiteStar`
@@ -73,7 +72,7 @@ from modern_di import Group, Scope, providers
7372

7473
class Dependencies(Group):
7574
singleton = providers.Factory(
76-
scope=Scope.APP,
75+
scope=Scope.APP, # scope is APP by default, can be missing
7776
creator=create_singleton,
7877
cache_settings=providers.CacheSettings()
7978
)

docs/introduction/key-concepts.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ Container also provides methods for overriding providers with objects:
7474

7575
When resolving by type, the container looks for a provider that was registered with a matching `bound_type`.
7676

77+
The container itself can also be resolved as a dependency using `container.resolve(Container)`, which returns the same container instance.
78+
7779
## Group
7880

7981
A Group is a collection of providers. They cannot be instantiated.

docs/introduction/resolving.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ One of the key features of `Modern-DI` since 2.x version is the automatic resolu
1616
### How It Works
1717

1818
When a factory is created:
19+
1920
1. Modern-DI parses the creator's signature to identify parameter names and types
2021
2. For each parameter with a type annotation, it searches for a registered provider that matches by parameter type.
2122
3. If a matching provider is found, it's automatically injected when the factory is resolved
@@ -27,12 +28,12 @@ Example:
2728
import dataclasses
2829
from modern_di import Group, Container, Scope, providers
2930

30-
@dataclasses.dataclass(kw_only=True, slots=True)
31+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
3132
class DatabaseConfig:
3233
host: str
3334
port: int
3435

35-
@dataclasses.dataclass(kw_only=True, slots=True)
36+
@dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
3637
class DatabaseConnection:
3738
config: DatabaseConfig # Automatically resolved by type
3839
timeout: int = 30 # Uses default value

docs/providers/container.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Container Provider
2+
3+
The Container Provider is a special provider that you should not initialize
4+
It is automatically registered with each container, so you can resolve the container itself directly:
5+
6+
```python
7+
from modern_di import Container, providers
8+
9+
container = Container()
10+
11+
# Resolve the container itself
12+
the_container = container.resolve(Container)
13+
the_same_container = container.resolve_provider(providers.container_provider)
14+
```
15+
16+
It's synthetic example just to show that it works. More useful example is to inject `Container` to another object:
17+
18+
```python
19+
from modern_di import Container, Group, providers
20+
21+
def some_creator(di_container: Container) -> str:
22+
# do sth with container
23+
return "string"
24+
25+
class Dependencies(Group):
26+
some_factory = providers.Factory(creator=some_creator)
27+
28+
# explicit container injection
29+
another_factory = providers.Factory(creator=some_creator, kwargs={"di_container": providers.container_provider})
30+
31+
```
File renamed without changes.

docs/testing/fixtures.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ async def di_container() -> typing.AsyncIterator[modern_di.Container]:
3030

3131
@pytest.fixture
3232
async def request_di_container(di_container: modern_di.Container) -> typing.AsyncIterator[modern_di.Container]:
33+
"""Fixture with REQUEST-scope container."""
3334
di_container_ = di_container.build_child_container(scope=modern_di.Scope.REQUEST)
3435
try:
3536
yield di_container_

mkdocs.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ nav:
99
- Key Concepts: introduction/key-concepts.md
1010
- Providers:
1111
- Factories: providers/factories.md
12-
- Context Providers: providers/context-providers.md
12+
- Context: providers/context.md
13+
- Container: providers/container.md
1314
- Integrations:
1415
- FastAPI: integrations/fastapi.md
1516
- FastStream: integrations/faststream.md

packages/modern-di/modern_di/container.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from modern_di import types
77
from modern_di.group import Group
88
from modern_di.providers.abstract import AbstractProvider
9+
from modern_di.providers.container_provider import container_provider
910
from modern_di.registries.cache_registry import CacheRegistry
1011
from modern_di.registries.context_registry import ContextRegistry
1112
from modern_di.registries.overrides_registry import OverridesRegistry
@@ -44,7 +45,8 @@ def __init__(
4445
self.overrides_registry = parent_container.overrides_registry
4546
else:
4647
self.providers_registry = ProvidersRegistry()
47-
self.providers_registry.add_providers(_ContainerProvider())
48+
container_provider.bound_type = type(self)
49+
self.providers_registry.add_providers(container_provider)
4850
self.overrides_registry = OverridesRegistry()
4951
if groups:
5052
for one_group in groups:
@@ -121,13 +123,3 @@ def __deepcopy__(self, *_: object, **__: object) -> "typing_extensions.Self":
121123
def __copy__(self, *_: object, **__: object) -> "typing_extensions.Self":
122124
"""Prevent cloning object."""
123125
return self
124-
125-
126-
class _ContainerProvider(AbstractProvider[typing.Any]):
127-
__slots__ = AbstractProvider.BASE_SLOTS
128-
129-
def __init__(self) -> None:
130-
super().__init__(scope=Scope.APP, bound_type=Container)
131-
132-
def resolve(self, container: Container) -> Container:
133-
return container

0 commit comments

Comments
 (0)