modern-di 0.14.0__py3-none-any.whl → 1.0.0a4__py3-none-any.whl

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.
Files changed (36) hide show
  1. modern_di/__init__.py +6 -4
  2. modern_di/containers/__init__.py +0 -0
  3. modern_di/containers/abstract.py +172 -0
  4. modern_di/containers/async_container.py +98 -0
  5. modern_di/containers/sync_container.py +40 -0
  6. modern_di/group.py +26 -0
  7. modern_di/helpers/type_helpers.py +33 -0
  8. modern_di/providers/__init__.py +4 -4
  9. modern_di/providers/abstract.py +52 -108
  10. modern_di/providers/async_factory.py +10 -12
  11. modern_di/providers/async_singleton.py +37 -0
  12. modern_di/providers/container_provider.py +4 -8
  13. modern_di/providers/context_provider.py +16 -0
  14. modern_di/providers/dict.py +20 -13
  15. modern_di/providers/factory.py +18 -24
  16. modern_di/providers/list.py +20 -13
  17. modern_di/providers/object.py +6 -11
  18. modern_di/providers/resource.py +39 -67
  19. modern_di/providers/singleton.py +24 -44
  20. modern_di/registries/__init__.py +0 -0
  21. modern_di/registries/context_registry.py +16 -0
  22. modern_di/registries/overrides_registry.py +22 -0
  23. modern_di/registries/providers_registry.py +38 -0
  24. modern_di/registries/state_registry/__init__.py +0 -0
  25. modern_di/{provider_state.py → registries/state_registry/state.py} +2 -6
  26. modern_di/registries/state_registry/state_registry.py +30 -0
  27. {modern_di-0.14.0.dist-info → modern_di-1.0.0a4.dist-info}/METADATA +1 -1
  28. modern_di-1.0.0a4.dist-info/RECORD +32 -0
  29. modern_di/container.py +0 -162
  30. modern_di/graph.py +0 -39
  31. modern_di/helpers/attr_getter_helpers.py +0 -7
  32. modern_di/providers/context_adapter.py +0 -27
  33. modern_di/providers/injected_factory.py +0 -45
  34. modern_di/providers/selector.py +0 -39
  35. modern_di-0.14.0.dist-info/RECORD +0 -24
  36. {modern_di-0.14.0.dist-info → modern_di-1.0.0a4.dist-info}/WHEEL +0 -0
@@ -0,0 +1,32 @@
1
+ modern_di/__init__.py,sha256=3VNwCoKLtuRvHlw1LlEE_GqML6mSPxt2xiIb0lLPglE,296
2
+ modern_di/group.py,sha256=VUs8qHz_mzrFHr88Ztt4bEGJ1lNNcZuLdJD5SPksefc,707
3
+ modern_di/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ modern_di/scope.py,sha256=e6Olc-CF89clbYDNGciy-F8EqJt1Mw2703zfuJaEY94,113
5
+ modern_di/containers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ modern_di/containers/abstract.py,sha256=meFmNooMGcQIJ6PbsqILcVJsmaH6ADLcW_JGqQuIhS8,6823
7
+ modern_di/containers/async_container.py,sha256=JJJR_DQlHVIRe9-y1drti154ElgTBkqAN6dkDAdu3I8,3749
8
+ modern_di/containers/sync_container.py,sha256=JF9VQiHFqdsvSfbwaJl9wwxbTPrN-ztAQn7SMDsYUPg,1249
9
+ modern_di/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ modern_di/helpers/type_helpers.py,sha256=sIQmM6aLj8UJ-JL3cpFattdV8rVegDHPFODFAm-fjP0,795
11
+ modern_di/providers/__init__.py,sha256=NidhR4kH4HkgL4tLurG4S2W4mWuG5ENvPEZckWsMW2U,810
12
+ modern_di/providers/abstract.py,sha256=6c80h4SvYtwh4-JOk6INsWn6V6fh_J5Mn1y7rrW9A-k,2890
13
+ modern_di/providers/async_factory.py,sha256=Bx_xHET6c_IKttEnwFc4VDpLft25gvVpZ_uDVx3kkYQ,815
14
+ modern_di/providers/async_singleton.py,sha256=nXPWr-0oM9xdUwh4Cikni92XMqRLF3PvRp0RCKxoTXI,1046
15
+ modern_di/providers/container_provider.py,sha256=QMOT767Yl00sPJKBW_926m7ZfyIUOG_zzOoPxg9Na2Q,337
16
+ modern_di/providers/context_provider.py,sha256=lZu_INZnbiWQsR-qMrKEOtZpYaSiE4-rf2ZIP5p3-x8,427
17
+ modern_di/providers/dict.py,sha256=ni2avO16XfX98mE6tgIX8v0Pn4iAPQYL_1YBDB4WlHo,708
18
+ modern_di/providers/factory.py,sha256=ocbdlsPoxtoc_t2D0wWsssP08_8gAUAJmr5IOb4Q3sQ,943
19
+ modern_di/providers/list.py,sha256=KZPBctoE5LqgHemBR3Viq5pA7w6CJMOsHbvlBlbF4Kg,674
20
+ modern_di/providers/object.py,sha256=5hoyLC5whtG2TNlOQe3LKTKTRvQEmhOWpzugN1hczhI,572
21
+ modern_di/providers/resource.py,sha256=wB0EFV6HOkdbeU-ibvklz0b8Zs0ZOAyqYpJhxTKW3aw,3173
22
+ modern_di/providers/singleton.py,sha256=ACEcXzjXJbrFji_0LtFJvRGfZbxJBX7EJaH82dEBKQg,1276
23
+ modern_di/registries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
24
+ modern_di/registries/context_registry.py,sha256=fdHvQIHYyaRqAK8MFG80HXKixo-CRvFN20LeRBHSCs4,412
25
+ modern_di/registries/overrides_registry.py,sha256=Qh5D0z7_sh6Y1S94w7w61vjvVbdu4z9gnRvNGDDdWxk,706
26
+ modern_di/registries/providers_registry.py,sha256=wyuwoSNcLP3vjPw3vttkGuoZu7KmCd8WV6UF5QYLFyk,1564
27
+ modern_di/registries/state_registry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ modern_di/registries/state_registry/state.py,sha256=sb8dVe62eO4Vbvpf9r_6ilkMW96UTtcj6NDcC_D1QTA,1062
29
+ modern_di/registries/state_registry/state_registry.py,sha256=jOTznGH7YGNVFEtdMvT-MaPb0iBN6XvXKLWwrDWerxE,1032
30
+ modern_di-1.0.0a4.dist-info/METADATA,sha256=7ea01J8jGxiPYD0ObgN0t4uIpXoSe2apxr9QLTycgX0,3383
31
+ modern_di-1.0.0a4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
32
+ modern_di-1.0.0a4.dist-info/RECORD,,
modern_di/container.py DELETED
@@ -1,162 +0,0 @@
1
- import contextlib
2
- import enum
3
- import types
4
- import typing
5
-
6
- from modern_di.provider_state import ProviderState
7
-
8
-
9
- if typing.TYPE_CHECKING:
10
- import typing_extensions
11
-
12
-
13
- T_co = typing.TypeVar("T_co", covariant=True)
14
-
15
-
16
- class Container(contextlib.AbstractAsyncContextManager["Container"], contextlib.AbstractContextManager["Container"]):
17
- __slots__ = (
18
- "_is_async",
19
- "_overrides",
20
- "_provider_states",
21
- "_use_threading_lock",
22
- "context",
23
- "parent_container",
24
- "scope",
25
- )
26
-
27
- def __init__(
28
- self,
29
- *,
30
- scope: enum.IntEnum,
31
- parent_container: typing.Optional["Container"] = None,
32
- context: dict[str, typing.Any] | None = None,
33
- use_threading_lock: bool = True,
34
- ) -> None:
35
- self.scope = scope
36
- self.parent_container = parent_container
37
- self.context: dict[str, typing.Any] = context or {}
38
- self._is_async: bool | None = None
39
- self._provider_states: dict[str, ProviderState[typing.Any]] = {}
40
- self._overrides: dict[str, typing.Any] = parent_container._overrides if parent_container else {} # noqa: SLF001
41
- self._use_threading_lock = use_threading_lock
42
-
43
- def _exit(self) -> None:
44
- self._is_async = None
45
- self._provider_states = {}
46
- self._overrides = {}
47
- self.context = {}
48
-
49
- def _check_entered(self) -> None:
50
- if self._is_async is None:
51
- msg = "Enter the context first"
52
- raise RuntimeError(msg)
53
-
54
- def build_child_container(
55
- self, context: dict[str, typing.Any] | None = None, scope: enum.IntEnum | None = None
56
- ) -> "typing_extensions.Self":
57
- self._check_entered()
58
- if scope and scope <= self.scope:
59
- msg = "Scope of child container must be more than current scope"
60
- raise RuntimeError(msg)
61
-
62
- if not scope:
63
- try:
64
- scope = self.scope.__class__(self.scope.value + 1)
65
- except ValueError as exc:
66
- msg = f"Max scope is reached, {self.scope.name}"
67
- raise RuntimeError(msg) from exc
68
-
69
- return self.__class__(scope=scope, parent_container=self, context=context)
70
-
71
- def find_container(self, scope: enum.IntEnum) -> "typing_extensions.Self":
72
- container = self
73
- if container.scope < scope:
74
- msg = f"Scope {scope.name} is not initialized"
75
- raise RuntimeError(msg)
76
-
77
- while container.scope > scope and container.parent_container:
78
- container = typing.cast("typing_extensions.Self", container.parent_container)
79
-
80
- if container.scope != scope:
81
- msg = f"Scope {scope.name} is skipped"
82
- raise RuntimeError(msg)
83
-
84
- return container
85
-
86
- def fetch_provider_state(
87
- self,
88
- provider_id: str,
89
- is_async_resource: bool = False,
90
- use_asyncio_lock: bool = False,
91
- use_threading_lock: bool = False,
92
- ) -> ProviderState[typing.Any]:
93
- self._check_entered()
94
- if is_async_resource and self._is_async is False:
95
- msg = "Resolving async resource in sync container is not allowed"
96
- raise RuntimeError(msg)
97
-
98
- if provider_state := self._provider_states.get(provider_id):
99
- return provider_state
100
-
101
- # expected to be thread-safe, because setdefault is atomic
102
- return self._provider_states.setdefault(
103
- provider_id,
104
- ProviderState(
105
- use_asyncio_lock=use_asyncio_lock,
106
- use_threading_lock=self._use_threading_lock and use_threading_lock,
107
- ),
108
- )
109
-
110
- def override(self, provider_id: str, override_object: object) -> None:
111
- self._overrides[provider_id] = override_object
112
-
113
- def fetch_override(self, provider_id: str) -> object | None:
114
- return self._overrides.get(provider_id)
115
-
116
- def reset_override(self, provider_id: str | None = None) -> None:
117
- if provider_id is None:
118
- self._overrides = {}
119
- else:
120
- self._overrides.pop(provider_id, None)
121
-
122
- def async_enter(self) -> "Container":
123
- self._is_async = True
124
- return self
125
-
126
- def sync_enter(self) -> "Container":
127
- self._is_async = False
128
- return self
129
-
130
- async def async_close(self) -> None:
131
- self._check_entered()
132
- for provider_state in reversed(self._provider_states.values()):
133
- await provider_state.async_tear_down()
134
- self._exit()
135
-
136
- def sync_close(self) -> None:
137
- self._check_entered()
138
- for provider_state in reversed(self._provider_states.values()):
139
- provider_state.sync_tear_down()
140
- self._exit()
141
-
142
- async def __aenter__(self) -> "Container":
143
- return self.async_enter()
144
-
145
- async def __aexit__(
146
- self,
147
- exc_type: type[BaseException] | None,
148
- exc_val: BaseException | None,
149
- traceback: types.TracebackType | None,
150
- ) -> None:
151
- await self.async_close()
152
-
153
- def __enter__(self) -> "Container":
154
- return self.sync_enter()
155
-
156
- def __exit__(
157
- self,
158
- exc_type: type[BaseException] | None,
159
- exc_value: BaseException | None,
160
- traceback: types.TracebackType | None,
161
- ) -> None:
162
- self.sync_close()
modern_di/graph.py DELETED
@@ -1,39 +0,0 @@
1
- import typing
2
-
3
- from modern_di import Container
4
- from modern_di.providers.abstract import AbstractCreatorProvider, AbstractProvider
5
-
6
-
7
- if typing.TYPE_CHECKING:
8
- import typing_extensions
9
-
10
-
11
- T = typing.TypeVar("T")
12
- P = typing.ParamSpec("P")
13
-
14
-
15
- class BaseGraph:
16
- providers: dict[str, AbstractProvider[typing.Any]]
17
-
18
- def __new__(cls, *_: typing.Any, **__: typing.Any) -> "typing_extensions.Self": # noqa: ANN401
19
- msg = f"{cls.__name__} cannot not be instantiated"
20
- raise RuntimeError(msg)
21
-
22
- @classmethod
23
- def get_providers(cls) -> dict[str, AbstractProvider[typing.Any]]:
24
- if not hasattr(cls, "providers"):
25
- cls.providers = {k: v for k, v in cls.__dict__.items() if isinstance(v, AbstractProvider)}
26
-
27
- return cls.providers
28
-
29
- @classmethod
30
- async def async_resolve_creators(cls, container: Container) -> None:
31
- for provider in cls.get_providers().values():
32
- if isinstance(provider, AbstractCreatorProvider) and provider.scope == container.scope:
33
- await provider.async_resolve(container)
34
-
35
- @classmethod
36
- def sync_resolve_creators(cls, container: Container) -> None:
37
- for provider in cls.get_providers().values():
38
- if isinstance(provider, AbstractCreatorProvider) and provider.scope == container.scope:
39
- provider.sync_resolve(container)
@@ -1,7 +0,0 @@
1
- import typing
2
- from operator import attrgetter
3
-
4
-
5
- def get_value_from_object_by_dotted_path(obj: typing.Any, path: str) -> typing.Any: # noqa: ANN401
6
- attribute_getter = attrgetter(path)
7
- return attribute_getter(obj)
@@ -1,27 +0,0 @@
1
- import enum
2
- import typing
3
-
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 ContextAdapter(AbstractProvider[T_co]):
13
- __slots__ = [*AbstractProvider.BASE_SLOTS, "_function"]
14
-
15
- def __init__(
16
- self,
17
- scope: enum.IntEnum,
18
- function: typing.Callable[..., T_co],
19
- ) -> None:
20
- super().__init__(scope)
21
- self._function = function
22
-
23
- async def async_resolve(self, container: Container) -> T_co:
24
- return self._function(**container.find_container(self.scope).context)
25
-
26
- def sync_resolve(self, container: Container) -> T_co:
27
- return self._function(**container.find_container(self.scope).context)
@@ -1,45 +0,0 @@
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 SyncInjectedFactory(AbstractProvider[T_co]):
13
- __slots__ = [*AbstractProvider.BASE_SLOTS, "_factory_provider"]
14
-
15
- def __init__(self, factory_provider: AbstractProvider[T_co]) -> None:
16
- super().__init__(factory_provider.scope)
17
- self._factory_provider = factory_provider
18
-
19
- async def async_resolve(self, container: Container) -> typing.Callable[[], T_co]: # type: ignore[override]
20
- return self.sync_resolve(container)
21
-
22
- def sync_resolve(self, container: Container) -> typing.Callable[[], T_co]: # type: ignore[override]
23
- return functools.partial(self._factory_provider.sync_resolve, container)
24
-
25
- @property
26
- def cast(self) -> typing.Callable[[], T_co]: # type: ignore[override]
27
- return typing.cast(typing.Callable[[], T_co], self)
28
-
29
-
30
- class AsyncInjectedFactory(AbstractProvider[T_co]):
31
- __slots__ = [*AbstractProvider.BASE_SLOTS, "_factory_provider"]
32
-
33
- def __init__(self, factory_provider: AbstractProvider[T_co]) -> None:
34
- super().__init__(factory_provider.scope)
35
- self._factory_provider = factory_provider
36
-
37
- async def async_resolve(self, container: Container) -> typing.Callable[[], typing.Awaitable[T_co]]: # type: ignore[override]
38
- return self.sync_resolve(container)
39
-
40
- def sync_resolve(self, container: Container) -> typing.Callable[[], typing.Awaitable[T_co]]: # type: ignore[override]
41
- return functools.partial(self._factory_provider.async_resolve, container)
42
-
43
- @property
44
- def cast(self) -> typing.Callable[[], typing.Awaitable[T_co]]: # type: ignore[override]
45
- return typing.cast(typing.Callable[[], typing.Awaitable[T_co]], self)
@@ -1,39 +0,0 @@
1
- import enum
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 Selector(AbstractProvider[T_co]):
13
- __slots__ = [*AbstractProvider.BASE_SLOTS, "_function", "_providers"]
14
-
15
- def __init__(
16
- self, scope: enum.IntEnum, function: typing.Callable[..., str], **providers: AbstractProvider[T_co]
17
- ) -> None:
18
- super().__init__(scope)
19
- self._check_providers_scope(providers.values())
20
- self._function: typing.Final = function
21
- self._providers: typing.Final = providers
22
-
23
- async def async_resolve(self, container: Container) -> T_co:
24
- container = container.find_container(self.scope)
25
- selected_key = self._function(**container.context)
26
- if selected_key not in self._providers:
27
- msg = f"No provider matches {selected_key}"
28
- raise RuntimeError(msg)
29
-
30
- return await self._providers[selected_key].async_resolve(container)
31
-
32
- def sync_resolve(self, container: Container) -> T_co:
33
- container = container.find_container(self.scope)
34
- selected_key = self._function(**container.context)
35
- if selected_key not in self._providers:
36
- msg = f"No provider matches {selected_key}"
37
- raise RuntimeError(msg)
38
-
39
- return self._providers[selected_key].sync_resolve(container)
@@ -1,24 +0,0 @@
1
- modern_di/__init__.py,sha256=L01VkzSJiV0d0FPrh1DZ-Wy5mUmoG6X-oLz7xYxtehI,194
2
- modern_di/container.py,sha256=x3DBSaEha4ny0Nbs2UmUyLF4xc5hWOyYsV7bfq_LKtw,5236
3
- modern_di/graph.py,sha256=X60wtG3Mqus_5YZNiZlQuXoHODBp7rYl_IHJs7GzSQM,1356
4
- modern_di/provider_state.py,sha256=oU08QnMr0yhIZKkz0Pee8_RnWtETDE9ux4JB83qhwTI,1358
5
- modern_di/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- modern_di/scope.py,sha256=e6Olc-CF89clbYDNGciy-F8EqJt1Mw2703zfuJaEY94,113
7
- modern_di/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- modern_di/helpers/attr_getter_helpers.py,sha256=HUjExWLRAz2h0YX2_h5xKPX9-_K3i-BZTeZb3u-Wq5k,221
9
- modern_di/providers/__init__.py,sha256=U7_gXfy7nUnprfD8RNhaJnPqTBkTAM1BBu025FH5sXc,788
10
- modern_di/providers/abstract.py,sha256=TZryLsWRXsOu0aApmY13iHTNHTl1yU7jHs6sPCJrz5k,5417
11
- modern_di/providers/async_factory.py,sha256=KTsUL5S-Pz0VGPdjle9qhdMs9QD_B39ifjhSqiWgStY,1090
12
- modern_di/providers/container_provider.py,sha256=r5IEQXgKtPwvHvbqkbPnmGyDGGCCjokTtdard9Rvt40,548
13
- modern_di/providers/context_adapter.py,sha256=_b1x3ToQPWT-9KkDioFhw1W8Q1VXZYUnczfYzMTobVA,760
14
- modern_di/providers/dict.py,sha256=nCU9iaqteYHDbILAfhrdnbMgS9_emE4MS7Xn2VoUlPo,858
15
- modern_di/providers/factory.py,sha256=Nn9WZuOHF4r5Go4vOuW_jAjmXs0A2WJCGiGQ1eUFB58,1507
16
- modern_di/providers/injected_factory.py,sha256=KkDww-zUgm41LFt5j8crRzzFuBSkmKvOfptzHWMgVok,1848
17
- modern_di/providers/list.py,sha256=3hx34RfBRmqzh-cT5D6wSTDJPkBGMK_ul4n9gQz-o9M,769
18
- modern_di/providers/object.py,sha256=Sm0mb3Ua7cZ5Ay65fLvl7fnJDSQrQZwvzio3lkkXP2A,825
19
- modern_di/providers/resource.py,sha256=Vp8psY_svOQEJ_38DWhR8ASYpqhP9DoB7bC0jH0IlaU,4605
20
- modern_di/providers/selector.py,sha256=RQbHD2-Liw-TGqu6UELbfCzXYuqxiO_Mg1tLyF3mKQo,1419
21
- modern_di/providers/singleton.py,sha256=_hUpCmbHgLAigdhBiu0zypwWwrIGdB6_oZkGfuLxzNE,2372
22
- modern_di-0.14.0.dist-info/METADATA,sha256=YLbd-0l_WOLXLQ_YrjgWvIuOBZAX_GnGENncQqM4cNI,3382
23
- modern_di-0.14.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
24
- modern_di-0.14.0.dist-info/RECORD,,