python-injection 0.8.5__tar.gz → 0.9.1__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 python-injection might be problematic. Click here for more details.

Files changed (23) hide show
  1. {python_injection-0.8.5 → python_injection-0.9.1}/PKG-INFO +3 -5
  2. {python_injection-0.8.5 → python_injection-0.9.1}/documentation/basic-usage.md +1 -1
  3. python_injection-0.9.1/injection/__init__.py +29 -0
  4. {python_injection-0.8.5 → python_injection-0.9.1}/injection/__init__.pyi +65 -46
  5. {python_injection-0.8.5 → python_injection-0.9.1}/injection/common/event.py +3 -3
  6. {python_injection-0.8.5 → python_injection-0.9.1}/injection/common/invertible.py +6 -8
  7. {python_injection-0.8.5 → python_injection-0.9.1}/injection/common/lazy.py +9 -14
  8. {python_injection-0.8.5 → python_injection-0.9.1}/injection/common/tools/threading.py +4 -4
  9. python_injection-0.9.1/injection/common/tools/type.py +75 -0
  10. {python_injection-0.8.5 → python_injection-0.9.1}/injection/core/module.py +150 -110
  11. {python_injection-0.8.5 → python_injection-0.9.1}/injection/exceptions.py +4 -6
  12. {python_injection-0.8.5 → python_injection-0.9.1}/injection/integrations/blacksheep.py +5 -7
  13. python_injection-0.9.1/injection/testing/__init__.py +38 -0
  14. python_injection-0.9.1/injection/testing/__init__.pyi +22 -0
  15. {python_injection-0.8.5 → python_injection-0.9.1}/pyproject.toml +4 -3
  16. python_injection-0.8.5/injection/__init__.py +0 -28
  17. python_injection-0.8.5/injection/common/tools/type.py +0 -48
  18. {python_injection-0.8.5 → python_injection-0.9.1}/injection/common/__init__.py +0 -0
  19. {python_injection-0.8.5 → python_injection-0.9.1}/injection/common/tools/__init__.py +0 -0
  20. {python_injection-0.8.5 → python_injection-0.9.1}/injection/core/__init__.py +0 -0
  21. {python_injection-0.8.5 → python_injection-0.9.1}/injection/integrations/__init__.py +0 -0
  22. {python_injection-0.8.5 → python_injection-0.9.1}/injection/py.typed +0 -0
  23. {python_injection-0.8.5 → python_injection-0.9.1}/injection/utils.py +0 -0
@@ -1,23 +1,21 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.8.5
3
+ Version: 0.9.1
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
7
7
  Keywords: dependencies,inject,injection
8
8
  Author: remimd
9
- Requires-Python: >=3.10,<4
9
+ Requires-Python: >=3.12,<4
10
10
  Classifier: License :: OSI Approved :: MIT License
11
11
  Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
12
  Classifier: Programming Language :: Python :: 3.12
15
13
  Project-URL: Repository, https://github.com/100nm/python-injection
16
14
  Description-Content-Type: text/markdown
17
15
 
18
16
  # Basic usage
19
17
 
20
- ## Create an injectable
18
+ ## Register an injectable
21
19
 
22
20
  > **Note**: If the class needs dependencies, these will be resolved when the instance is retrieved.
23
21
 
@@ -1,6 +1,6 @@
1
1
  # Basic usage
2
2
 
3
- ## Create an injectable
3
+ ## Register an injectable
4
4
 
5
5
  > **Note**: If the class needs dependencies, these will be resolved when the instance is retrieved.
6
6
 
@@ -0,0 +1,29 @@
1
+ from .core import Injectable, Module
2
+ from .core import Mode as InjectableMode
3
+ from .core import Priority as ModulePriority
4
+
5
+ __all__ = (
6
+ "Injectable",
7
+ "InjectableMode",
8
+ "Module",
9
+ "ModulePriority",
10
+ "get_instance",
11
+ "get_lazy_instance",
12
+ "inject",
13
+ "injectable",
14
+ "set_constant",
15
+ "should_be_injectable",
16
+ "singleton",
17
+ )
18
+
19
+ _module = Module.default()
20
+
21
+ get_instance = _module.get_instance
22
+ get_lazy_instance = _module.get_lazy_instance
23
+ inject = _module.inject
24
+ injectable = _module.injectable
25
+ set_constant = _module.set_constant
26
+ should_be_injectable = _module.should_be_injectable
27
+ singleton = _module.singleton
28
+
29
+ del _module
@@ -1,33 +1,34 @@
1
1
  from abc import abstractmethod
2
- from collections.abc import Callable, Iterable
2
+ from collections.abc import Callable
3
3
  from contextlib import ContextDecorator
4
- from enum import Enum
4
+ from enum import StrEnum
5
5
  from types import UnionType
6
6
  from typing import (
7
7
  Any,
8
8
  ContextManager,
9
- Final,
10
- Literal,
11
9
  Protocol,
12
- TypeVar,
10
+ Self,
13
11
  final,
14
12
  runtime_checkable,
15
13
  )
16
14
 
17
15
  from .common.invertible import Invertible
16
+ from .common.tools.type import TypeInfo
17
+ from .core import InjectableFactory
18
+ from .core import ModeStr as InjectableModeStr
19
+ from .core import PriorityStr as ModulePriorityStr
18
20
 
19
- _T = TypeVar("_T")
20
- _T_co = TypeVar("_T_co", covariant=True)
21
+ _module: Module = ...
21
22
 
22
- default_module: Final[Module] = ...
23
+ get_instance = _module.get_instance
24
+ get_lazy_instance = _module.get_lazy_instance
25
+ inject = _module.inject
26
+ injectable = _module.injectable
27
+ set_constant = _module.set_constant
28
+ should_be_injectable = _module.should_be_injectable
29
+ singleton = _module.singleton
23
30
 
24
- get_instance = default_module.get_instance
25
- get_lazy_instance = default_module.get_lazy_instance
26
- inject = default_module.inject
27
- injectable = default_module.injectable
28
- set_constant = default_module.set_constant
29
- should_be_injectable = default_module.should_be_injectable
30
- singleton = default_module.singleton
31
+ del _module
31
32
 
32
33
  @final
33
34
  class Module:
@@ -41,6 +42,8 @@ class Module:
41
42
 
42
43
  def __init__(self, name: str = ...): ...
43
44
  def __contains__(self, cls: type | UnionType, /) -> bool: ...
45
+ @property
46
+ def is_locked(self) -> bool: ...
44
47
  def inject(self, wrapped: Callable[..., Any] = ..., /):
45
48
  """
46
49
  Decorator applicable to a class or function. Inject function dependencies using
@@ -48,15 +51,15 @@ class Module:
48
51
  will be those of the `__init__` method.
49
52
  """
50
53
 
51
- def injectable(
54
+ def injectable[T](
52
55
  self,
53
- wrapped: Callable[..., Any] = ...,
56
+ wrapped: Callable[..., T] = ...,
54
57
  /,
55
58
  *,
56
- cls: type[Injectable] = ...,
59
+ cls: InjectableFactory[T] = ...,
57
60
  inject: bool = ...,
58
- on: type | Iterable[type] | UnionType = ...,
59
- mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
61
+ on: TypeInfo[T] = ...,
62
+ mode: InjectableMode | InjectableModeStr = ...,
60
63
  ):
61
64
  """
62
65
  Decorator applicable to a class or function. It is used to indicate how the
@@ -64,14 +67,14 @@ class Module:
64
67
  injected each time.
65
68
  """
66
69
 
67
- def singleton(
70
+ def singleton[T](
68
71
  self,
69
- wrapped: Callable[..., Any] = ...,
72
+ wrapped: Callable[..., T] = ...,
70
73
  /,
71
74
  *,
72
75
  inject: bool = ...,
73
- on: type | Iterable[type] | UnionType = ...,
74
- mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
76
+ on: TypeInfo[T] = ...,
77
+ mode: InjectableMode | InjectableModeStr = ...,
75
78
  ):
76
79
  """
77
80
  Decorator applicable to a class or function. It is used to indicate how the
@@ -86,37 +89,37 @@ class Module:
86
89
  registered.
87
90
  """
88
91
 
89
- def set_constant(
92
+ def set_constant[T](
90
93
  self,
91
- instance: _T,
92
- on: type | Iterable[type] | UnionType = ...,
94
+ instance: T,
95
+ on: TypeInfo[T] = ...,
93
96
  *,
94
- mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
95
- ) -> _T:
97
+ mode: InjectableMode | InjectableModeStr = ...,
98
+ ) -> T:
96
99
  """
97
100
  Function for registering a specific instance to be injected. This is useful for
98
101
  registering global variables. The difference with the singleton decorator is
99
102
  that no dependencies are resolved, so the module doesn't need to be locked.
100
103
  """
101
104
 
102
- def resolve(self, cls: type[_T]) -> _T:
105
+ def resolve[T](self, cls: type[T]) -> T:
103
106
  """
104
107
  Function used to retrieve an instance associated with the type passed in
105
108
  parameter or an exception will be raised.
106
109
  """
107
110
 
108
- def get_instance(self, cls: type[_T]) -> _T | None:
111
+ def get_instance[T](self, cls: type[T]) -> T | None:
109
112
  """
110
113
  Function used to retrieve an instance associated with the type passed in
111
114
  parameter or return `None`.
112
115
  """
113
116
 
114
- def get_lazy_instance(
117
+ def get_lazy_instance[T](
115
118
  self,
116
- cls: type[_T],
119
+ cls: type[T],
117
120
  *,
118
121
  cache: bool = ...,
119
- ) -> Invertible[_T | None]:
122
+ ) -> Invertible[T | None]:
120
123
  """
121
124
  Function used to retrieve an instance associated with the type passed in
122
125
  parameter or `None`. Return a `Invertible` object. To access the instance
@@ -126,19 +129,24 @@ class Module:
126
129
  Example: instance = ~lazy_instance
127
130
  """
128
131
 
132
+ def init_modules(self, *modules: Module) -> Self:
133
+ """
134
+ Function to clean modules in use and to use those passed as parameters.
135
+ """
136
+
129
137
  def use(
130
138
  self,
131
139
  module: Module,
132
140
  *,
133
- priority: ModulePriority | Literal["low", "high"] = ...,
134
- ):
141
+ priority: ModulePriority | ModulePriorityStr = ...,
142
+ ) -> Self:
135
143
  """
136
144
  Function for using another module. Using another module replaces the module's
137
145
  dependencies with those of the module used. If the dependency is not found, it
138
146
  will be searched for in the module's dependency container.
139
147
  """
140
148
 
141
- def stop_using(self, module: Module):
149
+ def stop_using(self, module: Module) -> Self:
142
150
  """
143
151
  Function to remove a module in use.
144
152
  """
@@ -147,7 +155,7 @@ class Module:
147
155
  self,
148
156
  module: Module,
149
157
  *,
150
- priority: ModulePriority | Literal["low", "high"] = ...,
158
+ priority: ModulePriority | ModulePriorityStr = ...,
151
159
  ) -> ContextManager | ContextDecorator:
152
160
  """
153
161
  Context manager or decorator for temporary use of a module.
@@ -156,8 +164,8 @@ class Module:
156
164
  def change_priority(
157
165
  self,
158
166
  module: Module,
159
- priority: ModulePriority | Literal["low", "high"],
160
- ):
167
+ priority: ModulePriority | ModulePriorityStr,
168
+ ) -> Self:
161
169
  """
162
170
  Function for changing the priority of a module in use.
163
171
  There are two priority values:
@@ -166,27 +174,38 @@ class Module:
166
174
  * **HIGH**: The module concerned becomes the most important of the modules used.
167
175
  """
168
176
 
169
- def unlock(self):
177
+ def unlock(self) -> Self:
170
178
  """
171
179
  Function to unlock the module by deleting cached instances of singletons.
172
180
  """
173
181
 
182
+ @classmethod
183
+ def from_name(cls, name: str) -> Self:
184
+ """
185
+ Class method for getting or creating a module by name.
186
+ """
187
+
188
+ @classmethod
189
+ def default(cls) -> Self:
190
+ """
191
+ Class method for getting the default module.
192
+ """
193
+
174
194
  @final
175
- class ModulePriority(str, Enum):
195
+ class ModulePriority(StrEnum):
176
196
  LOW = ...
177
197
  HIGH = ...
178
198
 
179
199
  @runtime_checkable
180
- class Injectable(Protocol[_T_co]):
181
- def __init__(self, factory: Callable[[], _T_co] = ..., /): ...
200
+ class Injectable[T](Protocol):
182
201
  @property
183
202
  def is_locked(self) -> bool: ...
184
203
  def unlock(self): ...
185
204
  @abstractmethod
186
- def get_instance(self) -> _T_co: ...
205
+ def get_instance(self) -> T: ...
187
206
 
188
207
  @final
189
- class InjectableMode(str, Enum):
208
+ class InjectableMode(StrEnum):
190
209
  FALLBACK = ...
191
210
  NORMAL = ...
192
211
  OVERRIDE = ...
@@ -1,7 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from contextlib import ExitStack, contextmanager, suppress
3
3
  from dataclasses import dataclass, field
4
- from typing import ContextManager
4
+ from typing import ContextManager, Self
5
5
  from weakref import WeakSet
6
6
 
7
7
  __all__ = ("Event", "EventChannel", "EventListener")
@@ -36,11 +36,11 @@ class EventChannel:
36
36
 
37
37
  yield
38
38
 
39
- def add_listener(self, listener: EventListener):
39
+ def add_listener(self, listener: EventListener) -> Self:
40
40
  self.__listeners.add(listener)
41
41
  return self
42
42
 
43
- def remove_listener(self, listener: EventListener):
43
+ def remove_listener(self, listener: EventListener) -> Self:
44
44
  with suppress(KeyError):
45
45
  self.__listeners.remove(listener)
46
46
 
@@ -1,23 +1,21 @@
1
1
  from abc import abstractmethod
2
2
  from collections.abc import Callable
3
3
  from dataclasses import dataclass
4
- from typing import Protocol, TypeVar, runtime_checkable
4
+ from typing import Protocol, runtime_checkable
5
5
 
6
6
  __all__ = ("Invertible", "SimpleInvertible")
7
7
 
8
- _T_co = TypeVar("_T_co", covariant=True)
9
-
10
8
 
11
9
  @runtime_checkable
12
- class Invertible(Protocol[_T_co]):
10
+ class Invertible[T](Protocol):
13
11
  @abstractmethod
14
- def __invert__(self) -> _T_co:
12
+ def __invert__(self) -> T:
15
13
  raise NotImplementedError
16
14
 
17
15
 
18
16
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
19
- class SimpleInvertible(Invertible[_T_co]):
20
- callable: Callable[[], _T_co]
17
+ class SimpleInvertible[T](Invertible[T]):
18
+ callable: Callable[[], T]
21
19
 
22
- def __invert__(self) -> _T_co:
20
+ def __invert__(self) -> T:
23
21
  return self.callable()
@@ -1,31 +1,26 @@
1
1
  from collections.abc import Callable, Iterator, Mapping
2
2
  from types import MappingProxyType
3
- from typing import TypeVar
4
3
 
5
4
  from injection.common.invertible import Invertible
6
5
 
7
6
  __all__ = ("Lazy", "LazyMapping")
8
7
 
9
- _T = TypeVar("_T")
10
- _K = TypeVar("_K")
11
- _V = TypeVar("_V")
12
8
 
13
-
14
- class Lazy(Invertible[_T]):
9
+ class Lazy[T](Invertible[T]):
15
10
  __slots__ = ("__cache", "__is_set")
16
11
 
17
- def __init__(self, factory: Callable[[], _T]):
12
+ def __init__(self, factory: Callable[[], T]):
18
13
  self.__setup_cache(factory)
19
14
 
20
- def __invert__(self) -> _T:
15
+ def __invert__(self) -> T:
21
16
  return next(self.__cache)
22
17
 
23
18
  @property
24
19
  def is_set(self) -> bool:
25
20
  return self.__is_set
26
21
 
27
- def __setup_cache(self, factory: Callable[[], _T]):
28
- def cache_generator() -> Iterator[_T]:
22
+ def __setup_cache(self, factory: Callable[[], T]):
23
+ def cache_generator() -> Iterator[T]:
29
24
  nonlocal factory
30
25
  cached = factory()
31
26
  self.__is_set = True
@@ -38,16 +33,16 @@ class Lazy(Invertible[_T]):
38
33
  self.__is_set = False
39
34
 
40
35
 
41
- class LazyMapping(Mapping[_K, _V]):
36
+ class LazyMapping[K, V](Mapping[K, V]):
42
37
  __slots__ = ("__lazy",)
43
38
 
44
- def __init__(self, iterator: Iterator[tuple[_K, _V]]):
39
+ def __init__(self, iterator: Iterator[tuple[K, V]]):
45
40
  self.__lazy = Lazy(lambda: MappingProxyType(dict(iterator)))
46
41
 
47
- def __getitem__(self, key: _K, /) -> _V:
42
+ def __getitem__(self, key: K, /) -> V:
48
43
  return (~self.__lazy)[key]
49
44
 
50
- def __iter__(self) -> Iterator[_K]:
45
+ def __iter__(self) -> Iterator[K]:
51
46
  yield from ~self.__lazy
52
47
 
53
48
  def __len__(self) -> int:
@@ -3,10 +3,10 @@ from threading import RLock
3
3
 
4
4
  __all__ = ("synchronized",)
5
5
 
6
- __lock = RLock()
7
-
8
6
 
9
7
  @contextmanager
10
8
  def synchronized():
11
- with __lock:
12
- yield
9
+ lock = RLock()
10
+
11
+ with lock:
12
+ yield lock
@@ -0,0 +1,75 @@
1
+ from collections.abc import Callable, Iterable, Iterator
2
+ from inspect import get_annotations, isfunction
3
+ from types import UnionType
4
+ from typing import (
5
+ Annotated,
6
+ Any,
7
+ NamedTuple,
8
+ Self,
9
+ Union,
10
+ get_args,
11
+ get_origin,
12
+ )
13
+
14
+ __all__ = ("TypeInfo", "TypeReport", "analyze_types", "get_return_types")
15
+
16
+ type TypeInfo[T] = type[T] | Callable[..., T] | Iterable[TypeInfo[T]] | UnionType
17
+
18
+
19
+ class TypeReport[T](NamedTuple):
20
+ origin: type[T]
21
+ args: tuple[Any, ...]
22
+
23
+ @property
24
+ def type(self) -> type[T]:
25
+ origin = self.origin
26
+
27
+ if args := self.args:
28
+ return origin[*args]
29
+
30
+ return origin
31
+
32
+ @property
33
+ def no_args(self) -> Self:
34
+ if self.args:
35
+ return type(self)(self.origin, ())
36
+
37
+ return self
38
+
39
+
40
+ def analyze_types(*types: type | Any) -> Iterator[TypeReport[Any]]:
41
+ for tp in types:
42
+ if tp is None:
43
+ continue
44
+
45
+ origin = get_origin(tp)
46
+
47
+ if origin is Union or isinstance(tp, UnionType):
48
+ inner_types = get_args(tp)
49
+
50
+ elif origin is Annotated:
51
+ inner_types = get_args(tp)[:1]
52
+
53
+ else:
54
+ yield TypeReport(origin or tp, get_args(tp))
55
+ continue
56
+
57
+ yield from analyze_types(*inner_types)
58
+
59
+
60
+ def get_return_types(*args: TypeInfo[Any]) -> Iterator[type | UnionType]:
61
+ for arg in args:
62
+ if isinstance(arg, Iterable) and not isinstance(
63
+ get_origin(arg) or arg,
64
+ type | str,
65
+ ):
66
+ inner_args = arg
67
+
68
+ elif isfunction(arg):
69
+ inner_args = (get_annotations(arg, eval_str=True).get("return"),)
70
+
71
+ else:
72
+ yield arg
73
+ continue
74
+
75
+ yield from get_return_types(*inner_args)