modern-di 0.14.0__py3-none-any.whl → 1.0.0a3__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 +97 -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.0a3.dist-info}/METADATA +1 -1
  28. modern_di-1.0.0a3.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.0a3.dist-info}/WHEEL +0 -0
@@ -0,0 +1,37 @@
1
+ import enum
2
+ import typing
3
+
4
+ from modern_di.providers.abstract import AbstractCreatorProvider
5
+ from modern_di.registries.state_registry.state import ProviderState
6
+
7
+
8
+ T_co = typing.TypeVar("T_co", covariant=True)
9
+ P = typing.ParamSpec("P")
10
+
11
+
12
+ class AsyncSingleton(AbstractCreatorProvider[T_co]):
13
+ __slots__ = AbstractCreatorProvider.BASE_SLOTS
14
+ HAS_STATE = True
15
+
16
+ def __init__(
17
+ self,
18
+ scope: enum.IntEnum,
19
+ creator: typing.Callable[P, typing.Awaitable[T_co]],
20
+ *args: P.args,
21
+ **kwargs: P.kwargs,
22
+ ) -> None:
23
+ super().__init__(scope, creator, *args, **kwargs)
24
+ self.is_async = True
25
+
26
+ async def async_resolve(
27
+ self,
28
+ *,
29
+ args: list[typing.Any],
30
+ kwargs: dict[str, typing.Any],
31
+ provider_state: ProviderState[T_co] | None,
32
+ **__: object,
33
+ ) -> T_co:
34
+ assert provider_state
35
+ coroutine: typing.Awaitable[T_co] = self._creator(*args, **kwargs)
36
+ provider_state.instance = await coroutine
37
+ return provider_state.instance
@@ -1,7 +1,6 @@
1
+ import enum
1
2
  import typing
2
3
 
3
- import modern_di
4
- from modern_di import Container
5
4
  from modern_di.providers import AbstractProvider
6
5
 
7
6
 
@@ -9,11 +8,8 @@ T_co = typing.TypeVar("T_co", covariant=True)
9
8
  P = typing.ParamSpec("P")
10
9
 
11
10
 
12
- class ContainerProvider(AbstractProvider[modern_di.Container]):
11
+ class ContainerProvider(AbstractProvider[typing.Any]):
13
12
  __slots__ = AbstractProvider.BASE_SLOTS
14
13
 
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)
14
+ def __init__(self, scope: enum.IntEnum) -> None:
15
+ super().__init__(scope)
@@ -0,0 +1,16 @@
1
+ import enum
2
+ import typing
3
+
4
+ from modern_di.providers import AbstractProvider
5
+
6
+
7
+ T_co = typing.TypeVar("T_co", covariant=True)
8
+ P = typing.ParamSpec("P")
9
+
10
+
11
+ class ContextProvider(AbstractProvider[T_co]):
12
+ __slots__ = [*AbstractProvider.BASE_SLOTS, "context_type", "required"]
13
+
14
+ def __init__(self, scope: enum.IntEnum, context_type: type[T_co]) -> None:
15
+ super().__init__(scope)
16
+ self.context_type = context_type
@@ -1,7 +1,6 @@
1
1
  import enum
2
2
  import typing
3
3
 
4
- from modern_di import Container
5
4
  from modern_di.providers.abstract import AbstractProvider
6
5
 
7
6
 
@@ -9,15 +8,23 @@ T_co = typing.TypeVar("T_co", covariant=True)
9
8
 
10
9
 
11
10
  class Dict(AbstractProvider[dict[str, T_co]]):
12
- __slots__ = [*AbstractProvider.BASE_SLOTS, "_providers"]
13
-
14
- def __init__(self, scope: enum.IntEnum, **providers: AbstractProvider[T_co]) -> None:
15
- super().__init__(scope)
16
- self._check_providers_scope(providers.values())
17
- self._providers: typing.Final = providers
18
-
19
- async def async_resolve(self, container: Container) -> dict[str, T_co]:
20
- return {key: await provider.async_resolve(container) for key, provider in self._providers.items()}
21
-
22
- def sync_resolve(self, container: Container) -> dict[str, T_co]:
23
- return {key: provider.sync_resolve(container) for key, provider in self._providers.items()}
11
+ __slots__ = AbstractProvider.BASE_SLOTS
12
+
13
+ def __init__(self, scope: enum.IntEnum, **kwargs: AbstractProvider[T_co]) -> None:
14
+ super().__init__(scope, kwargs=kwargs)
15
+
16
+ async def async_resolve(
17
+ self,
18
+ *,
19
+ kwargs: dict[str, typing.Any] | None,
20
+ **__: object,
21
+ ) -> dict[str, T_co]:
22
+ return kwargs or {}
23
+
24
+ def sync_resolve(
25
+ self,
26
+ *,
27
+ kwargs: dict[str, typing.Any] | None,
28
+ **__: object,
29
+ ) -> dict[str, T_co]:
30
+ return kwargs or {}
@@ -1,9 +1,7 @@
1
1
  import enum
2
2
  import typing
3
3
 
4
- from modern_di import Container
5
4
  from modern_di.providers.abstract import AbstractCreatorProvider
6
- from modern_di.providers.injected_factory import AsyncInjectedFactory, SyncInjectedFactory
7
5
 
8
6
 
9
7
  T_co = typing.TypeVar("T_co", covariant=True)
@@ -11,7 +9,7 @@ P = typing.ParamSpec("P")
11
9
 
12
10
 
13
11
  class Factory(AbstractCreatorProvider[T_co]):
14
- __slots__ = [*AbstractCreatorProvider.BASE_SLOTS, "_creator"]
12
+ __slots__ = AbstractCreatorProvider.BASE_SLOTS
15
13
 
16
14
  def __init__(
17
15
  self,
@@ -22,24 +20,20 @@ class Factory(AbstractCreatorProvider[T_co]):
22
20
  ) -> None:
23
21
  super().__init__(scope, creator, *args, **kwargs)
24
22
 
25
- @property
26
- def sync_provider(self) -> SyncInjectedFactory[T_co]:
27
- return SyncInjectedFactory(self)
28
-
29
- @property
30
- def async_provider(self) -> AsyncInjectedFactory[T_co]:
31
- return AsyncInjectedFactory(self)
32
-
33
- async def async_resolve(self, container: Container) -> T_co:
34
- container = container.find_container(self.scope)
35
- if (override := container.fetch_override(self.provider_id)) is not None:
36
- return typing.cast(T_co, override)
37
-
38
- return typing.cast(T_co, await self._async_build_creator(container))
39
-
40
- def sync_resolve(self, container: Container) -> T_co:
41
- container = container.find_container(self.scope)
42
- if (override := container.fetch_override(self.provider_id)) is not None:
43
- return typing.cast(T_co, override)
44
-
45
- return typing.cast(T_co, self._sync_build_creator(container))
23
+ async def async_resolve(
24
+ self,
25
+ *,
26
+ args: list[typing.Any],
27
+ kwargs: dict[str, typing.Any],
28
+ **__: object,
29
+ ) -> T_co:
30
+ return typing.cast(T_co, self._creator(*args, **kwargs))
31
+
32
+ def sync_resolve(
33
+ self,
34
+ *,
35
+ args: list[typing.Any],
36
+ kwargs: dict[str, typing.Any],
37
+ **__: object,
38
+ ) -> T_co:
39
+ return typing.cast(T_co, self._creator(*args, **kwargs))
@@ -1,7 +1,6 @@
1
1
  import enum
2
2
  import typing
3
3
 
4
- from modern_di import Container
5
4
  from modern_di.providers.abstract import AbstractProvider
6
5
 
7
6
 
@@ -9,15 +8,23 @@ T_co = typing.TypeVar("T_co", covariant=True)
9
8
 
10
9
 
11
10
  class List(AbstractProvider[list[T_co]]):
12
- __slots__ = [*AbstractProvider.BASE_SLOTS, "_providers"]
13
-
14
- def __init__(self, scope: enum.IntEnum, *providers: AbstractProvider[T_co]) -> None:
15
- super().__init__(scope)
16
- self._check_providers_scope(providers)
17
- self._providers: typing.Final = providers
18
-
19
- async def async_resolve(self, container: Container) -> list[T_co]:
20
- return [await x.async_resolve(container) for x in self._providers]
21
-
22
- def sync_resolve(self, container: Container) -> list[T_co]:
23
- return [x.sync_resolve(container) for x in self._providers]
11
+ __slots__ = AbstractProvider.BASE_SLOTS
12
+
13
+ def __init__(self, scope: enum.IntEnum, *args: AbstractProvider[T_co]) -> None:
14
+ super().__init__(scope, args=list(args))
15
+
16
+ async def async_resolve(
17
+ self,
18
+ *,
19
+ args: list[typing.Any] | None,
20
+ **__: object,
21
+ ) -> list[T_co]:
22
+ return args or []
23
+
24
+ def sync_resolve(
25
+ self,
26
+ *,
27
+ args: list[typing.Any] | None,
28
+ **__: object,
29
+ ) -> list[T_co]:
30
+ return args or []
@@ -1,27 +1,22 @@
1
1
  import enum
2
2
  import typing
3
3
 
4
- from modern_di import Container
5
- from modern_di.providers.abstract import AbstractOverrideProvider
4
+ from modern_di.providers.abstract import AbstractProvider
6
5
 
7
6
 
8
7
  T_co = typing.TypeVar("T_co", covariant=True)
9
8
  P = typing.ParamSpec("P")
10
9
 
11
10
 
12
- class Object(AbstractOverrideProvider[T_co]):
13
- __slots__ = [*AbstractOverrideProvider.BASE_SLOTS, "_obj"]
11
+ class Object(AbstractProvider[T_co]):
12
+ __slots__ = [*AbstractProvider.BASE_SLOTS, "_obj"]
14
13
 
15
14
  def __init__(self, scope: enum.IntEnum, obj: T_co) -> None:
16
15
  super().__init__(scope)
17
16
  self._obj: typing.Final = obj
18
17
 
19
- async def async_resolve(self, container: Container) -> T_co:
20
- return self.sync_resolve(container)
21
-
22
- def sync_resolve(self, container: Container) -> T_co:
23
- container = container.find_container(self.scope)
24
- if (override := container.fetch_override(self.provider_id)) is not None:
25
- return typing.cast(T_co, override)
18
+ async def async_resolve(self, *_: object, **__: object) -> T_co:
19
+ return self._obj
26
20
 
21
+ def sync_resolve(self, *_: object, **__: object) -> T_co:
27
22
  return self._obj
@@ -1,10 +1,10 @@
1
- import contextlib # noqa: A005
1
+ import contextlib
2
2
  import enum
3
3
  import inspect
4
4
  import typing
5
5
 
6
- from modern_di import Container
7
6
  from modern_di.providers.abstract import AbstractCreatorProvider
7
+ from modern_di.registries.state_registry.state import ProviderState
8
8
 
9
9
 
10
10
  T_co = typing.TypeVar("T_co", covariant=True)
@@ -12,13 +12,14 @@ P = typing.ParamSpec("P")
12
12
 
13
13
 
14
14
  class Resource(AbstractCreatorProvider[T_co]):
15
- __slots__ = [*AbstractCreatorProvider.BASE_SLOTS, "_creator", "_args", "_kwargs", "_is_async"]
15
+ __slots__ = AbstractCreatorProvider.BASE_SLOTS
16
+ HAS_STATE = True
16
17
 
17
18
  def _is_creator_async(
18
19
  self,
19
20
  _: contextlib.AbstractContextManager[T_co] | contextlib.AbstractAsyncContextManager[T_co],
20
21
  ) -> typing.TypeGuard[contextlib.AbstractAsyncContextManager[T_co]]:
21
- return self._is_async
22
+ return self.is_async
22
23
 
23
24
  def __init__(
24
25
  self,
@@ -35,85 +36,56 @@ class Resource(AbstractCreatorProvider[T_co]):
35
36
  ) -> None:
36
37
  new_creator: typing.Any
37
38
  if inspect.isasyncgenfunction(creator):
38
- self._is_async = True
39
+ is_async = True
39
40
  new_creator = contextlib.asynccontextmanager(creator)
40
41
  elif inspect.isgeneratorfunction(creator):
41
- self._is_async = False
42
+ is_async = False
42
43
  new_creator = contextlib.contextmanager(creator)
43
44
  elif isinstance(creator, type) and issubclass(creator, typing.AsyncContextManager):
44
- self._is_async = True
45
+ is_async = True
45
46
  new_creator = creator
46
47
  elif isinstance(creator, type) and issubclass(creator, typing.ContextManager):
47
- self._is_async = False
48
+ is_async = False
48
49
  new_creator = creator
49
50
  else:
50
51
  msg = "Unsupported resource type"
51
52
  raise TypeError(msg)
52
53
 
53
54
  super().__init__(scope, new_creator, *args, **kwargs)
55
+ self.is_async = is_async
54
56
 
55
- async def async_resolve(self, container: Container) -> T_co:
56
- container = container.find_container(self.scope)
57
- if (override := container.fetch_override(self.provider_id)) is not None:
58
- return typing.cast(T_co, override)
59
-
60
- provider_state = container.fetch_provider_state(
61
- self.provider_id, is_async_resource=self._is_async, use_asyncio_lock=True
62
- )
63
- if provider_state.instance is not None:
64
- return typing.cast(T_co, provider_state.instance)
65
-
66
- if provider_state.asyncio_lock:
67
- await provider_state.asyncio_lock.acquire()
68
-
69
- try:
70
- if provider_state.instance is not None:
71
- return typing.cast(T_co, provider_state.instance)
72
-
73
- _intermediate_ = await self._async_build_creator(container)
74
-
75
- if self._is_creator_async(self._creator): # type: ignore[arg-type]
76
- provider_state.context_stack = contextlib.AsyncExitStack()
77
- provider_state.instance = await provider_state.context_stack.enter_async_context(_intermediate_)
78
- else:
79
- provider_state.context_stack = contextlib.ExitStack()
80
- provider_state.instance = provider_state.context_stack.enter_context(_intermediate_)
81
- finally:
82
- if provider_state.asyncio_lock:
83
- provider_state.asyncio_lock.release()
57
+ async def async_resolve(
58
+ self,
59
+ *,
60
+ args: list[typing.Any],
61
+ kwargs: dict[str, typing.Any],
62
+ provider_state: ProviderState[T_co] | None,
63
+ **_: object,
64
+ ) -> T_co:
65
+ assert provider_state
66
+ _intermediate_ = self._creator(*args, **kwargs)
67
+ if self._is_creator_async(self._creator): # type: ignore[arg-type]
68
+ provider_state.context_stack = contextlib.AsyncExitStack()
69
+ provider_state.instance = await provider_state.context_stack.enter_async_context(_intermediate_)
70
+ else:
71
+ provider_state.context_stack = contextlib.ExitStack()
72
+ provider_state.instance = provider_state.context_stack.enter_context(_intermediate_)
84
73
 
85
74
  return typing.cast(T_co, provider_state.instance)
86
75
 
87
- def sync_resolve(self, container: Container) -> T_co:
88
- container = container.find_container(self.scope)
89
- if (override := container.fetch_override(self.provider_id)) is not None:
90
- return typing.cast(T_co, override)
91
-
92
- provider_state = container.fetch_provider_state(
93
- self.provider_id, is_async_resource=self._is_async, use_threading_lock=True
76
+ def sync_resolve(
77
+ self,
78
+ *,
79
+ args: list[typing.Any],
80
+ kwargs: dict[str, typing.Any],
81
+ provider_state: ProviderState[T_co] | None,
82
+ **_: object,
83
+ ) -> T_co:
84
+ assert provider_state
85
+ _intermediate_ = self._creator(*args, **kwargs)
86
+ provider_state.context_stack = contextlib.ExitStack()
87
+ provider_state.instance = provider_state.context_stack.enter_context(
88
+ typing.cast(contextlib.AbstractContextManager[typing.Any], _intermediate_)
94
89
  )
95
- if provider_state.instance is not None:
96
- return typing.cast(T_co, provider_state.instance)
97
-
98
- if self._is_async:
99
- msg = "Async resource cannot be resolved synchronously"
100
- raise RuntimeError(msg)
101
-
102
- if provider_state.threading_lock:
103
- provider_state.threading_lock.acquire()
104
-
105
- try:
106
- if provider_state.instance is not None:
107
- return typing.cast(T_co, provider_state.instance)
108
-
109
- _intermediate_ = self._sync_build_creator(container)
110
-
111
- provider_state.context_stack = contextlib.ExitStack()
112
- provider_state.instance = provider_state.context_stack.enter_context(
113
- typing.cast(contextlib.AbstractContextManager[typing.Any], _intermediate_)
114
- )
115
- finally:
116
- if provider_state.threading_lock:
117
- provider_state.threading_lock.release()
118
90
 
119
91
  return typing.cast(T_co, provider_state.instance)
@@ -1,8 +1,8 @@
1
1
  import enum
2
2
  import typing
3
3
 
4
- from modern_di import Container
5
4
  from modern_di.providers.abstract import AbstractCreatorProvider
5
+ from modern_di.registries.state_registry.state import ProviderState
6
6
 
7
7
 
8
8
  T_co = typing.TypeVar("T_co", covariant=True)
@@ -10,7 +10,8 @@ P = typing.ParamSpec("P")
10
10
 
11
11
 
12
12
  class Singleton(AbstractCreatorProvider[T_co]):
13
- __slots__ = [*AbstractCreatorProvider.BASE_SLOTS, "_creator"]
13
+ __slots__ = AbstractCreatorProvider.BASE_SLOTS
14
+ HAS_STATE = True
14
15
 
15
16
  def __init__(
16
17
  self,
@@ -21,47 +22,26 @@ class Singleton(AbstractCreatorProvider[T_co]):
21
22
  ) -> None:
22
23
  super().__init__(scope, creator, *args, **kwargs)
23
24
 
24
- async def async_resolve(self, container: Container) -> T_co:
25
- container = container.find_container(self.scope)
26
- if (override := container.fetch_override(self.provider_id)) is not None:
27
- return typing.cast(T_co, override)
28
-
29
- provider_state = container.fetch_provider_state(self.provider_id, use_asyncio_lock=True)
30
- if provider_state.instance is not None:
31
- return typing.cast(T_co, provider_state.instance)
32
-
33
- assert provider_state.asyncio_lock
34
- await provider_state.asyncio_lock.acquire()
35
-
36
- try:
37
- if provider_state.instance is not None:
38
- return typing.cast(T_co, provider_state.instance)
39
-
40
- provider_state.instance = typing.cast(T_co, await self._async_build_creator(container))
41
- finally:
42
- provider_state.asyncio_lock.release()
43
-
25
+ async def async_resolve(
26
+ self,
27
+ *,
28
+ args: list[typing.Any],
29
+ kwargs: dict[str, typing.Any],
30
+ provider_state: ProviderState[T_co] | None,
31
+ **__: object,
32
+ ) -> T_co:
33
+ assert provider_state
34
+ provider_state.instance = self._creator(*args, **kwargs)
44
35
  return provider_state.instance
45
36
 
46
- def sync_resolve(self, container: Container) -> T_co:
47
- container = container.find_container(self.scope)
48
- if (override := container.fetch_override(self.provider_id)) is not None:
49
- return typing.cast(T_co, override)
50
-
51
- provider_state = container.fetch_provider_state(self.provider_id, use_threading_lock=True)
52
- if provider_state.instance is not None:
53
- return typing.cast(T_co, provider_state.instance)
54
-
55
- if provider_state.threading_lock:
56
- provider_state.threading_lock.acquire()
57
-
58
- try:
59
- if provider_state.instance is not None:
60
- return typing.cast(T_co, provider_state.instance)
61
-
62
- provider_state.instance = self._sync_build_creator(container)
63
- finally:
64
- if provider_state.threading_lock:
65
- provider_state.threading_lock.release()
66
-
67
- return typing.cast(T_co, provider_state.instance)
37
+ def sync_resolve(
38
+ self,
39
+ *,
40
+ args: list[typing.Any],
41
+ kwargs: dict[str, typing.Any],
42
+ provider_state: ProviderState[T_co] | None,
43
+ **__: object,
44
+ ) -> T_co:
45
+ assert provider_state
46
+ provider_state.instance = self._creator(*args, **kwargs)
47
+ return provider_state.instance
File without changes
@@ -0,0 +1,16 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+
5
+ T_co = typing.TypeVar("T_co", covariant=True)
6
+
7
+
8
+ @dataclasses.dataclass(slots=True, frozen=True)
9
+ class ContextRegistry:
10
+ context: dict[type[typing.Any], typing.Any]
11
+
12
+ def find_context(self, context_type: type[T_co]) -> T_co | None:
13
+ if context_type and (context := self.context.get(context_type)):
14
+ return typing.cast(T_co, context)
15
+
16
+ return None
@@ -0,0 +1,22 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+
5
+ T_co = typing.TypeVar("T_co", covariant=True)
6
+
7
+
8
+ @dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
9
+ class OverridesRegistry:
10
+ overrides: dict[str, typing.Any] = dataclasses.field(init=False, default_factory=dict)
11
+
12
+ def override(self, provider_id: str, override_object: object) -> None:
13
+ self.overrides[provider_id] = override_object
14
+
15
+ def reset_override(self, provider_id: str | None = None) -> None:
16
+ if provider_id is None:
17
+ self.overrides.clear()
18
+ else:
19
+ self.overrides.pop(provider_id, None)
20
+
21
+ def fetch_override(self, provider_id: str) -> object | None:
22
+ return self.overrides.get(provider_id)
@@ -0,0 +1,38 @@
1
+ import dataclasses
2
+ import typing
3
+ import warnings
4
+
5
+ from modern_di.providers.abstract import AbstractProvider
6
+
7
+
8
+ T_co = typing.TypeVar("T_co", covariant=True)
9
+
10
+
11
+ @dataclasses.dataclass(slots=True, frozen=True)
12
+ class ProvidersRegistry:
13
+ providers_by_name: dict[str, AbstractProvider[typing.Any]] = dataclasses.field(init=False, default_factory=dict)
14
+ providers_by_type: dict[type, AbstractProvider[typing.Any]] = dataclasses.field(init=False, default_factory=dict)
15
+
16
+ def find_provider(
17
+ self, dependency_name: str | None = None, dependency_type: type[T_co] | None = None
18
+ ) -> AbstractProvider[T_co] | None:
19
+ if dependency_name and (provider := self.providers_by_name.get(dependency_name)):
20
+ return provider
21
+
22
+ if dependency_type and (provider := self.providers_by_type.get(dependency_type)):
23
+ return provider
24
+
25
+ return None
26
+
27
+ def add_providers(self, **kwargs: AbstractProvider[typing.Any]) -> None:
28
+ if duplicates_by_name := set(self.providers_by_name.keys()) & set(kwargs.keys()):
29
+ warnings.warn(f"Duplicated by name providers {duplicates_by_name}", RuntimeWarning, stacklevel=2)
30
+
31
+ self.providers_by_name.update(kwargs)
32
+
33
+ if duplicates_by_type := set(self.providers_by_type.keys()) & {
34
+ x.bound_type for x in kwargs.values() if x.bound_type
35
+ }:
36
+ warnings.warn(f"Duplicated by type providers {duplicates_by_type}", RuntimeWarning, stacklevel=2)
37
+
38
+ self.providers_by_type.update({x.bound_type: x for x in kwargs.values() if x.bound_type})
File without changes
@@ -1,6 +1,4 @@
1
- import asyncio
2
1
  import contextlib
3
- import threading
4
2
  import typing
5
3
 
6
4
 
@@ -8,13 +6,11 @@ T_co = typing.TypeVar("T_co", covariant=True)
8
6
 
9
7
 
10
8
  class ProviderState(typing.Generic[T_co]):
11
- __slots__ = "asyncio_lock", "context_stack", "instance", "threading_lock"
9
+ __slots__ = "context_stack", "instance"
12
10
 
13
- def __init__(self, use_asyncio_lock: bool, use_threading_lock: bool) -> None:
11
+ def __init__(self) -> None:
14
12
  self.context_stack: contextlib.AsyncExitStack | contextlib.ExitStack | None = None
15
13
  self.instance: T_co | None = None
16
- self.asyncio_lock: typing.Final = asyncio.Lock() if use_asyncio_lock else None
17
- self.threading_lock: typing.Final = threading.Lock() if use_threading_lock else None
18
14
 
19
15
  async def async_tear_down(self) -> None:
20
16
  if self.context_stack is None:
@@ -0,0 +1,30 @@
1
+ import dataclasses
2
+ import typing
3
+
4
+ from modern_di.providers import AbstractProvider
5
+ from modern_di.registries.state_registry.state import ProviderState
6
+
7
+
8
+ T_co = typing.TypeVar("T_co", covariant=True)
9
+
10
+
11
+ @dataclasses.dataclass(kw_only=True, slots=True, frozen=True)
12
+ class StateRegistry:
13
+ states: dict[str, ProviderState[typing.Any]] = dataclasses.field(init=False, default_factory=dict)
14
+
15
+ def fetch_provider_state(self, provider: AbstractProvider[T_co]) -> ProviderState[T_co] | None:
16
+ if not provider.HAS_STATE:
17
+ return None
18
+
19
+ if provider_state := self.states.get(provider.provider_id):
20
+ return provider_state
21
+
22
+ return self.states.setdefault(provider.provider_id, ProviderState())
23
+
24
+ async def async_tear_down(self) -> None:
25
+ for provider_state in reversed(self.states.values()):
26
+ await provider_state.async_tear_down()
27
+
28
+ def sync_tear_down(self) -> None:
29
+ for provider_state in reversed(self.states.values()):
30
+ provider_state.sync_tear_down()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modern-di
3
- Version: 0.14.0
3
+ Version: 1.0.0a3
4
4
  Summary: Dependency Injection framework with IOC-container and scopes
5
5
  Project-URL: repository, https://github.com/modern-python/modern-di
6
6
  Project-URL: docs, https://modern-di.readthedocs.io