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
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 @@ def __str__(self) -> str:
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 @@ def __contains__(self, cls: type | UnionType, /) -> bool:
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 @@ def __brokers(self) -> Iterator[Container | Module]:
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 @@ def get_constant():
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 @@ def __move_module(self, module: Module, priority: ModulePriorities):
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 @@ def on_event(self, event: Event, /):
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
0 commit comments