modern-di 0.5.1__tar.gz → 0.6.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of modern-di might be problematic. Click here for more details.

@@ -0,0 +1,120 @@
1
+ Metadata-Version: 2.3
2
+ Name: modern-di
3
+ Version: 0.6.0
4
+ Summary: Dependency Injection framework with IOC-container and scopes
5
+ Project-URL: repository, https://github.com/modern-python/modern-di
6
+ Project-URL: docs, https://modern-di.readthedocs.io
7
+ Author-email: Artur Shiriev <me@shiriev.ru>
8
+ Keywords: DI,dependency injector,ioc-container,mocks,python
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Topic :: Software Development :: Libraries
14
+ Classifier: Typing :: Typed
15
+ Requires-Python: <4,>=3.10
16
+ Description-Content-Type: text/markdown
17
+
18
+ "Modern-DI"
19
+ ==
20
+
21
+ | Project | Badges |
22
+ |--------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
23
+ | common | [![MyPy Strict](https://img.shields.io/badge/mypy-strict-blue)](https://mypy.readthedocs.io/en/stable/getting_started.html#strict-mode-and-configuration) [![GitHub stars](https://img.shields.io/github/stars/modern-python/modern-di)](https://github.com/modern-python/modern-di/stargazers) |
24
+ | modern-di | [![Supported versions](https://img.shields.io/pypi/pyversions/modern-di.svg)](https://pypi.python.org/pypi/modern-di ) [![downloads](https://img.shields.io/pypi/dm/modern-di.svg)](https://pypistats.org/packages/modern-di) |
25
+ | modern-di-fastapi | [![Supported versions](https://img.shields.io/pypi/pyversions/modern-di-fastapi.svg)](https://pypi.python.org/pypi/modern-di-fastapi) [![downloads](https://img.shields.io/pypi/dm/modern-di-fastapi.svg)](https://pypistats.org/packages/modern-di-fastapi) |
26
+ | 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) |
27
+
28
+ Dependency injection framework for Python inspired by `dependency-injector` and `dishka`.
29
+
30
+ It is in early development state and gives you the following:
31
+ - DI framework with IOC-container and scopes.
32
+ - Async and sync resolving.
33
+ - Python 3.10-3.13 support.
34
+ - Full coverage by types annotations (mypy in strict mode).
35
+ - Overriding dependencies for tests.
36
+ - Package with zero dependencies.
37
+
38
+ 📚 [Documentation](https://modern-di.readthedocs.io)
39
+
40
+ ## Describe resources and classes:
41
+ ```python
42
+ import dataclasses
43
+ import logging
44
+ import typing
45
+
46
+
47
+ logger = logging.getLogger(__name__)
48
+
49
+
50
+ # singleton provider with finalization
51
+ def create_sync_resource() -> typing.Iterator[str]:
52
+ logger.debug("Resource initiated")
53
+ try:
54
+ yield "sync resource"
55
+ finally:
56
+ logger.debug("Resource destructed")
57
+
58
+
59
+ # same, but async
60
+ async def create_async_resource() -> typing.AsyncIterator[str]:
61
+ logger.debug("Async resource initiated")
62
+ try:
63
+ yield "async resource"
64
+ finally:
65
+ logger.debug("Async resource destructed")
66
+
67
+
68
+ @dataclasses.dataclass(kw_only=True, slots=True)
69
+ class DependentFactory:
70
+ sync_resource: str
71
+ async_resource: str
72
+ ```
73
+
74
+ ## Describe dependencies graph (IoC-container)
75
+ ```python
76
+ from modern_di import BaseGraph, Scope, providers
77
+
78
+
79
+ class Dependencies(BaseGraph):
80
+ sync_resource = providers.Resource(Scope.APP, create_sync_resource)
81
+ async_resource = providers.Resource(Scope.APP, create_async_resource)
82
+
83
+ simple_factory = providers.Factory(Scope.REQUEST, SimpleFactory, dep1="text", dep2=123)
84
+ dependent_factory = providers.Factory(
85
+ Scope.REQUEST,
86
+ sync_resource=sync_resource,
87
+ async_resource=async_resource,
88
+ )
89
+ ```
90
+
91
+ ## Create container and resolve dependencies in your code
92
+ ```python
93
+ from modern_di import Container, Scope
94
+
95
+
96
+ # init container of app scope in sync mode
97
+ with Container(scope=Scope.APP) as app_container:
98
+ # resolve sync resource
99
+ Dependencies.sync_resource.sync_resolve(app_container)
100
+
101
+
102
+ # init container of app scope in async mode
103
+ async with Container(scope=Scope.APP) as app_container:
104
+ # resolve async resource
105
+ await Dependencies.async_resource.async_resolve(app_container)
106
+
107
+ # resolve sync resource
108
+ instance1 = await Dependencies.sync_resource.async_resolve(app_container)
109
+ instance2 = Dependencies.sync_resource.sync_resolve(app_container)
110
+ assert instance1 is instance2
111
+
112
+ # create container of request scope
113
+ async with app_container.build_child_container(scope=Scope.REQUEST) as request_container:
114
+ # resolve factories of request scope
115
+ Dependencies.simple_factory.sync_resolve(request_container)
116
+ await Dependencies.dependent_factory.async_resolve(request_container)
117
+
118
+ # resources of app-scope also can be resolved here
119
+
120
+ ```
@@ -3,6 +3,7 @@ import typing
3
3
 
4
4
  from modern_di import Container
5
5
  from modern_di.providers.abstract import AbstractCreatorProvider
6
+ from modern_di.providers.injected_factory import InjectedFactory
6
7
 
7
8
 
8
9
  T_co = typing.TypeVar("T_co", covariant=True)
@@ -21,6 +22,10 @@ class Factory(AbstractCreatorProvider[T_co]):
21
22
  ) -> None:
22
23
  super().__init__(scope, creator, *args, **kwargs)
23
24
 
25
+ @property
26
+ def factory_provider(self) -> InjectedFactory[T_co]:
27
+ return InjectedFactory(self)
28
+
24
29
  async def async_resolve(self, container: Container) -> T_co:
25
30
  container = container.find_container(self.scope)
26
31
  if (override := container.fetch_override(self.provider_id)) is not None:
@@ -0,0 +1,24 @@
1
+ import functools
2
+ import typing
3
+
4
+ from modern_di import Container
5
+ from modern_di.providers.abstract import AbstractProvider
6
+
7
+
8
+ T_co = typing.TypeVar("T_co", covariant=True)
9
+ P = typing.ParamSpec("P")
10
+
11
+
12
+ class InjectedFactory(typing.Generic[T_co]):
13
+ __slots__ = ("_factory_provider",)
14
+
15
+ def __init__(self, factory_provider: AbstractProvider[T_co]) -> None:
16
+ self._factory_provider = factory_provider
17
+
18
+ async def async_resolve(self, container: Container) -> typing.Callable[[], T_co]:
19
+ await self._factory_provider.async_resolve(container)
20
+ return functools.partial(self._factory_provider.sync_resolve, container)
21
+
22
+ def sync_resolve(self, container: Container) -> typing.Callable[[], T_co]:
23
+ self._factory_provider.sync_resolve(container)
24
+ return functools.partial(self._factory_provider.sync_resolve, container)
@@ -23,7 +23,13 @@ class Resource(AbstractCreatorProvider[T_co]):
23
23
  def __init__(
24
24
  self,
25
25
  scope: enum.IntEnum,
26
- creator: typing.Callable[P, typing.Iterator[T_co] | typing.AsyncIterator[T_co]],
26
+ creator: typing.Callable[
27
+ P,
28
+ typing.Iterator[T_co]
29
+ | typing.AsyncIterator[T_co]
30
+ | typing.ContextManager[T_co]
31
+ | typing.AsyncContextManager[T_co],
32
+ ],
27
33
  *args: P.args,
28
34
  **kwargs: P.kwargs,
29
35
  ) -> None:
@@ -34,9 +40,15 @@ class Resource(AbstractCreatorProvider[T_co]):
34
40
  elif inspect.isgeneratorfunction(creator):
35
41
  self._is_async = False
36
42
  new_creator = contextlib.contextmanager(creator)
43
+ elif isinstance(creator, type) and issubclass(creator, typing.AsyncContextManager):
44
+ self._is_async = True
45
+ new_creator = creator
46
+ elif isinstance(creator, type) and issubclass(creator, typing.ContextManager):
47
+ self._is_async = False
48
+ new_creator = creator
37
49
  else:
38
50
  msg = "Unsupported resource type"
39
- raise RuntimeError(msg)
51
+ raise TypeError(msg)
40
52
 
41
53
  super().__init__(scope, new_creator, *args, **kwargs)
42
54
 
@@ -19,6 +19,16 @@ dynamic = ["version", "readme"]
19
19
  repository = "https://github.com/modern-python/modern-di"
20
20
  docs = "https://modern-di.readthedocs.io"
21
21
 
22
+ [dependency-groups]
23
+ dev = [
24
+ "pytest",
25
+ "pytest-cov",
26
+ "pytest-asyncio",
27
+ "ruff",
28
+ "mypy",
29
+ "typing-extensions",
30
+ ]
31
+
22
32
  [build-system]
23
33
  requires = ["hatchling", "hatch-vcs", "hatch-fancy-pypi-readme"]
24
34
  build-backend = "hatchling.build"
@@ -40,6 +50,7 @@ path = "../../README.md"
40
50
  [tool.pytest.ini_options]
41
51
  addopts = "--cov=. --cov-report term-missing"
42
52
  asyncio_mode = "auto"
53
+ asyncio_default_fixture_loop_scope = "function"
43
54
 
44
55
  [tool.coverage.report]
45
56
  exclude_also = ["if typing.TYPE_CHECKING:"]
modern_di-0.5.1/PKG-INFO DELETED
@@ -1,38 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: modern-di
3
- Version: 0.5.1
4
- Summary: Dependency Injection framework with IOC-container and scopes
5
- Project-URL: repository, https://github.com/modern-python/modern-di
6
- Project-URL: docs, https://modern-di.readthedocs.io
7
- Author-email: Artur Shiriev <me@shiriev.ru>
8
- License-Expression: MIT
9
- Keywords: DI,dependency injector,ioc-container,mocks,python
10
- Classifier: Programming Language :: Python :: 3.10
11
- Classifier: Programming Language :: Python :: 3.11
12
- Classifier: Programming Language :: Python :: 3.12
13
- Classifier: Programming Language :: Python :: 3.13
14
- Classifier: Topic :: Software Development :: Libraries
15
- Classifier: Typing :: Typed
16
- Requires-Python: <4,>=3.10
17
- Description-Content-Type: text/markdown
18
-
19
- "Modern-DI"
20
- ==
21
-
22
- | Project | Badges |
23
- |-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
24
- | common | [![MyPy Strict](https://img.shields.io/badge/mypy-strict-blue)](https://mypy.readthedocs.io/en/stable/getting_started.html#strict-mode-and-configuration) [![GitHub stars](https://img.shields.io/github/stars/modern-python/modern-di)](https://github.com/modern-python/modern-di/stargazers) |
25
- | modern-di | [![Supported versions](https://img.shields.io/pypi/pyversions/modern-di.svg)](https://pypi.python.org/pypi/modern-di ) [![downloads](https://img.shields.io/pypi/dm/modern-di.svg)](https://pypistats.org/packages/modern-di) |
26
- | modern-di-fastapi | [![Supported versions](https://img.shields.io/pypi/pyversions/modern-di-fastapi.svg)](https://pypi.python.org/pypi/modern-di-fastapi) [![downloads](https://img.shields.io/pypi/dm/modern-di-fastapi.svg)](https://pypistats.org/packages/modern-di-fastapi) |
27
-
28
- Dependency injection framework for Python inspired by `dependency-injector` and `dishka`.
29
-
30
- It is in early development state and gives you the following:
31
- - DI framework with IOC-container and scopes.
32
- - Async and sync resolving.
33
- - Python 3.10-3.13 support.
34
- - Full coverage by types annotations (mypy in strict mode).
35
- - Overriding dependencies for tests.
36
- - Package with zero dependencies.
37
-
38
- 📚 [Documentation](https://modern-di.readthedocs.io)
File without changes
File without changes
File without changes
File without changes