python-injection 0.10.2__tar.gz → 0.10.4__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.
Files changed (22) hide show
  1. {python_injection-0.10.2 → python_injection-0.10.4}/PKG-INFO +1 -1
  2. {python_injection-0.10.2 → python_injection-0.10.4}/injection/__init__.py +3 -5
  3. {python_injection-0.10.2 → python_injection-0.10.4}/injection/__init__.pyi +10 -11
  4. python_injection-0.10.4/injection/_core/__init__.py +53 -0
  5. {python_injection-0.10.2 → python_injection-0.10.4}/injection/_core/common/type.py +27 -26
  6. python_injection-0.10.4/injection/_core/hook.py +96 -0
  7. {python_injection-0.10.2 → python_injection-0.10.4}/injection/_core/module.py +97 -59
  8. {python_injection-0.10.2 → python_injection-0.10.4}/injection/exceptions.py +5 -1
  9. {python_injection-0.10.2 → python_injection-0.10.4}/pyproject.toml +1 -1
  10. python_injection-0.10.2/injection/integrations/__init__.py +0 -0
  11. {python_injection-0.10.2 → python_injection-0.10.4}/README.md +0 -0
  12. {python_injection-0.10.2/injection/_core → python_injection-0.10.4/injection/_core/common}/__init__.py +0 -0
  13. {python_injection-0.10.2 → python_injection-0.10.4}/injection/_core/common/event.py +0 -0
  14. {python_injection-0.10.2 → python_injection-0.10.4}/injection/_core/common/invertible.py +0 -0
  15. {python_injection-0.10.2 → python_injection-0.10.4}/injection/_core/common/lazy.py +0 -0
  16. {python_injection-0.10.2 → python_injection-0.10.4}/injection/_core/common/threading.py +0 -0
  17. {python_injection-0.10.2/injection/_core/common → python_injection-0.10.4/injection/integrations}/__init__.py +0 -0
  18. {python_injection-0.10.2 → python_injection-0.10.4}/injection/integrations/blacksheep.py +0 -0
  19. {python_injection-0.10.2 → python_injection-0.10.4}/injection/py.typed +0 -0
  20. {python_injection-0.10.2 → python_injection-0.10.4}/injection/testing/__init__.py +0 -0
  21. {python_injection-0.10.2 → python_injection-0.10.4}/injection/testing/__init__.pyi +0 -0
  22. {python_injection-0.10.2 → python_injection-0.10.4}/injection/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.10.2
3
+ Version: 0.10.4
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
@@ -1,12 +1,10 @@
1
- from ._core.module import Injectable, Module
2
- from ._core.module import Mode as InjectableMode
3
- from ._core.module import Priority as ModulePriority
1
+ from ._core.module import Injectable, Mode, Module, Priority
4
2
 
5
3
  __all__ = (
6
4
  "Injectable",
7
- "InjectableMode",
5
+ "Mode",
8
6
  "Module",
9
- "ModulePriority",
7
+ "Priority",
10
8
  "constant",
11
9
  "find_instance",
12
10
  "get_instance",
@@ -16,8 +16,7 @@ from ._core.common.invertible import Invertible as _Invertible
16
16
  from ._core.common.type import InputType as _InputType
17
17
  from ._core.common.type import TypeInfo as _TypeInfo
18
18
  from ._core.module import InjectableFactory as _InjectableFactory
19
- from ._core.module import ModeStr as InjectableModeStr
20
- from ._core.module import PriorityStr as ModulePriorityStr
19
+ from ._core.module import ModeStr, PriorityStr
21
20
 
22
21
  _: Module = ...
23
22
 
@@ -68,7 +67,7 @@ class Module:
68
67
  cls: _InjectableFactory[T] = ...,
69
68
  inject: bool = ...,
70
69
  on: _TypeInfo[T] = ...,
71
- mode: InjectableMode | InjectableModeStr = ...,
70
+ mode: Mode | ModeStr = ...,
72
71
  ):
73
72
  """
74
73
  Decorator applicable to a class or function. It is used to indicate how the
@@ -83,7 +82,7 @@ class Module:
83
82
  *,
84
83
  inject: bool = ...,
85
84
  on: _TypeInfo[T] = ...,
86
- mode: InjectableMode | InjectableModeStr = ...,
85
+ mode: Mode | ModeStr = ...,
87
86
  ):
88
87
  """
89
88
  Decorator applicable to a class or function. It is used to indicate how the
@@ -104,7 +103,7 @@ class Module:
104
103
  /,
105
104
  *,
106
105
  on: _TypeInfo[T] = ...,
107
- mode: InjectableMode | InjectableModeStr = ...,
106
+ mode: Mode | ModeStr = ...,
108
107
  ):
109
108
  """
110
109
  Decorator applicable to a class or function. It is used to indicate how the
@@ -118,7 +117,7 @@ class Module:
118
117
  on: _TypeInfo[T] = ...,
119
118
  *,
120
119
  alias: bool = ...,
121
- mode: InjectableMode | InjectableModeStr = ...,
120
+ mode: Mode | ModeStr = ...,
122
121
  ) -> Self:
123
122
  """
124
123
  Function for registering a specific instance to be injected. This is useful for
@@ -162,7 +161,7 @@ class Module:
162
161
  self,
163
162
  module: Module,
164
163
  *,
165
- priority: ModulePriority | ModulePriorityStr = ...,
164
+ priority: Priority | PriorityStr = ...,
166
165
  ) -> Self:
167
166
  """
168
167
  Function for using another module. Using another module replaces the module's
@@ -179,7 +178,7 @@ class Module:
179
178
  self,
180
179
  module: Module,
181
180
  *,
182
- priority: ModulePriority | ModulePriorityStr = ...,
181
+ priority: Priority | PriorityStr = ...,
183
182
  ) -> ContextManager[None] | ContextDecorator:
184
183
  """
185
184
  Context manager or decorator for temporary use of a module.
@@ -188,7 +187,7 @@ class Module:
188
187
  def change_priority(
189
188
  self,
190
189
  module: Module,
191
- priority: ModulePriority | ModulePriorityStr,
190
+ priority: Priority | PriorityStr,
192
191
  ) -> Self:
193
192
  """
194
193
  Function for changing the priority of a module in use.
@@ -217,7 +216,7 @@ class Module:
217
216
  """
218
217
 
219
218
  @final
220
- class ModulePriority(Enum):
219
+ class Priority(Enum):
221
220
  LOW = ...
222
221
  HIGH = ...
223
222
 
@@ -230,7 +229,7 @@ class Injectable[T](Protocol):
230
229
  def get_instance(self) -> T: ...
231
230
 
232
231
  @final
233
- class InjectableMode(Enum):
232
+ class Mode(Enum):
234
233
  FALLBACK = ...
235
234
  NORMAL = ...
236
235
  OVERRIDE = ...
@@ -0,0 +1,53 @@
1
+ from collections.abc import Iterable
2
+ from typing import Any
3
+
4
+ from injection._core.common.type import InputType, standardize_types
5
+ from injection._core.hook import HookGenerator
6
+ from injection._core.module import Locator, Mode, Record, Updater
7
+
8
+ __all__ = ()
9
+
10
+
11
+ @Locator.static_hooks.on_conflict
12
+ def check_mode[T](
13
+ new: Record[T],
14
+ existing: Record[T],
15
+ cls: InputType[T],
16
+ *_: Any,
17
+ **__: Any,
18
+ ) -> HookGenerator[bool]:
19
+ new_mode = new.mode
20
+ is_override = new_mode == Mode.OVERRIDE
21
+
22
+ if new_mode == existing.mode and not is_override:
23
+ raise RuntimeError(f"An injectable already exists for the class `{cls}`.")
24
+
25
+ value = yield
26
+ return value or is_override
27
+
28
+
29
+ @Locator.static_hooks.on_conflict
30
+ def compare_mode_rank[T](
31
+ new: Record[T],
32
+ existing: Record[T],
33
+ *_: Any,
34
+ **__: Any,
35
+ ) -> HookGenerator[bool]:
36
+ value = yield
37
+ return value or new.mode.rank > existing.mode.rank
38
+
39
+
40
+ @Locator.static_hooks.on_input
41
+ def standardize_input_classes[T](
42
+ *_: Any,
43
+ **__: Any,
44
+ ) -> HookGenerator[Iterable[InputType[T]]]:
45
+ classes = yield
46
+ return tuple(standardize_types(*classes, with_origin=True))
47
+
48
+
49
+ @Locator.static_hooks.on_update
50
+ def standardize_classes[T](*_: Any, **__: Any) -> HookGenerator[Updater[T]]:
51
+ updater = yield
52
+ updater.classes = set(standardize_types(*updater.classes))
53
+ return updater
@@ -1,22 +1,42 @@
1
- from collections.abc import Callable, Iterable, Iterator
2
- from inspect import get_annotations, isfunction
3
- from types import UnionType
1
+ from collections.abc import Awaitable, Callable, Iterable, Iterator
2
+ from inspect import iscoroutinefunction, isfunction
3
+ from types import GenericAlias, UnionType
4
4
  from typing import (
5
5
  Annotated,
6
6
  Any,
7
7
  TypeAliasType,
8
8
  Union,
9
- cast,
10
9
  get_args,
11
10
  get_origin,
11
+ get_type_hints,
12
12
  )
13
13
 
14
- type TypeDef[T] = type[T] | TypeAliasType
14
+ type TypeDef[T] = type[T] | TypeAliasType | GenericAlias
15
15
  type InputType[T] = TypeDef[T] | UnionType
16
16
  type TypeInfo[T] = InputType[T] | Callable[..., T] | Iterable[TypeInfo[T]]
17
17
 
18
18
 
19
- def analyze_types(
19
+ def get_return_types(*args: TypeInfo[Any]) -> Iterator[InputType[Any]]:
20
+ for arg in args:
21
+ if isinstance(arg, Iterable) and not (
22
+ isinstance(arg, type | str) or isinstance(get_origin(arg), type)
23
+ ):
24
+ inner_args = arg
25
+
26
+ elif isfunction(arg) and (return_type := get_type_hints(arg).get("return")):
27
+ if iscoroutinefunction(arg):
28
+ return_type = Awaitable[return_type] # type: ignore[valid-type]
29
+
30
+ inner_args = (return_type,)
31
+
32
+ else:
33
+ yield arg # type: ignore[misc]
34
+ continue
35
+
36
+ yield from get_return_types(*inner_args)
37
+
38
+
39
+ def standardize_types(
20
40
  *types: InputType[Any],
21
41
  with_origin: bool = False,
22
42
  ) -> Iterator[TypeDef[Any]]:
@@ -40,23 +60,4 @@ def analyze_types(
40
60
 
41
61
  continue
42
62
 
43
- yield from analyze_types(*inner_types, with_origin=with_origin)
44
-
45
-
46
- def get_return_types(*args: TypeInfo[Any]) -> Iterator[InputType[Any]]:
47
- for arg in args:
48
- if isinstance(arg, Iterable) and not (
49
- isinstance(arg, type | str) or isinstance(get_origin(arg), type)
50
- ):
51
- inner_args = arg
52
-
53
- elif isfunction(arg) and (
54
- return_type := get_annotations(arg, eval_str=True).get("return")
55
- ):
56
- inner_args = (return_type,)
57
-
58
- else:
59
- yield cast(InputType[Any], arg)
60
- continue
61
-
62
- yield from get_return_types(*inner_args)
63
+ yield from standardize_types(*inner_types, with_origin=with_origin)
@@ -0,0 +1,96 @@
1
+ import itertools
2
+ from collections.abc import Callable, Generator, Iterator
3
+ from dataclasses import dataclass, field
4
+ from inspect import isclass, isgeneratorfunction
5
+ from typing import Self
6
+
7
+ from injection.exceptions import HookError
8
+
9
+ type HookGenerator[T] = Generator[None, T, T]
10
+ type HookFunction[**P, T] = Callable[P, T | HookGenerator[T]]
11
+
12
+
13
+ @dataclass(eq=False, frozen=True, slots=True)
14
+ class Hook[**P, T]:
15
+ __functions: list[HookFunction[P, T]] = field(
16
+ default_factory=list,
17
+ init=False,
18
+ repr=False,
19
+ )
20
+
21
+ def __call__( # type: ignore[no-untyped-def]
22
+ self,
23
+ wrapped: HookFunction[P, T] | type[HookFunction[P, T]] | None = None,
24
+ /,
25
+ ):
26
+ def decorator(wp): # type: ignore[no-untyped-def]
27
+ self.add(wp() if isclass(wp) else wp)
28
+ return wp
29
+
30
+ return decorator(wrapped) if wrapped else decorator
31
+
32
+ @property
33
+ def __stack(self) -> Iterator[HookFunction[P, T]]:
34
+ return iter(self.__functions)
35
+
36
+ def add(self, *functions: HookFunction[P, T]) -> Self:
37
+ self.__functions.extend(functions)
38
+ return self
39
+
40
+ @classmethod
41
+ def apply_several(cls, handler: Callable[P, T], *hooks: Self) -> Callable[P, T]:
42
+ stack = itertools.chain.from_iterable((hook.__stack for hook in hooks))
43
+ return cls.__apply_stack(handler, stack)
44
+
45
+ @classmethod
46
+ def __apply_function(
47
+ cls,
48
+ handler: Callable[P, T],
49
+ function: HookFunction[P, T],
50
+ ) -> Callable[P, T]:
51
+ if not isgeneratorfunction(function):
52
+ return function # type: ignore[return-value]
53
+
54
+ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
55
+ hook = function(*args, **kwargs)
56
+
57
+ try:
58
+ next(hook)
59
+
60
+ try:
61
+ value = handler(*args, **kwargs)
62
+ except BaseException as exc:
63
+ hook.throw(exc)
64
+ else:
65
+ hook.send(value)
66
+ return value
67
+
68
+ except StopIteration as stop:
69
+ return stop.value
70
+
71
+ finally:
72
+ hook.close()
73
+
74
+ raise HookError("Missing return value.")
75
+
76
+ return wrapper
77
+
78
+ @classmethod
79
+ def __apply_stack(
80
+ cls,
81
+ handler: Callable[P, T],
82
+ stack: Iterator[HookFunction[P, T]],
83
+ ) -> Callable[P, T]:
84
+ for function in stack:
85
+ new_handler = cls.__apply_function(handler, function)
86
+ return cls.__apply_stack(new_handler, stack)
87
+
88
+ return handler
89
+
90
+
91
+ def apply_hooks[**P, T](
92
+ handler: Callable[P, T],
93
+ hook: Hook[P, T],
94
+ *hooks: Hook[P, T],
95
+ ) -> Callable[P, T]:
96
+ return Hook.apply_several(handler, hook, *hooks)
@@ -37,13 +37,8 @@ from injection._core.common.event import Event, EventChannel, EventListener
37
37
  from injection._core.common.invertible import Invertible, SimpleInvertible
38
38
  from injection._core.common.lazy import Lazy, LazyMapping
39
39
  from injection._core.common.threading import synchronized
40
- from injection._core.common.type import (
41
- InputType,
42
- TypeDef,
43
- TypeInfo,
44
- analyze_types,
45
- get_return_types,
46
- )
40
+ from injection._core.common.type import InputType, TypeInfo, get_return_types
41
+ from injection._core.hook import Hook, apply_hooks
47
42
  from injection.exceptions import (
48
43
  InjectionError,
49
44
  ModuleError,
@@ -64,7 +59,7 @@ class LocatorEvent(Event, ABC):
64
59
 
65
60
  @dataclass(frozen=True, slots=True)
66
61
  class LocatorDependenciesUpdated[T](LocatorEvent):
67
- classes: Collection[TypeDef[T]]
62
+ classes: Collection[InputType[T]]
68
63
  mode: Mode
69
64
 
70
65
  @override
@@ -206,6 +201,13 @@ class ShouldBeInjectable[T](Injectable[T]):
206
201
  def get_instance(self) -> NoReturn:
207
202
  raise InjectionError(f"`{self.cls}` should be an injectable.")
208
203
 
204
+ @classmethod
205
+ def from_callable(cls, callable: Callable[..., T]) -> Self:
206
+ if not isclass(callable):
207
+ raise TypeError(f"`{callable}` should be a class.")
208
+
209
+ return cls(callable)
210
+
209
211
 
210
212
  """
211
213
  Broker
@@ -255,22 +257,58 @@ class Mode(StrEnum):
255
257
 
256
258
  type ModeStr = Literal["fallback", "normal", "override"]
257
259
 
260
+ type InjectableFactory[T] = Callable[[Callable[..., T]], Injectable[T]]
261
+
258
262
 
259
263
  class Record[T](NamedTuple):
260
264
  injectable: Injectable[T]
261
265
  mode: Mode
262
266
 
263
267
 
268
+ @dataclass(repr=False, eq=False, kw_only=True, slots=True)
269
+ class Updater[T]:
270
+ factory: Callable[..., T]
271
+ classes: Iterable[InputType[T]]
272
+ injectable_factory: InjectableFactory[T]
273
+ mode: Mode
274
+
275
+ def make_record(self) -> Record[T]:
276
+ injectable = self.injectable_factory(self.factory)
277
+ return Record(injectable, self.mode)
278
+
279
+
280
+ class LocatorHooks[T](NamedTuple):
281
+ on_conflict: Hook[[Record[T], Record[T], InputType[T]], bool]
282
+ on_input: Hook[[Iterable[InputType[T]]], Iterable[InputType[T]]]
283
+ on_update: Hook[[Updater[T]], Updater[T]]
284
+
285
+ @classmethod
286
+ def default(cls) -> Self:
287
+ return cls(
288
+ on_conflict=Hook(),
289
+ on_input=Hook(),
290
+ on_update=Hook(),
291
+ )
292
+
293
+
264
294
  @dataclass(repr=False, frozen=True, slots=True)
265
295
  class Locator(Broker):
266
- __records: dict[TypeDef[Any], Record[Any]] = field(default_factory=dict, init=False)
267
- __channel: EventChannel = field(default_factory=EventChannel, init=False)
296
+ __records: dict[InputType[Any], Record[Any]] = field(
297
+ default_factory=dict,
298
+ init=False,
299
+ )
300
+ __channel: EventChannel = field(
301
+ default_factory=EventChannel,
302
+ init=False,
303
+ )
304
+
305
+ static_hooks: ClassVar[LocatorHooks[Any]] = LocatorHooks.default()
268
306
 
269
307
  @override
270
308
  def __getitem__[T](self, cls: InputType[T], /) -> Injectable[T]:
271
- for analyzed_class in analyze_types(cls, with_origin=True):
309
+ for input_class in self.__standardize_inputs((cls,)):
272
310
  try:
273
- record = self.__records[analyzed_class]
311
+ record = self.__records[input_class]
274
312
  except KeyError:
275
313
  continue
276
314
 
@@ -281,8 +319,8 @@ class Locator(Broker):
281
319
  @override
282
320
  def __contains__(self, cls: InputType[Any], /) -> bool:
283
321
  return any(
284
- analyzed_class in self.__records
285
- for analyzed_class in analyze_types(cls, with_origin=True)
322
+ input_class in self.__records
323
+ for input_class in self.__standardize_inputs((cls,))
286
324
  )
287
325
 
288
326
  @property
@@ -292,21 +330,16 @@ class Locator(Broker):
292
330
 
293
331
  @property
294
332
  def __injectables(self) -> frozenset[Injectable[Any]]:
295
- return frozenset(injectable for injectable, _ in self.__records.values())
333
+ return frozenset(record.injectable for record in self.__records.values())
296
334
 
297
335
  @synchronized()
298
- def update[T](
299
- self,
300
- classes: Iterable[InputType[T]],
301
- injectable: Injectable[T],
302
- mode: Mode | ModeStr,
303
- ) -> Self:
304
- mode = Mode(mode)
305
- record = Record(injectable, mode)
306
- records = {cls: record for cls in self.__prepare_for_updating(classes, mode)}
336
+ def update[T](self, updater: Updater[T]) -> Self:
337
+ updater = self.__update_preprocessing(updater)
338
+ record = updater.make_record()
339
+ records = dict(self.__prepare_for_updating(updater.classes, record))
307
340
 
308
341
  if records:
309
- event = LocatorDependenciesUpdated(self, records.keys(), mode)
342
+ event = LocatorDependenciesUpdated(self, records.keys(), record.mode)
310
343
 
311
344
  with self.dispatch(event):
312
345
  self.__records.update(records)
@@ -331,29 +364,38 @@ class Locator(Broker):
331
364
  def __prepare_for_updating[T](
332
365
  self,
333
366
  classes: Iterable[InputType[T]],
334
- mode: Mode,
335
- ) -> Iterator[TypeDef[T]]:
336
- rank = mode.rank
337
-
338
- for cls in frozenset(analyze_types(*classes)):
367
+ record: Record[T],
368
+ ) -> Iterator[tuple[InputType[T], Record[T]]]:
369
+ for cls in classes:
339
370
  try:
340
- record = self.__records[cls]
341
-
371
+ existing = self.__records[cls]
342
372
  except KeyError:
343
373
  ...
344
-
345
374
  else:
346
- current_mode = record.mode
375
+ if not self.__keep_new_record(record, existing, cls):
376
+ continue
347
377
 
348
- if mode == current_mode and mode != Mode.OVERRIDE:
349
- raise RuntimeError(
350
- f"An injectable already exists for the class `{cls}`."
351
- )
378
+ yield cls, record
352
379
 
353
- elif rank < current_mode.rank:
354
- continue
380
+ def __keep_new_record[T](
381
+ self,
382
+ new: Record[T],
383
+ existing: Record[T],
384
+ cls: InputType[T],
385
+ ) -> bool:
386
+ return apply_hooks(
387
+ lambda *args, **kwargs: False,
388
+ self.static_hooks.on_conflict,
389
+ )(new, existing, cls)
355
390
 
356
- yield cls
391
+ def __standardize_inputs[T](
392
+ self,
393
+ classes: Iterable[InputType[T]],
394
+ ) -> Iterable[InputType[T]]:
395
+ return apply_hooks(lambda c: c, self.static_hooks.on_input)(classes)
396
+
397
+ def __update_preprocessing[T](self, updater: Updater[T]) -> Updater[T]:
398
+ return apply_hooks(lambda u: u, self.static_hooks.on_update)(updater)
357
399
 
358
400
 
359
401
  """
@@ -372,8 +414,6 @@ class Priority(StrEnum):
372
414
 
373
415
  type PriorityStr = Literal["low", "high"]
374
416
 
375
- type InjectableFactory[T] = Callable[[Callable[..., T]], Injectable[T]]
376
-
377
417
 
378
418
  @dataclass(eq=False, frozen=True, slots=True)
379
419
  class Module(Broker, EventListener):
@@ -438,9 +478,14 @@ class Module(Broker, EventListener):
438
478
  ):
439
479
  def decorator(wp): # type: ignore[no-untyped-def]
440
480
  factory = self.inject(wp, return_factory=True) if inject else wp
441
- injectable = cls(factory)
442
481
  classes = get_return_types(wp, on)
443
- self.update(classes, injectable, mode)
482
+ updater = Updater(
483
+ factory=factory,
484
+ classes=classes,
485
+ injectable_factory=cls,
486
+ mode=Mode(mode),
487
+ )
488
+ self.update(updater)
444
489
  return wp
445
490
 
446
491
  return decorator(wrapped) if wrapped else decorator
@@ -449,11 +494,13 @@ class Module(Broker, EventListener):
449
494
 
450
495
  def should_be_injectable[T](self, wrapped: type[T] | None = None, /): # type: ignore[no-untyped-def]
451
496
  def decorator(wp): # type: ignore[no-untyped-def]
452
- self.update(
453
- (wp,),
454
- ShouldBeInjectable(wp),
497
+ updater = Updater(
498
+ factory=wp,
499
+ classes=(wp,),
500
+ injectable_factory=ShouldBeInjectable.from_callable,
455
501
  mode=Mode.FALLBACK,
456
502
  )
503
+ self.update(updater)
457
504
  return wp
458
505
 
459
506
  return decorator(wrapped) if wrapped else decorator
@@ -543,13 +590,8 @@ class Module(Broker, EventListener):
543
590
  function.set_owner(cls)
544
591
  return SimpleInvertible(function)
545
592
 
546
- def update[T](
547
- self,
548
- classes: Iterable[InputType[T]],
549
- injectable: Injectable[T],
550
- mode: Mode | ModeStr = Mode.get_default(),
551
- ) -> Self:
552
- self.__locator.update(classes, injectable, mode)
593
+ def update[T](self, updater: Updater[T]) -> Self:
594
+ self.__locator.update(updater)
553
595
  return self
554
596
 
555
597
  def init_modules(self, *modules: Module) -> Self:
@@ -881,11 +923,7 @@ class InjectedFunction[**P, T](EventListener):
881
923
  yield
882
924
  self.update(event.module)
883
925
 
884
- @synchronized()
885
926
  def __close_setup_queue(self) -> None:
886
- if self.__setup_queue is None:
887
- return
888
-
889
927
  self.__setup_queue = None
890
928
 
891
929
  def __setup(self) -> None:
@@ -1,11 +1,12 @@
1
1
  from typing import Any
2
2
 
3
3
  __all__ = (
4
+ "HookError",
4
5
  "InjectionError",
5
- "NoInjectable",
6
6
  "ModuleError",
7
7
  "ModuleLockError",
8
8
  "ModuleNotUsedError",
9
+ "NoInjectable",
9
10
  )
10
11
 
11
12
 
@@ -33,3 +34,6 @@ class ModuleLockError(ModuleError): ...
33
34
 
34
35
 
35
36
  class ModuleNotUsedError(KeyError, ModuleError): ...
37
+
38
+
39
+ class HookError(InjectionError): ...
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "python-injection"
3
- version = "0.10.2"
3
+ version = "0.10.4"
4
4
  description = "Fast and easy dependency injection framework."
5
5
  license = "MIT"
6
6
  authors = ["remimd"]