modern-di 0.5.1__tar.gz → 0.7.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,122 @@
1
+ Metadata-Version: 2.3
2
+ Name: modern-di
3
+ Version: 0.7.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
+ License: 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
+ | 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) |
28
+
29
+ Dependency injection framework for Python inspired by `dependency-injector` and `dishka`.
30
+
31
+ It is in development state yet and gives you the following:
32
+ - DI framework with IOC-container and scopes.
33
+ - Async and sync resolving.
34
+ - Python 3.10-3.13 support.
35
+ - Full coverage by types annotations (mypy in strict mode).
36
+ - Overriding dependencies for tests.
37
+ - Package with zero dependencies.
38
+ - Integration with FastAPI and LiteStar
39
+
40
+ 📚 [Documentation](https://modern-di.readthedocs.io)
41
+
42
+ ## Describe resources and classes:
43
+ ```python
44
+ import dataclasses
45
+ import logging
46
+ import typing
47
+
48
+
49
+ logger = logging.getLogger(__name__)
50
+
51
+
52
+ # singleton provider with finalization
53
+ def create_sync_resource() -> typing.Iterator[str]:
54
+ logger.debug("Resource initiated")
55
+ try:
56
+ yield "sync resource"
57
+ finally:
58
+ logger.debug("Resource destructed")
59
+
60
+
61
+ # same, but async
62
+ async def create_async_resource() -> typing.AsyncIterator[str]:
63
+ logger.debug("Async resource initiated")
64
+ try:
65
+ yield "async resource"
66
+ finally:
67
+ logger.debug("Async resource destructed")
68
+
69
+
70
+ @dataclasses.dataclass(kw_only=True, slots=True)
71
+ class DependentFactory:
72
+ sync_resource: str
73
+ async_resource: str
74
+ ```
75
+
76
+ ## Describe dependencies graph (IoC-container)
77
+ ```python
78
+ from modern_di import BaseGraph, Scope, providers
79
+
80
+
81
+ class Dependencies(BaseGraph):
82
+ sync_resource = providers.Resource(Scope.APP, create_sync_resource)
83
+ async_resource = providers.Resource(Scope.APP, create_async_resource)
84
+
85
+ simple_factory = providers.Factory(Scope.REQUEST, SimpleFactory, dep1="text", dep2=123)
86
+ dependent_factory = providers.Factory(
87
+ Scope.REQUEST,
88
+ sync_resource=sync_resource,
89
+ async_resource=async_resource,
90
+ )
91
+ ```
92
+
93
+ ## Create container and resolve dependencies in your code
94
+ ```python
95
+ from modern_di import Container, Scope
96
+
97
+
98
+ # init container of app scope in sync mode
99
+ with Container(scope=Scope.APP) as app_container:
100
+ # resolve sync resource
101
+ Dependencies.sync_resource.sync_resolve(app_container)
102
+
103
+
104
+ # init container of app scope in async mode
105
+ async with Container(scope=Scope.APP) as app_container:
106
+ # resolve async resource
107
+ await Dependencies.async_resource.async_resolve(app_container)
108
+
109
+ # resolve sync resource
110
+ instance1 = await Dependencies.sync_resource.async_resolve(app_container)
111
+ instance2 = Dependencies.sync_resource.sync_resolve(app_container)
112
+ assert instance1 is instance2
113
+
114
+ # create container of request scope
115
+ async with app_container.build_child_container(scope=Scope.REQUEST) as request_container:
116
+ # resolve factories of request scope
117
+ Dependencies.simple_factory.sync_resolve(request_container)
118
+ await Dependencies.dependent_factory.async_resolve(request_container)
119
+
120
+ # resources of app-scope also can be resolved here
121
+
122
+ ```
@@ -1,4 +1,5 @@
1
1
  from modern_di.providers.abstract import AbstractProvider
2
+ from modern_di.providers.container_provider import ContainerProvider
2
3
  from modern_di.providers.context_adapter import ContextAdapter
3
4
  from modern_di.providers.dict import Dict
4
5
  from modern_di.providers.factory import Factory
@@ -10,6 +11,7 @@ from modern_di.providers.singleton import Singleton
10
11
 
11
12
  __all__ = [
12
13
  "AbstractProvider",
14
+ "ContainerProvider",
13
15
  "ContextAdapter",
14
16
  "Factory",
15
17
  "Dict",
@@ -0,0 +1,19 @@
1
+ import typing
2
+
3
+ import modern_di
4
+ from modern_di import Container
5
+ from modern_di.providers import AbstractProvider
6
+
7
+
8
+ T_co = typing.TypeVar("T_co", covariant=True)
9
+ P = typing.ParamSpec("P")
10
+
11
+
12
+ class ContainerProvider(AbstractProvider[modern_di.Container]):
13
+ __slots__ = AbstractProvider.BASE_SLOTS
14
+
15
+ async def async_resolve(self, container: Container) -> modern_di.Container:
16
+ return self.sync_resolve(container)
17
+
18
+ def sync_resolve(self, container: Container) -> modern_di.Container:
19
+ return container.find_container(self.scope)
@@ -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