python-injection 0.6.8__py3-none-any.whl → 0.6.9__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/_pkg.py CHANGED
@@ -1,6 +1,7 @@
1
- from .core import Module, ModulePriorities
1
+ from .core import Injectable, Module, ModulePriorities
2
2
 
3
3
  __all__ = (
4
+ "Injectable",
4
5
  "Module",
5
6
  "ModulePriorities",
6
7
  "default_module",
@@ -16,9 +17,7 @@ default_module = Module(f"{__name__}:default_module")
16
17
 
17
18
  get_instance = default_module.get_instance
18
19
  get_lazy_instance = default_module.get_lazy_instance
19
-
20
20
  inject = default_module.inject
21
21
  injectable = default_module.injectable
22
- singleton = default_module.singleton
23
-
24
22
  set_constant = default_module.set_constant
23
+ singleton = default_module.singleton
injection/_pkg.pyi CHANGED
@@ -1,8 +1,17 @@
1
+ from abc import abstractmethod
1
2
  from collections.abc import Callable, Iterable
2
3
  from contextlib import ContextDecorator
3
4
  from enum import Enum
4
5
  from types import UnionType
5
- from typing import Any, ContextManager, Final, TypeVar, final
6
+ from typing import (
7
+ Any,
8
+ ContextManager,
9
+ Final,
10
+ Protocol,
11
+ TypeVar,
12
+ final,
13
+ runtime_checkable,
14
+ )
6
15
 
7
16
  from injection.common.lazy import Lazy
8
17
 
@@ -12,12 +21,10 @@ default_module: Final[Module] = ...
12
21
 
13
22
  get_instance = default_module.get_instance
14
23
  get_lazy_instance = default_module.get_lazy_instance
15
-
16
24
  inject = default_module.inject
17
25
  injectable = default_module.injectable
18
- singleton = default_module.singleton
19
-
20
26
  set_constant = default_module.set_constant
27
+ singleton = default_module.singleton
21
28
 
22
29
  @final
23
30
  class Module:
@@ -42,6 +49,7 @@ class Module:
42
49
  wrapped: Callable[..., Any] = ...,
43
50
  /,
44
51
  *,
52
+ cls: type[Injectable] = ...,
45
53
  on: type | Iterable[type] | UnionType = ...,
46
54
  ):
47
55
  """
@@ -119,3 +127,12 @@ class Module:
119
127
  class ModulePriorities(Enum):
120
128
  HIGH = ...
121
129
  LOW = ...
130
+
131
+ @runtime_checkable
132
+ class Injectable(Protocol[_T]):
133
+ def __init__(self, factory: Callable[[], _T] = ..., *args, **kwargs): ...
134
+ @property
135
+ def is_locked(self) -> bool: ...
136
+ def unlock(self): ...
137
+ @abstractmethod
138
+ def get_instance(self) -> _T: ...
@@ -1,8 +1,9 @@
1
- from collections.abc import Iterator
1
+ from collections.abc import Iterable, Iterator
2
+ from inspect import get_annotations, isfunction
2
3
  from types import NoneType, UnionType
3
4
  from typing import Annotated, Any, Union, get_args, get_origin
4
5
 
5
- __all__ = ("format_type", "get_origins")
6
+ __all__ = ("find_types", "format_type", "get_origins")
6
7
 
7
8
 
8
9
  def format_type(cls: type | Any) -> str:
@@ -12,25 +13,36 @@ def format_type(cls: type | Any) -> str:
12
13
  return str(cls)
13
14
 
14
15
 
15
- def get_origins(*classes: type | Any) -> Iterator[type | Any]:
16
- for cls in classes:
17
- origin = get_origin(cls) or cls
16
+ def get_origins(*types: type | Any) -> Iterator[type | Any]:
17
+ for tp in types:
18
+ origin = get_origin(tp) or tp
18
19
 
19
20
  if origin in (None, NoneType):
20
21
  continue
21
22
 
22
- arguments = get_args(cls)
23
+ elif origin in (Union, UnionType):
24
+ args = get_args(tp)
23
25
 
24
- if origin in (Union, UnionType):
25
- yield from get_origins(*arguments)
26
+ elif origin is Annotated is not tp:
27
+ args = (tp.__origin__,)
28
+
29
+ else:
30
+ yield origin
31
+ continue
32
+
33
+ yield from get_origins(*args)
26
34
 
27
- elif origin is Annotated:
28
- try:
29
- annotated = arguments[0]
30
- except IndexError:
31
- continue
32
35
 
33
- yield from get_origins(annotated)
36
+ def find_types(*args: Any) -> Iterator[type | UnionType]:
37
+ for argument in args:
38
+ if isinstance(argument, Iterable) and not isinstance(argument, type | str):
39
+ arguments = argument
40
+
41
+ elif isfunction(argument):
42
+ arguments = (get_annotations(argument, eval_str=True).get("return"),)
34
43
 
35
44
  else:
36
- yield origin
45
+ yield argument
46
+ continue
47
+
48
+ yield from find_types(*arguments)
injection/core/module.py CHANGED
@@ -15,8 +15,8 @@ from collections.abc import (
15
15
  from contextlib import ContextDecorator, contextmanager, suppress
16
16
  from dataclasses import dataclass, field
17
17
  from enum import Enum, auto
18
- from functools import singledispatchmethod, wraps
19
- from inspect import Signature, get_annotations, isclass, isfunction
18
+ from functools import partialmethod, singledispatchmethod, wraps
19
+ from inspect import Signature, isclass
20
20
  from threading import RLock
21
21
  from types import MappingProxyType, UnionType
22
22
  from typing import (
@@ -26,13 +26,12 @@ from typing import (
26
26
  Protocol,
27
27
  TypeVar,
28
28
  cast,
29
- final,
30
29
  runtime_checkable,
31
30
  )
32
31
 
33
32
  from injection.common.event import Event, EventChannel, EventListener
34
33
  from injection.common.lazy import Lazy, LazyMapping
35
- from injection.common.tools import format_type, get_origins
34
+ from injection.common.tools import find_types, format_type, get_origins
36
35
  from injection.exceptions import (
37
36
  ModuleError,
38
37
  ModuleLockError,
@@ -133,6 +132,9 @@ Injectables
133
132
  class Injectable(Protocol[_T]):
134
133
  __slots__ = ()
135
134
 
135
+ def __init__(self, factory: Callable[[], _T] = ..., *args, **kwargs):
136
+ ...
137
+
136
138
  @property
137
139
  def is_locked(self) -> bool:
138
140
  return False
@@ -288,18 +290,6 @@ class Module(EventListener):
288
290
  def __str__(self) -> str:
289
291
  return self.name or object.__str__(self)
290
292
 
291
- @property
292
- def inject(self) -> InjectDecorator:
293
- return InjectDecorator(self)
294
-
295
- @property
296
- def injectable(self) -> InjectableDecorator:
297
- return InjectableDecorator(self, NewInjectable)
298
-
299
- @property
300
- def singleton(self) -> InjectableDecorator:
301
- return InjectableDecorator(self, SingletonInjectable)
302
-
303
293
  @property
304
294
  def is_locked(self) -> bool:
305
295
  return any(broker.is_locked for broker in self.__brokers)
@@ -309,6 +299,25 @@ class Module(EventListener):
309
299
  yield from tuple(self.__modules)
310
300
  yield self.__container
311
301
 
302
+ def injectable(
303
+ self,
304
+ wrapped: Callable[..., Any] = None,
305
+ /,
306
+ *,
307
+ cls: type[Injectable] = NewInjectable,
308
+ on: type | Types = None,
309
+ ):
310
+ def decorator(wp):
311
+ factory = self.inject(wp, return_factory=True)
312
+ injectable = cls(factory)
313
+ classes = find_types(wp, on)
314
+ self.update(classes, injectable)
315
+ return wp
316
+
317
+ return decorator(wrapped) if wrapped else decorator
318
+
319
+ singleton = partialmethod(injectable, cls=SingletonInjectable)
320
+
312
321
  def set_constant(self, instance: _T, on: type | Types = None) -> _T:
313
322
  cls = type(instance)
314
323
 
@@ -318,6 +327,29 @@ class Module(EventListener):
318
327
 
319
328
  return instance
320
329
 
330
+ def inject(
331
+ self,
332
+ wrapped: Callable[..., Any] = None,
333
+ /,
334
+ *,
335
+ return_factory: bool = False,
336
+ ):
337
+ def decorator(wp):
338
+ if not return_factory and isclass(wp):
339
+ wp.__init__ = decorator(wp.__init__)
340
+ return wp
341
+
342
+ lazy_binder = Lazy[Binder](lambda: self.__new_binder(wp))
343
+
344
+ @wraps(wp)
345
+ def wrapper(*args, **kwargs):
346
+ arguments = (~lazy_binder).bind(*args, **kwargs)
347
+ return wp(*arguments.args, **arguments.kwargs)
348
+
349
+ return wrapper
350
+
351
+ return decorator(wrapped) if wrapped else decorator
352
+
321
353
  def get_instance(self, cls: type[_T]) -> _T | None:
322
354
  try:
323
355
  injectable = self[cls]
@@ -420,6 +452,12 @@ class Module(EventListener):
420
452
  f"`{module}` can't be found in the modules used by `{self}`."
421
453
  ) from exc
422
454
 
455
+ def __new_binder(self, target: Callable[..., Any]) -> Binder:
456
+ signature = inspect.signature(target, eval_str=True)
457
+ binder = Binder(signature).update(self)
458
+ self.add_listener(binder)
459
+ return binder
460
+
423
461
 
424
462
  """
425
463
  Binder
@@ -502,85 +540,3 @@ class Binder(EventListener):
502
540
  def _(self, event: ModuleEvent, /) -> ContextManager:
503
541
  yield
504
542
  self.update(event.on_module)
505
-
506
-
507
- """
508
- Decorators
509
- """
510
-
511
-
512
- @final
513
- @dataclass(repr=False, frozen=True, slots=True)
514
- class InjectDecorator:
515
- __module: Module
516
-
517
- def __call__(self, wrapped: Callable[..., Any] = None, /):
518
- def decorator(wp):
519
- if isclass(wp):
520
- return self.__class_decorator(wp)
521
-
522
- return self.__decorator(wp)
523
-
524
- return decorator(wrapped) if wrapped else decorator
525
-
526
- def __decorator(self, function: Callable[..., Any], /) -> Callable[..., Any]:
527
- lazy_binder = Lazy[Binder](lambda: self.__new_binder(function))
528
-
529
- @wraps(function)
530
- def wrapper(*args, **kwargs):
531
- arguments = (~lazy_binder).bind(*args, **kwargs)
532
- return function(*arguments.args, **arguments.kwargs)
533
-
534
- return wrapper
535
-
536
- def __class_decorator(self, cls: type, /) -> type:
537
- cls.__init__ = self.__decorator(cls.__init__)
538
- return cls
539
-
540
- def __new_binder(self, function: Callable[..., Any]) -> Binder:
541
- signature = inspect.signature(function, eval_str=True)
542
- binder = Binder(signature).update(self.__module)
543
- self.__module.add_listener(binder)
544
- return binder
545
-
546
-
547
- @final
548
- @dataclass(repr=False, frozen=True, slots=True)
549
- class InjectableDecorator:
550
- __module: Module
551
- __injectable_type: type[BaseInjectable]
552
-
553
- def __repr__(self) -> str:
554
- return f"<{self.__injectable_type.__qualname__} decorator>"
555
-
556
- def __call__(
557
- self,
558
- wrapped: Callable[..., Any] = None,
559
- /,
560
- *,
561
- on: type | Types = None,
562
- ):
563
- def decorator(wp):
564
- @self.__module.inject
565
- @wraps(wp, updated=())
566
- def factory(*args, **kwargs):
567
- return wp(*args, **kwargs)
568
-
569
- injectable = self.__injectable_type(factory)
570
- classes = self.__get_classes(wp, on)
571
- self.__module.update(classes, injectable)
572
- return wp
573
-
574
- return decorator(wrapped) if wrapped else decorator
575
-
576
- @classmethod
577
- def __get_classes(cls, *objects: Any) -> Iterator[type | UnionType]:
578
- for obj in objects:
579
- if isinstance(obj, Iterable) and not isinstance(obj, type | str):
580
- yield from cls.__get_classes(*obj)
581
-
582
- elif isfunction(obj):
583
- yield get_annotations(obj, eval_str=True).get("return")
584
-
585
- else:
586
- yield obj
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-injection
3
- Version: 0.6.8
3
+ Version: 0.6.9
4
4
  Summary: Fast and easy dependency injection framework.
5
5
  Home-page: https://github.com/100nm/python-injection
6
6
  License: MIT
@@ -1,17 +1,17 @@
1
1
  injection/__init__.py,sha256=9_AVJILxKIBiL_6KJSh2RRydgSHXH38uahAGD0S1-dI,20
2
- injection/_pkg.py,sha256=7Ns-9VxyGeIf2fOVfOAa5rD_kxAcY1WSHGSkKYAW4T0,536
3
- injection/_pkg.pyi,sha256=YfSr-pwahHfVx8wak4JxDcXsgrIohd2ZJ73GgysFLNA,4209
2
+ injection/_pkg.py,sha256=dM48pyMks5CDaUdl4Mv6h_BudbAl8QfYsLbYW9-8Wfw,564
3
+ injection/_pkg.pyi,sha256=EFMxWfYDqf2aI3UosWNXacof8kPsoiu_9GuqFZg4pOQ,4602
4
4
  injection/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  injection/common/event.py,sha256=uFoGRnxxkohH53JEStn4tN2Pn79HlgGExh7VkXdBwVQ,1316
6
6
  injection/common/lazy.py,sha256=HIefQ1z7ivgU791MDSwBmUcdST3bOv0sivSMyR2DfHc,1493
7
7
  injection/common/tools/__init__.py,sha256=S2y9DaQ4EaTRty9fKpBtPXA7hSjkzgM5N2jFf5jcmJU,21
8
- injection/common/tools/_type.py,sha256=3hElLUPtiG6_kmxeWiNabX5zNKN_m1vUc3gBVdOf9WY,887
8
+ injection/common/tools/_type.py,sha256=-zL0dtoVZme71Mscvav7iEWxY2-JltzNTekbWOCPSFo,1276
9
9
  injection/core/__init__.py,sha256=zuf0ubI2dHnbjn1059eduhS-ACIkkROa6-dhp10krh0,22
10
- injection/core/module.py,sha256=Lm0uc0uUvVQLfnXwfH6xaXoHYuPvdbR8HgFXWrZmi5Y,15595
10
+ injection/core/module.py,sha256=d_dkGRQzEkR_frxdNbxBxbp0jqB-mU0UZ4mwg5aIATA,14407
11
11
  injection/exceptions.py,sha256=wd4OxmpneGEmlZ0yeNBfnCYfPYqUksfbmA2mopLNm_s,688
12
12
  injection/integrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  injection/integrations/blacksheep.py,sha256=F4h85VS9dLGHn-ARDARYMB02Htsd3nKMT9Y-EF4cOqE,818
14
14
  injection/utils.py,sha256=a4w2SXyXP1A8AC_4hyEypByVUZOwSFIA3lh8StLxuLY,590
15
- python_injection-0.6.8.dist-info/METADATA,sha256=4E21PIf33RBie3Wje2_h58VBDSkBwhKGn7gZAuai6q0,3227
16
- python_injection-0.6.8.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
17
- python_injection-0.6.8.dist-info/RECORD,,
15
+ python_injection-0.6.9.dist-info/METADATA,sha256=Y8KBbVJPazkix_y012d70rivwFLvdRwrhEnbpCItOXA,3227
16
+ python_injection-0.6.9.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
17
+ python_injection-0.6.9.dist-info/RECORD,,