python-injection 0.8.2__py3-none-any.whl → 0.8.3__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.

Potentially problematic release.


This version of python-injection might be problematic. Click here for more details.

injection/__init__.py CHANGED
@@ -1,8 +1,10 @@
1
- from .core import Injectable, Module
1
+ from .core import Injectable, InjectableMode, Module, ModulePriority
2
2
 
3
3
  __all__ = (
4
4
  "Injectable",
5
+ "InjectableMode",
5
6
  "Module",
7
+ "ModulePriority",
6
8
  "default_module",
7
9
  "get_instance",
8
10
  "get_lazy_instance",
injection/__init__.pyi CHANGED
@@ -1,6 +1,7 @@
1
1
  from abc import abstractmethod
2
2
  from collections.abc import Callable, Iterable
3
3
  from contextlib import ContextDecorator
4
+ from enum import Enum
4
5
  from types import UnionType
5
6
  from typing import (
6
7
  Any,
@@ -54,7 +55,7 @@ class Module:
54
55
  cls: type[Injectable] = ...,
55
56
  inject: bool = ...,
56
57
  on: type | Iterable[type] | UnionType = ...,
57
- mode: Literal["fallback", "normal", "override"] = ...,
58
+ mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
58
59
  ):
59
60
  """
60
61
  Decorator applicable to a class or function. It is used to indicate how the
@@ -69,7 +70,7 @@ class Module:
69
70
  *,
70
71
  inject: bool = ...,
71
72
  on: type | Iterable[type] | UnionType = ...,
72
- mode: Literal["fallback", "normal", "override"] = ...,
73
+ mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
73
74
  ):
74
75
  """
75
76
  Decorator applicable to a class or function. It is used to indicate how the
@@ -89,7 +90,7 @@ class Module:
89
90
  instance: _T,
90
91
  on: type | Iterable[type] | UnionType = ...,
91
92
  *,
92
- mode: Literal["fallback", "normal", "override"] = ...,
93
+ mode: InjectableMode | Literal["fallback", "normal", "override"] = ...,
93
94
  ) -> _T:
94
95
  """
95
96
  Function for registering a specific instance to be injected. This is useful for
@@ -119,7 +120,12 @@ class Module:
119
120
  Example: instance = ~lazy_instance
120
121
  """
121
122
 
122
- def use(self, module: Module, *, priority: Literal["low", "high"] = ...):
123
+ def use(
124
+ self,
125
+ module: Module,
126
+ *,
127
+ priority: ModulePriority | Literal["low", "high"] = ...,
128
+ ):
123
129
  """
124
130
  Function for using another module. Using another module replaces the module's
125
131
  dependencies with those of the module used. If the dependency is not found, it
@@ -135,13 +141,17 @@ class Module:
135
141
  self,
136
142
  module: Module,
137
143
  *,
138
- priority: Literal["low", "high"] = ...,
144
+ priority: ModulePriority | Literal["low", "high"] = ...,
139
145
  ) -> ContextManager | ContextDecorator:
140
146
  """
141
147
  Context manager or decorator for temporary use of a module.
142
148
  """
143
149
 
144
- def change_priority(self, module: Module, priority: Literal["low", "high"]):
150
+ def change_priority(
151
+ self,
152
+ module: Module,
153
+ priority: ModulePriority | Literal["low", "high"],
154
+ ):
145
155
  """
146
156
  Function for changing the priority of a module in use.
147
157
  There are two priority values:
@@ -155,6 +165,11 @@ class Module:
155
165
  Function to unlock the module by deleting cached instances of singletons.
156
166
  """
157
167
 
168
+ @final
169
+ class ModulePriority(str, Enum):
170
+ LOW = ...
171
+ HIGH = ...
172
+
158
173
  @runtime_checkable
159
174
  class Injectable(Protocol[_T]):
160
175
  def __init__(self, factory: Callable[[], _T] = ..., /): ...
@@ -163,3 +178,9 @@ class Injectable(Protocol[_T]):
163
178
  def unlock(self): ...
164
179
  @abstractmethod
165
180
  def get_instance(self) -> _T: ...
181
+
182
+ @final
183
+ class InjectableMode(str, Enum):
184
+ FALLBACK = ...
185
+ NORMAL = ...
186
+ OVERRIDE = ...
injection/core/module.py CHANGED
@@ -14,6 +14,7 @@ from collections.abc import (
14
14
  )
15
15
  from contextlib import ContextDecorator, contextmanager, suppress
16
16
  from dataclasses import dataclass, field
17
+ from enum import Enum
17
18
  from functools import partialmethod, singledispatchmethod, update_wrapper
18
19
  from inspect import Signature, isclass
19
20
  from types import MethodType, UnionType
@@ -27,7 +28,6 @@ from typing import (
27
28
  Protocol,
28
29
  TypeVar,
29
30
  cast,
30
- get_args,
31
31
  runtime_checkable,
32
32
  )
33
33
 
@@ -45,7 +45,7 @@ from injection.exceptions import (
45
45
  NoInjectable,
46
46
  )
47
47
 
48
- __all__ = ("Injectable", "Module")
48
+ __all__ = ("Injectable", "InjectableMode", "Module", "ModulePriority")
49
49
 
50
50
  _logger = logging.getLogger(__name__)
51
51
 
@@ -53,15 +53,6 @@ _T = TypeVar("_T")
53
53
  _Types = Iterable[type] | UnionType
54
54
 
55
55
 
56
- """
57
- Literal types
58
- """
59
-
60
-
61
- _Mode = Literal["fallback", "normal", "override"]
62
- _Priority = Literal["low", "high"]
63
-
64
-
65
56
  """
66
57
  Events
67
58
  """
@@ -75,7 +66,7 @@ class ContainerEvent(Event, ABC):
75
66
  @dataclass(frozen=True, slots=True)
76
67
  class ContainerDependenciesUpdated(ContainerEvent):
77
68
  classes: Collection[type]
78
- mode: _Mode
69
+ mode: InjectableMode
79
70
 
80
71
  def __str__(self) -> str:
81
72
  length = len(self.classes)
@@ -113,6 +104,7 @@ class ModuleEventProxy(ModuleEvent):
113
104
  @dataclass(frozen=True, slots=True)
114
105
  class ModuleAdded(ModuleEvent):
115
106
  module_added: Module
107
+ priority: ModulePriority
116
108
 
117
109
  def __str__(self) -> str:
118
110
  return f"`{self.on_module}` now uses `{self.module_added}`."
@@ -129,7 +121,7 @@ class ModuleRemoved(ModuleEvent):
129
121
  @dataclass(frozen=True, slots=True)
130
122
  class ModulePriorityUpdated(ModuleEvent):
131
123
  module_updated: Module
132
- priority: _Priority
124
+ priority: ModulePriority
133
125
 
134
126
  def __str__(self) -> str:
135
127
  return (
@@ -241,20 +233,37 @@ Container
241
233
  """
242
234
 
243
235
 
236
+ class InjectableMode(str, Enum):
237
+ FALLBACK = "fallback"
238
+ NORMAL = "normal"
239
+ OVERRIDE = "override"
240
+
241
+ @property
242
+ def rank(self) -> int:
243
+ return tuple(type(self)).index(self)
244
+
245
+ @classmethod
246
+ def get_default(cls):
247
+ return cls.NORMAL
248
+
249
+
250
+ _Mode = InjectableMode | Literal["fallback", "normal", "override"]
251
+
252
+
244
253
  class Record(NamedTuple):
245
254
  injectable: Injectable
246
- mode: _Mode
255
+ mode: InjectableMode
247
256
 
248
257
 
249
258
  @dataclass(repr=False, frozen=True, slots=True)
250
259
  class Container(Broker):
251
- __data: dict[type, Record] = field(default_factory=dict, init=False)
260
+ __records: dict[type, Record] = field(default_factory=dict, init=False)
252
261
  __channel: EventChannel = field(default_factory=EventChannel, init=False)
253
262
 
254
263
  def __getitem__(self, cls: type[_T] | UnionType, /) -> Injectable[_T]:
255
264
  for cls in get_origins(cls):
256
265
  try:
257
- injectable, _ = self.__data[cls]
266
+ injectable, _ = self.__records[cls]
258
267
  except KeyError:
259
268
  continue
260
269
 
@@ -263,7 +272,7 @@ class Container(Broker):
263
272
  raise NoInjectable(cls)
264
273
 
265
274
  def __contains__(self, cls: type | UnionType, /) -> bool:
266
- return any(cls in self.__data for cls in get_origins(cls))
275
+ return any(cls in self.__records for cls in get_origins(cls))
267
276
 
268
277
  @property
269
278
  def is_locked(self) -> bool:
@@ -271,10 +280,11 @@ class Container(Broker):
271
280
 
272
281
  @property
273
282
  def __injectables(self) -> frozenset[Injectable]:
274
- return frozenset(injectable for injectable, _ in self.__data.values())
283
+ return frozenset(injectable for injectable, _ in self.__records.values())
275
284
 
276
285
  @synchronized()
277
286
  def update(self, classes: Iterable[type], injectable: Injectable, mode: _Mode):
287
+ mode = InjectableMode(mode)
278
288
  records = {
279
289
  cls: Record(injectable, mode)
280
290
  for cls in self.__filter_classes(classes, mode)
@@ -284,7 +294,7 @@ class Container(Broker):
284
294
  event = ContainerDependenciesUpdated(self, records.keys(), mode)
285
295
 
286
296
  with self.notify(event):
287
- self.__data.update(records)
297
+ self.__records.update(records)
288
298
 
289
299
  return self
290
300
 
@@ -302,13 +312,16 @@ class Container(Broker):
302
312
  def notify(self, event: Event) -> ContextManager | ContextDecorator:
303
313
  return self.__channel.dispatch(event)
304
314
 
305
- def __filter_classes(self, classes: Iterable[type], mode: _Mode) -> Iterator[type]:
306
- modes = get_args(_Mode)
307
- rank = modes.index(mode)
315
+ def __filter_classes(
316
+ self,
317
+ classes: Iterable[type],
318
+ mode: InjectableMode,
319
+ ) -> Iterator[type]:
320
+ rank = mode.rank
308
321
 
309
322
  for cls in frozenset(get_origins(*classes)):
310
323
  try:
311
- _, current_mode = self.__data[cls]
324
+ _, current_mode = self.__records[cls]
312
325
 
313
326
  except KeyError:
314
327
  pass
@@ -319,7 +332,7 @@ class Container(Broker):
319
332
  f"An injectable already exists for the class `{format_type(cls)}`."
320
333
  )
321
334
 
322
- elif rank < modes.index(current_mode):
335
+ elif rank < current_mode.rank:
323
336
  continue
324
337
 
325
338
  yield cls
@@ -330,6 +343,18 @@ Module
330
343
  """
331
344
 
332
345
 
346
+ class ModulePriority(str, Enum):
347
+ LOW = "low"
348
+ HIGH = "high"
349
+
350
+ @classmethod
351
+ def get_default(cls):
352
+ return cls.LOW
353
+
354
+
355
+ _Priority = ModulePriority | Literal["low", "high"]
356
+
357
+
333
358
  @dataclass(repr=False, eq=False, frozen=True, slots=True)
334
359
  class Module(EventListener, Broker):
335
360
  name: str = field(default=None)
@@ -376,7 +401,7 @@ class Module(EventListener, Broker):
376
401
  cls: type[Injectable] = NewInjectable,
377
402
  inject: bool = True,
378
403
  on: type | _Types = None,
379
- mode: _Mode = "normal",
404
+ mode: _Mode = InjectableMode.get_default(),
380
405
  ):
381
406
  def decorator(wp):
382
407
  factory = self.inject(wp, return_factory=True) if inject else wp
@@ -394,7 +419,7 @@ class Module(EventListener, Broker):
394
419
  self.update(
395
420
  (wp,),
396
421
  ShouldBeInjectable(wp),
397
- mode="fallback",
422
+ mode=InjectableMode.FALLBACK,
398
423
  )
399
424
  return wp
400
425
 
@@ -405,7 +430,7 @@ class Module(EventListener, Broker):
405
430
  instance: _T,
406
431
  on: type | _Types = None,
407
432
  *,
408
- mode: _Mode = "normal",
433
+ mode: _Mode = InjectableMode.get_default(),
409
434
  ) -> _T:
410
435
  cls = type(instance)
411
436
  self.injectable(
@@ -468,19 +493,25 @@ class Module(EventListener, Broker):
468
493
  self,
469
494
  classes: Iterable[type],
470
495
  injectable: Injectable,
471
- mode: _Mode = "normal",
496
+ mode: _Mode = InjectableMode.get_default(),
472
497
  ):
473
498
  self.__container.update(classes, injectable, mode)
474
499
  return self
475
500
 
476
- def use(self, module: Module, *, priority: _Priority = "low"):
501
+ def use(
502
+ self,
503
+ module: Module,
504
+ *,
505
+ priority: _Priority = ModulePriority.get_default(),
506
+ ):
477
507
  if module is self:
478
508
  raise ModuleError("Module can't be used by itself.")
479
509
 
480
510
  if module in self.__modules:
481
511
  raise ModuleError(f"`{self}` already uses `{module}`.")
482
512
 
483
- event = ModuleAdded(self, module)
513
+ priority = ModulePriority(priority)
514
+ event = ModuleAdded(self, module, priority)
484
515
 
485
516
  with self.notify(event):
486
517
  self.__modules[module] = None
@@ -504,13 +535,14 @@ class Module(EventListener, Broker):
504
535
  self,
505
536
  module: Module,
506
537
  *,
507
- priority: _Priority = "low",
538
+ priority: _Priority = ModulePriority.get_default(),
508
539
  ) -> ContextManager | ContextDecorator:
509
540
  self.use(module, priority=priority)
510
541
  yield
511
542
  self.stop_using(module)
512
543
 
513
544
  def change_priority(self, module: Module, priority: _Priority):
545
+ priority = ModulePriority(priority)
514
546
  event = ModulePriorityUpdated(self, module, priority)
515
547
 
516
548
  with self.notify(event):
@@ -549,8 +581,8 @@ class Module(EventListener, Broker):
549
581
  if self.is_locked:
550
582
  raise ModuleLockError(f"`{self}` is locked.")
551
583
 
552
- def __move_module(self, module: Module, priority: _Priority):
553
- last = priority != "high"
584
+ def __move_module(self, module: Module, priority: ModulePriority):
585
+ last = priority != ModulePriority.HIGH
554
586
 
555
587
  try:
556
588
  self.__modules.move_to_end(module, last=last)
@@ -646,14 +678,7 @@ class InjectedFunction(EventListener):
646
678
  )
647
679
 
648
680
  def __init__(self, wrapped: Callable[..., Any], /):
649
- try:
650
- variables = vars(wrapped)
651
- except TypeError:
652
- pass
653
- else:
654
- self.__update_vars(variables)
655
- del variables
656
-
681
+ self.__update_vars_from(wrapped)
657
682
  update_wrapper(self, wrapped, updated=())
658
683
  self.__dependencies = Dependencies.empty()
659
684
  self.__owner = None
@@ -745,7 +770,15 @@ class InjectedFunction(EventListener):
745
770
  self.__signature__ = inspect.signature(self.wrapped, eval_str=True)
746
771
  return self
747
772
 
748
- def __update_vars(self, variables: Mapping[str, Any], /):
773
+ def __update_vars_from(self, obj: Any):
774
+ try:
775
+ variables = vars(obj)
776
+ except TypeError:
777
+ pass
778
+ else:
779
+ self.__update_vars(variables)
780
+
781
+ def __update_vars(self, variables: Mapping[str, Any]):
749
782
  def is_dunder(var: str) -> bool:
750
783
  return var.startswith("__") and var.endswith("__")
751
784
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.8.2
3
+ Version: 0.8.3
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
@@ -136,7 +136,7 @@ class ConcreteServiceOverload(ConcreteService):
136
136
  ...
137
137
  ```
138
138
 
139
- If a class is registered in a package and you want to override it, there is the `mode` parameter:
139
+ If a class is registered in a package, and you want to override it, there is the `mode` parameter:
140
140
 
141
141
  ```python
142
142
  @injectable
@@ -1,5 +1,5 @@
1
- injection/__init__.py,sha256=QyzDT1tjjIvXQmwFjBJL_veQkEHYrn1OvATiGYFbzHo,609
2
- injection/__init__.pyi,sha256=XarvsM5fn9uMI0vNI4o1hrQAcHFL6Gaz2qSlL5hh4GQ,5395
1
+ injection/__init__.py,sha256=6Z4-h68LLXJ6FlrLqEMeJjw_RG7jyl0b-GQwKC2vIEY,685
2
+ injection/__init__.pyi,sha256=DrMtI3q4JYqPT2nwEoSmY3gVO62TwOUOJToEv9rtPVk,5755
3
3
  injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  injection/common/event.py,sha256=uFoGRnxxkohH53JEStn4tN2Pn79HlgGExh7VkXdBwVQ,1316
5
5
  injection/common/invertible.py,sha256=CyGp57Ik1pSQ2G7bRvnFWkY0kJkZDD5_19OjMYNvQes,558
@@ -9,11 +9,11 @@ injection/common/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
9
9
  injection/common/tools/threading.py,sha256=02mBkIir2kQaXt47ewMJnOvhCldXCmPI2V-BW_l_6V8,271
10
10
  injection/common/tools/type.py,sha256=-zL0dtoVZme71Mscvav7iEWxY2-JltzNTekbWOCPSFo,1276
11
11
  injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
12
- injection/core/module.py,sha256=F6UEi0KEdz9CutM0IzRLYJUCAnCk8XAmHUBPmxHgMjE,19663
12
+ injection/core/module.py,sha256=mO8vHhbbvYTMFEucmpjFbGrJTeBrtlPiERmQDCxBGao,20545
13
13
  injection/exceptions.py,sha256=nE56jW00ZB1T-Z-dvfPczPShs3CwIc7tIvdYlOXlaXA,653
14
14
  injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  injection/integrations/blacksheep.py,sha256=vcLil1IccS7JtXpuVu7s2LqN5Zravfe_7xpAt5cTIU0,723
16
16
  injection/utils.py,sha256=_79aiciimZpuoUTz5lojKySUMMzpkU-e7SotiHIFTI8,676
17
- python_injection-0.8.2.dist-info/METADATA,sha256=CSKZD9lBsSbRQO3XZYFVa6ofP5L6Gi_dsDhVke_mS1U,3671
18
- python_injection-0.8.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
19
- python_injection-0.8.2.dist-info/RECORD,,
17
+ python_injection-0.8.3.dist-info/METADATA,sha256=148AKRb_jDQah38kKBXqLv185AoQeq8NTgg_9JMZDY4,3672
18
+ python_injection-0.8.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
19
+ python_injection-0.8.3.dist-info/RECORD,,