aspyx 1.0.1__py3-none-any.whl → 1.2.0__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 aspyx might be problematic. Click here for more details.
- aspyx/di/__init__.py +7 -5
- aspyx/di/aop/aop.py +2 -2
- aspyx/di/configuration/__init__.py +4 -1
- aspyx/di/configuration/configuration.py +4 -54
- aspyx/di/configuration/env_configuration_source.py +55 -0
- aspyx/di/configuration/yaml_configuration_source.py +26 -0
- aspyx/di/di.py +260 -94
- aspyx/di/util/__init__.py +8 -0
- aspyx/di/util/stringbuilder.py +31 -0
- aspyx/reflection/reflection.py +40 -6
- {aspyx-1.0.1.dist-info → aspyx-1.2.0.dist-info}/METADATA +95 -27
- aspyx-1.2.0.dist-info/RECORD +18 -0
- aspyx-1.0.1.dist-info/RECORD +0 -14
- {aspyx-1.0.1.dist-info → aspyx-1.2.0.dist-info}/WHEEL +0 -0
- {aspyx-1.0.1.dist-info → aspyx-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {aspyx-1.0.1.dist-info → aspyx-1.2.0.dist-info}/top_level.txt +0 -0
aspyx/di/di.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
The
|
|
2
|
+
The dependency injection module provides a framework for managing dependencies and lifecycle of objects in Python applications.
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
@@ -7,10 +7,11 @@ import inspect
|
|
|
7
7
|
import logging
|
|
8
8
|
|
|
9
9
|
from abc import abstractmethod, ABC
|
|
10
|
-
from enum import Enum
|
|
10
|
+
from enum import Enum
|
|
11
11
|
import threading
|
|
12
12
|
from typing import Type, Dict, TypeVar, Generic, Optional, cast, Callable
|
|
13
13
|
|
|
14
|
+
from aspyx.di.util import StringBuilder
|
|
14
15
|
from aspyx.reflection import Decorators, TypeDescriptor, DecoratorDescriptor
|
|
15
16
|
|
|
16
17
|
T = TypeVar("T")
|
|
@@ -26,11 +27,21 @@ class Factory(ABC, Generic[T]):
|
|
|
26
27
|
def create(self) -> T:
|
|
27
28
|
pass
|
|
28
29
|
|
|
29
|
-
class
|
|
30
|
+
class DIException(Exception):
|
|
30
31
|
"""
|
|
31
32
|
Exception raised for errors in the injector.
|
|
32
33
|
"""
|
|
33
34
|
|
|
35
|
+
class DIRegistrationException(DIException):
|
|
36
|
+
"""
|
|
37
|
+
Exception raised during the registration of dependencies.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
class DIRuntimeException(DIException):
|
|
41
|
+
"""
|
|
42
|
+
Exception raised during the runtime.
|
|
43
|
+
"""
|
|
44
|
+
|
|
34
45
|
class AbstractInstanceProvider(ABC, Generic[T]):
|
|
35
46
|
"""
|
|
36
47
|
Interface for instance providers.
|
|
@@ -39,6 +50,9 @@ class AbstractInstanceProvider(ABC, Generic[T]):
|
|
|
39
50
|
def get_module(self) -> str:
|
|
40
51
|
pass
|
|
41
52
|
|
|
53
|
+
def get_host(self) -> Type[T]:
|
|
54
|
+
return type(self)
|
|
55
|
+
|
|
42
56
|
@abstractmethod
|
|
43
57
|
def get_type(self) -> Type[T]:
|
|
44
58
|
pass
|
|
@@ -60,7 +74,10 @@ class AbstractInstanceProvider(ABC, Generic[T]):
|
|
|
60
74
|
pass
|
|
61
75
|
|
|
62
76
|
@abstractmethod
|
|
63
|
-
def resolve(self, context: Providers.Context)
|
|
77
|
+
def resolve(self, context: Providers.Context):
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
def check_factories(self):
|
|
64
81
|
pass
|
|
65
82
|
|
|
66
83
|
|
|
@@ -85,10 +102,22 @@ class InstanceProvider(AbstractInstanceProvider):
|
|
|
85
102
|
self.scope = scope
|
|
86
103
|
self.dependencies : Optional[list[AbstractInstanceProvider]] = None
|
|
87
104
|
|
|
105
|
+
# internal
|
|
106
|
+
|
|
107
|
+
def _is_resolved(self) -> bool:
|
|
108
|
+
return self.dependencies is not None
|
|
109
|
+
|
|
88
110
|
# implement AbstractInstanceProvider
|
|
89
111
|
|
|
90
|
-
def
|
|
91
|
-
return self
|
|
112
|
+
def get_host(self):
|
|
113
|
+
return self.host
|
|
114
|
+
|
|
115
|
+
def check_factories(self):
|
|
116
|
+
#register_factories(self.host)
|
|
117
|
+
pass
|
|
118
|
+
|
|
119
|
+
def resolve(self, context: Providers.Context):
|
|
120
|
+
pass
|
|
92
121
|
|
|
93
122
|
def get_module(self) -> str:
|
|
94
123
|
return self.host.__module__
|
|
@@ -178,11 +207,11 @@ class AmbiguousProvider(AbstractInstanceProvider):
|
|
|
178
207
|
def get_dependencies(self) -> list[AbstractInstanceProvider]:
|
|
179
208
|
return []
|
|
180
209
|
|
|
181
|
-
def resolve(self, context: Providers.Context)
|
|
182
|
-
|
|
210
|
+
def resolve(self, context: Providers.Context):
|
|
211
|
+
pass
|
|
183
212
|
|
|
184
213
|
def create(self, environment: Environment, *args):
|
|
185
|
-
raise
|
|
214
|
+
raise DIException(f"multiple candidates for type {self.type}")
|
|
186
215
|
|
|
187
216
|
def __str__(self):
|
|
188
217
|
return f"AmbiguousProvider({self.type})"
|
|
@@ -198,7 +227,7 @@ class Scopes:
|
|
|
198
227
|
def get(cls, scope: str, environment: Environment):
|
|
199
228
|
scope_type = Scopes.scopes.get(scope, None)
|
|
200
229
|
if scope_type is None:
|
|
201
|
-
raise
|
|
230
|
+
raise DIRegistrationException(f"unknown scope type {scope}")
|
|
202
231
|
|
|
203
232
|
return environment.get(scope_type)
|
|
204
233
|
|
|
@@ -245,7 +274,7 @@ class EnvironmentInstanceProvider(AbstractInstanceProvider):
|
|
|
245
274
|
|
|
246
275
|
# implement
|
|
247
276
|
|
|
248
|
-
def resolve(self, context: Providers.Context)
|
|
277
|
+
def resolve(self, context: Providers.Context):
|
|
249
278
|
pass # noop
|
|
250
279
|
|
|
251
280
|
def get_module(self) -> str:
|
|
@@ -266,7 +295,7 @@ class EnvironmentInstanceProvider(AbstractInstanceProvider):
|
|
|
266
295
|
for dependency in self.provider.get_dependencies():
|
|
267
296
|
instance_provider = providers.get(dependency.get_type(), None)
|
|
268
297
|
if instance_provider is None:
|
|
269
|
-
raise
|
|
298
|
+
raise DIRegistrationException(f"missing import for {dependency.get_type()} ")
|
|
270
299
|
|
|
271
300
|
self.dependencies.append(instance_provider)
|
|
272
301
|
|
|
@@ -297,22 +326,25 @@ class ClassInstanceProvider(InstanceProvider):
|
|
|
297
326
|
|
|
298
327
|
# implement
|
|
299
328
|
|
|
300
|
-
def
|
|
301
|
-
|
|
302
|
-
|
|
329
|
+
def check_factories(self):
|
|
330
|
+
register_factories(self.host)
|
|
331
|
+
|
|
332
|
+
def resolve(self, context: Providers.Context):
|
|
333
|
+
context.add(self)
|
|
303
334
|
|
|
304
|
-
|
|
335
|
+
if not self._is_resolved():
|
|
336
|
+
self.dependencies = []
|
|
305
337
|
|
|
306
338
|
# check constructor
|
|
307
339
|
|
|
308
340
|
init = TypeDescriptor.for_type(self.type).get_method("__init__")
|
|
309
341
|
if init is None:
|
|
310
|
-
raise
|
|
342
|
+
raise DIRegistrationException(f"{self.type.__name__} does not implement __init__")
|
|
311
343
|
|
|
312
344
|
for param in init.param_types:
|
|
313
345
|
provider = Providers.get_provider(param)
|
|
314
346
|
self.params += 1
|
|
315
|
-
if self.add_dependency(provider):
|
|
347
|
+
if self.add_dependency(provider): # a dependency can occur multiple times, e.g in __init__ and in an injected method
|
|
316
348
|
provider.resolve(context)
|
|
317
349
|
|
|
318
350
|
# check @inject
|
|
@@ -327,8 +359,6 @@ class ClassInstanceProvider(InstanceProvider):
|
|
|
327
359
|
else: # check if the dependencies create a cycle
|
|
328
360
|
context.add(*self.dependencies)
|
|
329
361
|
|
|
330
|
-
return self
|
|
331
|
-
|
|
332
362
|
def create(self, environment: Environment, *args):
|
|
333
363
|
Environment.logger.debug("%s create class %s", self, self.type.__qualname__)
|
|
334
364
|
|
|
@@ -357,11 +387,11 @@ class FunctionInstanceProvider(InstanceProvider):
|
|
|
357
387
|
|
|
358
388
|
# implement
|
|
359
389
|
|
|
360
|
-
def resolve(self, context: Providers.Context)
|
|
361
|
-
|
|
362
|
-
self.dependencies = []
|
|
390
|
+
def resolve(self, context: Providers.Context):
|
|
391
|
+
context.add(self)
|
|
363
392
|
|
|
364
|
-
|
|
393
|
+
if not self._is_resolved():
|
|
394
|
+
self.dependencies = []
|
|
365
395
|
|
|
366
396
|
provider = Providers.get_provider(self.host)
|
|
367
397
|
if self.add_dependency(provider):
|
|
@@ -369,8 +399,6 @@ class FunctionInstanceProvider(InstanceProvider):
|
|
|
369
399
|
else: # check if the dependencies crate a cycle
|
|
370
400
|
context.add(*self.dependencies)
|
|
371
401
|
|
|
372
|
-
return self
|
|
373
|
-
|
|
374
402
|
def create(self, environment: Environment, *args):
|
|
375
403
|
Environment.logger.debug("%s create class %s", self, self.type.__qualname__)
|
|
376
404
|
|
|
@@ -401,16 +429,15 @@ class FactoryInstanceProvider(InstanceProvider):
|
|
|
401
429
|
|
|
402
430
|
# implement
|
|
403
431
|
|
|
404
|
-
def resolve(self, context: Providers.Context)
|
|
405
|
-
|
|
406
|
-
self.dependencies = []
|
|
432
|
+
def resolve(self, context: Providers.Context):
|
|
433
|
+
context.add(self)
|
|
407
434
|
|
|
408
|
-
|
|
435
|
+
if not self._is_resolved():
|
|
436
|
+
self.dependencies = []
|
|
409
437
|
|
|
410
438
|
provider = Providers.get_provider(self.host)
|
|
411
439
|
if self.add_dependency(provider):
|
|
412
440
|
provider.resolve(context)
|
|
413
|
-
|
|
414
441
|
else: # check if the dependencies crate a cycle
|
|
415
442
|
context.add(*self.dependencies)
|
|
416
443
|
|
|
@@ -432,8 +459,10 @@ class Lifecycle(Enum):
|
|
|
432
459
|
|
|
433
460
|
__slots__ = []
|
|
434
461
|
|
|
435
|
-
|
|
436
|
-
|
|
462
|
+
ON_INJECT = 0
|
|
463
|
+
ON_INIT = 1
|
|
464
|
+
ON_RUNNING = 2
|
|
465
|
+
ON_DESTROY = 3
|
|
437
466
|
|
|
438
467
|
class LifecycleProcessor(ABC):
|
|
439
468
|
"""
|
|
@@ -486,7 +515,7 @@ class Providers:
|
|
|
486
515
|
def add(self, *providers: AbstractInstanceProvider):
|
|
487
516
|
for provider in providers:
|
|
488
517
|
if next((p for p in self.dependencies if p.get_type() is provider.get_type()), None) is not None:
|
|
489
|
-
raise
|
|
518
|
+
raise DIRegistrationException(self.cycle_report(provider))
|
|
490
519
|
|
|
491
520
|
self.dependencies.append(provider)
|
|
492
521
|
|
|
@@ -502,7 +531,7 @@ class Providers:
|
|
|
502
531
|
|
|
503
532
|
cycle += f"{p.get_type().__name__}"
|
|
504
533
|
|
|
505
|
-
cycle += f"
|
|
534
|
+
cycle += f" <> {provider.get_type().__name__}"
|
|
506
535
|
|
|
507
536
|
return cycle
|
|
508
537
|
|
|
@@ -538,13 +567,20 @@ class Providers:
|
|
|
538
567
|
return True
|
|
539
568
|
|
|
540
569
|
def cache_provider_for_type(provider: AbstractInstanceProvider, type: Type):
|
|
570
|
+
def location(provider: AbstractInstanceProvider):
|
|
571
|
+
host = provider.get_host()
|
|
572
|
+
file = inspect.getfile(host)
|
|
573
|
+
line = inspect.getsourcelines(host)[1]
|
|
574
|
+
|
|
575
|
+
return f"{file}:{line}"
|
|
576
|
+
|
|
541
577
|
existing_provider = Providers.cache.get(type)
|
|
542
578
|
if existing_provider is None:
|
|
543
579
|
Providers.cache[type] = provider
|
|
544
580
|
|
|
545
581
|
else:
|
|
546
582
|
if type is provider.get_type():
|
|
547
|
-
raise
|
|
583
|
+
raise DIRegistrationException(f"{type} already registered in {location(existing_provider)}, override in {location(provider)}")
|
|
548
584
|
|
|
549
585
|
if isinstance(existing_provider, AmbiguousProvider):
|
|
550
586
|
cast(AmbiguousProvider, existing_provider).add_provider(provider)
|
|
@@ -569,12 +605,14 @@ class Providers:
|
|
|
569
605
|
|
|
570
606
|
@classmethod
|
|
571
607
|
def resolve(cls):
|
|
572
|
-
for provider in Providers.check:
|
|
573
|
-
provider.resolve(Providers.Context())
|
|
574
608
|
|
|
575
|
-
Providers.check
|
|
609
|
+
for check in Providers.check:
|
|
610
|
+
check.check_factories()
|
|
576
611
|
|
|
577
|
-
#
|
|
612
|
+
# in the loop additional providers may be added
|
|
613
|
+
while len(Providers.check) > 0:
|
|
614
|
+
check = Providers.check.pop(0)
|
|
615
|
+
check.resolve(Providers.Context())
|
|
578
616
|
|
|
579
617
|
@classmethod
|
|
580
618
|
def report(cls):
|
|
@@ -585,7 +623,7 @@ class Providers:
|
|
|
585
623
|
def get_provider(cls, type: Type) -> AbstractInstanceProvider:
|
|
586
624
|
provider = Providers.cache.get(type, None)
|
|
587
625
|
if provider is None:
|
|
588
|
-
raise
|
|
626
|
+
raise DIException(f"{type.__name__} not registered as injectable")
|
|
589
627
|
|
|
590
628
|
return provider
|
|
591
629
|
|
|
@@ -595,7 +633,11 @@ def register_factories(cls: Type):
|
|
|
595
633
|
for method in descriptor.get_methods():
|
|
596
634
|
if method.has_decorator(create):
|
|
597
635
|
create_decorator = method.get_decorator(create)
|
|
598
|
-
|
|
636
|
+
return_type = method.return_type
|
|
637
|
+
if return_type is None:
|
|
638
|
+
raise DIRegistrationException(f"{cls.__name__}.{method.method.__name__} expected to have a return type")
|
|
639
|
+
|
|
640
|
+
Providers.register(FunctionInstanceProvider(cls, method.method, return_type, create_decorator.args[0],
|
|
599
641
|
create_decorator.args[1]))
|
|
600
642
|
def order(prio = 0):
|
|
601
643
|
def decorator(cls):
|
|
@@ -614,8 +656,6 @@ def injectable(eager=True, scope="singleton"):
|
|
|
614
656
|
|
|
615
657
|
Providers.register(ClassInstanceProvider(cls, eager, scope))
|
|
616
658
|
|
|
617
|
-
#TODO registerFactories(cls)
|
|
618
|
-
|
|
619
659
|
return cls
|
|
620
660
|
|
|
621
661
|
return decorator
|
|
@@ -653,6 +693,15 @@ def on_init():
|
|
|
653
693
|
|
|
654
694
|
return decorator
|
|
655
695
|
|
|
696
|
+
def on_running():
|
|
697
|
+
"""
|
|
698
|
+
Methods annotated with @on_running will be called when the container up and running."""
|
|
699
|
+
def decorator(func):
|
|
700
|
+
Decorators.add(func, on_running)
|
|
701
|
+
return func
|
|
702
|
+
|
|
703
|
+
return decorator
|
|
704
|
+
|
|
656
705
|
def on_destroy():
|
|
657
706
|
"""
|
|
658
707
|
Methods annotated with @on_destroy will be called when the instance is destroyed.
|
|
@@ -677,8 +726,6 @@ def environment(imports: Optional[list[Type]] = None):
|
|
|
677
726
|
Decorators.add(cls, environment, imports)
|
|
678
727
|
Decorators.add(cls, injectable) # do we need that?
|
|
679
728
|
|
|
680
|
-
register_factories(cls)
|
|
681
|
-
|
|
682
729
|
return cls
|
|
683
730
|
|
|
684
731
|
return decorator
|
|
@@ -736,8 +783,8 @@ class Environment:
|
|
|
736
783
|
|
|
737
784
|
self.type = env
|
|
738
785
|
self.parent = parent
|
|
739
|
-
if self.parent is None and env is not
|
|
740
|
-
self.parent =
|
|
786
|
+
if self.parent is None and env is not Boot:
|
|
787
|
+
self.parent = Boot.get_environment() # inherit environment including its manged instances!
|
|
741
788
|
|
|
742
789
|
self.providers: Dict[Type, AbstractInstanceProvider] = {}
|
|
743
790
|
self.lifecycle_processors: list[LifecycleProcessor] = []
|
|
@@ -750,7 +797,7 @@ class Environment:
|
|
|
750
797
|
|
|
751
798
|
Environment.instance = self
|
|
752
799
|
|
|
753
|
-
# resolve providers on a static basis. This is
|
|
800
|
+
# resolve providers on a static basis. This is executed for all new providers ( in case of new modules multiple times) !
|
|
754
801
|
|
|
755
802
|
Providers.resolve()
|
|
756
803
|
|
|
@@ -763,7 +810,7 @@ class Environment:
|
|
|
763
810
|
|
|
764
811
|
# bootstrapping hack, they will be overwritten by the "real" providers
|
|
765
812
|
|
|
766
|
-
if env is
|
|
813
|
+
if env is Boot:
|
|
767
814
|
add_provider(SingletonScope, SingletonScopeInstanceProvider())
|
|
768
815
|
add_provider(RequestScope, RequestScopeInstanceProvider())
|
|
769
816
|
|
|
@@ -777,7 +824,7 @@ class Environment:
|
|
|
777
824
|
|
|
778
825
|
decorator = TypeDescriptor.for_type(env).get_decorator(environment)
|
|
779
826
|
if decorator is None:
|
|
780
|
-
raise
|
|
827
|
+
raise DIRegistrationException(f"{env.__name__} is not an environment class")
|
|
781
828
|
|
|
782
829
|
scan = env.__module__
|
|
783
830
|
if "." in scan:
|
|
@@ -794,7 +841,7 @@ class Environment:
|
|
|
794
841
|
|
|
795
842
|
# register providers
|
|
796
843
|
|
|
797
|
-
# make sure, that for every type
|
|
844
|
+
# make sure, that for every type only a single EnvironmentInstanceProvider is created!
|
|
798
845
|
# otherwise inheritance will fuck it up
|
|
799
846
|
|
|
800
847
|
environment_providers : dict[AbstractInstanceProvider, EnvironmentInstanceProvider] = {}
|
|
@@ -823,6 +870,12 @@ class Environment:
|
|
|
823
870
|
for provider in load_environment(env):
|
|
824
871
|
if provider.is_eager():
|
|
825
872
|
provider.create(self)
|
|
873
|
+
|
|
874
|
+
# running callback
|
|
875
|
+
|
|
876
|
+
for instance in self.instances:
|
|
877
|
+
self.execute_processors(Lifecycle.ON_RUNNING, instance)
|
|
878
|
+
|
|
826
879
|
# internal
|
|
827
880
|
|
|
828
881
|
def execute_processors(self, lifecycle: Lifecycle, instance: T) -> T:
|
|
@@ -847,10 +900,54 @@ class Environment:
|
|
|
847
900
|
|
|
848
901
|
# execute processors
|
|
849
902
|
|
|
850
|
-
|
|
903
|
+
self.execute_processors(Lifecycle.ON_INJECT, instance)
|
|
904
|
+
self.execute_processors(Lifecycle.ON_INIT, instance)
|
|
905
|
+
|
|
906
|
+
return instance
|
|
851
907
|
|
|
852
908
|
# public
|
|
853
909
|
|
|
910
|
+
def report(self):
|
|
911
|
+
builder = StringBuilder()
|
|
912
|
+
|
|
913
|
+
builder.append(f"Environment {self.type.__name__}")
|
|
914
|
+
|
|
915
|
+
if self.parent is not None:
|
|
916
|
+
builder.append(f" parent {self.parent.type.__name__}")
|
|
917
|
+
|
|
918
|
+
builder.append("\n")
|
|
919
|
+
|
|
920
|
+
# post processors
|
|
921
|
+
|
|
922
|
+
builder.append("Processors \n")
|
|
923
|
+
for processor in self.lifecycle_processors:
|
|
924
|
+
builder.append(f"- {processor.__class__.__name__}\n")
|
|
925
|
+
|
|
926
|
+
# providers
|
|
927
|
+
|
|
928
|
+
builder.append("Providers \n")
|
|
929
|
+
for result_type, provider in self.providers.items():
|
|
930
|
+
if cast(EnvironmentInstanceProvider, provider).environment is self:
|
|
931
|
+
builder.append(f"- {result_type.__name__}: {cast(EnvironmentInstanceProvider, provider).provider}\n")
|
|
932
|
+
|
|
933
|
+
# instances
|
|
934
|
+
|
|
935
|
+
builder.append("Instances \n")
|
|
936
|
+
|
|
937
|
+
result = {}
|
|
938
|
+
for obj in self.instances:
|
|
939
|
+
cls = type(obj)
|
|
940
|
+
result[cls] = result.get(cls, 0) + 1
|
|
941
|
+
|
|
942
|
+
for cls, count in result.items():
|
|
943
|
+
builder.append(f"- {cls.__name__}: {count} \n")
|
|
944
|
+
|
|
945
|
+
# done
|
|
946
|
+
|
|
947
|
+
result = str(builder)
|
|
948
|
+
|
|
949
|
+
return result
|
|
950
|
+
|
|
854
951
|
def destroy(self):
|
|
855
952
|
"""
|
|
856
953
|
destroy all managed instances by calling the appropriate lifecycle methods
|
|
@@ -862,7 +959,7 @@ class Environment:
|
|
|
862
959
|
|
|
863
960
|
def get(self, type: Type[T]) -> T:
|
|
864
961
|
"""
|
|
865
|
-
|
|
962
|
+
Create or return a cached instance for the given type.
|
|
866
963
|
|
|
867
964
|
Arguments:
|
|
868
965
|
type (Type): The desired type
|
|
@@ -872,7 +969,7 @@ class Environment:
|
|
|
872
969
|
provider = self.providers.get(type, None)
|
|
873
970
|
if provider is None:
|
|
874
971
|
Environment.logger.error("%s is not supported", type)
|
|
875
|
-
raise
|
|
972
|
+
raise DIRuntimeException(f"{type} is not supported")
|
|
876
973
|
|
|
877
974
|
return provider.create(self)
|
|
878
975
|
|
|
@@ -883,22 +980,21 @@ class LifecycleCallable:
|
|
|
883
980
|
"order"
|
|
884
981
|
]
|
|
885
982
|
|
|
886
|
-
def __init__(self, decorator,
|
|
983
|
+
def __init__(self, decorator, lifecycle: Lifecycle):
|
|
887
984
|
self.decorator = decorator
|
|
888
985
|
self.lifecycle = lifecycle
|
|
889
986
|
self.order = 0
|
|
890
987
|
|
|
891
988
|
if TypeDescriptor.for_type(type(self)).has_decorator(order):
|
|
892
|
-
self.order =
|
|
989
|
+
self.order = TypeDescriptor.for_type(type(self)).get_decorator(order).args[0]
|
|
893
990
|
|
|
894
|
-
|
|
991
|
+
AbstractCallableProcessor.register(self)
|
|
895
992
|
|
|
896
993
|
def args(self, decorator: DecoratorDescriptor, method: TypeDescriptor.MethodDescriptor, environment: Environment):
|
|
897
994
|
return []
|
|
898
995
|
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
class CallableProcessor(LifecycleProcessor):
|
|
996
|
+
|
|
997
|
+
class AbstractCallableProcessor(LifecycleProcessor):
|
|
902
998
|
# local classes
|
|
903
999
|
|
|
904
1000
|
class MethodCall:
|
|
@@ -921,74 +1017,127 @@ class CallableProcessor(LifecycleProcessor):
|
|
|
921
1017
|
def __str__(self):
|
|
922
1018
|
return f"MethodCall({self.method.method.__name__})"
|
|
923
1019
|
|
|
924
|
-
#
|
|
1020
|
+
# static data
|
|
925
1021
|
|
|
926
|
-
|
|
927
|
-
|
|
1022
|
+
lock = threading.RLock()
|
|
1023
|
+
callables : Dict[object, LifecycleCallable] = {}
|
|
1024
|
+
cache : Dict[Type, list[list[AbstractCallableProcessor.MethodCall]]] = {}
|
|
1025
|
+
|
|
1026
|
+
# static methods
|
|
928
1027
|
|
|
929
|
-
|
|
930
|
-
|
|
1028
|
+
@classmethod
|
|
1029
|
+
def register(cls, callable: LifecycleCallable):
|
|
1030
|
+
AbstractCallableProcessor.callables[callable.decorator] = callable
|
|
931
1031
|
|
|
932
|
-
|
|
1032
|
+
@classmethod
|
|
1033
|
+
def compute_callables(cls, type: Type) -> list[list[AbstractCallableProcessor.MethodCall]]:
|
|
933
1034
|
descriptor = TypeDescriptor.for_type(type)
|
|
934
1035
|
|
|
935
|
-
result = []
|
|
1036
|
+
result = [[], [], [], []] # per lifecycle
|
|
936
1037
|
|
|
937
1038
|
for method in descriptor.get_methods():
|
|
938
1039
|
for decorator in method.decorators:
|
|
939
|
-
|
|
940
|
-
|
|
1040
|
+
callable = AbstractCallableProcessor.callables.get(decorator.decorator)
|
|
1041
|
+
if callable is not None: # any callable for this decorator?
|
|
1042
|
+
result[callable.lifecycle.value].append(
|
|
1043
|
+
AbstractCallableProcessor.MethodCall(method, decorator, callable))
|
|
941
1044
|
|
|
942
1045
|
# sort according to order
|
|
943
1046
|
|
|
944
|
-
result.sort(key=lambda call: call.lifecycle_callable.order)
|
|
1047
|
+
result[0].sort(key=lambda call: call.lifecycle_callable.order)
|
|
1048
|
+
result[1].sort(key=lambda call: call.lifecycle_callable.order)
|
|
1049
|
+
result[2].sort(key=lambda call: call.lifecycle_callable.order)
|
|
1050
|
+
result[3].sort(key=lambda call: call.lifecycle_callable.order)
|
|
945
1051
|
|
|
946
1052
|
# done
|
|
947
1053
|
|
|
948
1054
|
return result
|
|
949
1055
|
|
|
950
|
-
|
|
951
|
-
|
|
1056
|
+
@classmethod
|
|
1057
|
+
def callables_for(cls, type: Type) -> list[list[AbstractCallableProcessor.MethodCall]]:
|
|
1058
|
+
callables = AbstractCallableProcessor.cache.get(type, None)
|
|
952
1059
|
if callables is None:
|
|
953
|
-
|
|
954
|
-
|
|
1060
|
+
with AbstractCallableProcessor.lock:
|
|
1061
|
+
callables = AbstractCallableProcessor.cache.get(type, None)
|
|
1062
|
+
if callables is None:
|
|
1063
|
+
callables = AbstractCallableProcessor.compute_callables(type)
|
|
1064
|
+
AbstractCallableProcessor.cache[type] = callables
|
|
955
1065
|
|
|
956
1066
|
return callables
|
|
957
1067
|
|
|
958
|
-
|
|
959
|
-
|
|
1068
|
+
# constructor
|
|
1069
|
+
|
|
1070
|
+
def __init__(self, lifecycle: Lifecycle):
|
|
1071
|
+
super().__init__()
|
|
1072
|
+
|
|
1073
|
+
self.lifecycle = lifecycle
|
|
960
1074
|
|
|
961
1075
|
# implement
|
|
962
1076
|
|
|
963
1077
|
def process_lifecycle(self, lifecycle: Lifecycle, instance: object, environment: Environment) -> object:
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1078
|
+
if lifecycle is self.lifecycle:
|
|
1079
|
+
callables = self.callables_for(type(instance))
|
|
1080
|
+
|
|
1081
|
+
for callable in callables[lifecycle.value]:
|
|
967
1082
|
callable.execute(instance, environment)
|
|
968
1083
|
|
|
1084
|
+
@injectable()
|
|
1085
|
+
@order(1)
|
|
1086
|
+
class OnInjectCallableProcessor(AbstractCallableProcessor):
|
|
1087
|
+
def __init__(self):
|
|
1088
|
+
super().__init__(Lifecycle.ON_INJECT)
|
|
1089
|
+
|
|
1090
|
+
@injectable()
|
|
1091
|
+
@order(2)
|
|
1092
|
+
class OnInitCallableProcessor(AbstractCallableProcessor):
|
|
1093
|
+
def __init__(self):
|
|
1094
|
+
super().__init__(Lifecycle.ON_INIT)
|
|
1095
|
+
|
|
1096
|
+
@injectable()
|
|
1097
|
+
@order(3)
|
|
1098
|
+
class OnRunningCallableProcessor(AbstractCallableProcessor):
|
|
1099
|
+
def __init__(self):
|
|
1100
|
+
super().__init__(Lifecycle.ON_RUNNING)
|
|
1101
|
+
|
|
1102
|
+
@injectable()
|
|
1103
|
+
@order(4)
|
|
1104
|
+
class OnDestroyCallableProcessor(AbstractCallableProcessor):
|
|
1105
|
+
def __init__(self):
|
|
1106
|
+
super().__init__(Lifecycle.ON_DESTROY)
|
|
1107
|
+
|
|
1108
|
+
# the callables
|
|
1109
|
+
|
|
969
1110
|
@injectable()
|
|
970
1111
|
@order(1000)
|
|
971
1112
|
class OnInitLifecycleCallable(LifecycleCallable):
|
|
972
1113
|
__slots__ = []
|
|
973
1114
|
|
|
974
|
-
def __init__(self
|
|
975
|
-
super().__init__(on_init,
|
|
1115
|
+
def __init__(self):
|
|
1116
|
+
super().__init__(on_init, Lifecycle.ON_INIT)
|
|
976
1117
|
|
|
977
1118
|
@injectable()
|
|
978
1119
|
@order(1001)
|
|
979
1120
|
class OnDestroyLifecycleCallable(LifecycleCallable):
|
|
980
1121
|
__slots__ = []
|
|
981
1122
|
|
|
982
|
-
def __init__(self
|
|
983
|
-
super().__init__(on_destroy,
|
|
1123
|
+
def __init__(self):
|
|
1124
|
+
super().__init__(on_destroy, Lifecycle.ON_DESTROY)
|
|
1125
|
+
|
|
1126
|
+
@injectable()
|
|
1127
|
+
@order(1002)
|
|
1128
|
+
class OnRunningLifecycleCallable(LifecycleCallable):
|
|
1129
|
+
__slots__ = []
|
|
1130
|
+
|
|
1131
|
+
def __init__(self):
|
|
1132
|
+
super().__init__(on_running, Lifecycle.ON_RUNNING)
|
|
984
1133
|
|
|
985
1134
|
@injectable()
|
|
986
1135
|
@order(9)
|
|
987
1136
|
class EnvironmentAwareLifecycleCallable(LifecycleCallable):
|
|
988
1137
|
__slots__ = []
|
|
989
1138
|
|
|
990
|
-
def __init__(self
|
|
991
|
-
super().__init__(inject_environment,
|
|
1139
|
+
def __init__(self):
|
|
1140
|
+
super().__init__(inject_environment, Lifecycle.ON_INJECT)
|
|
992
1141
|
|
|
993
1142
|
def args(self, decorator: DecoratorDescriptor, method: TypeDescriptor.MethodDescriptor, environment: Environment):
|
|
994
1143
|
return [environment]
|
|
@@ -998,8 +1147,8 @@ class EnvironmentAwareLifecycleCallable(LifecycleCallable):
|
|
|
998
1147
|
class InjectLifecycleCallable(LifecycleCallable):
|
|
999
1148
|
__slots__ = []
|
|
1000
1149
|
|
|
1001
|
-
def __init__(self
|
|
1002
|
-
super().__init__(inject,
|
|
1150
|
+
def __init__(self):
|
|
1151
|
+
super().__init__(inject, Lifecycle.ON_INJECT)
|
|
1003
1152
|
|
|
1004
1153
|
# override
|
|
1005
1154
|
|
|
@@ -1046,7 +1195,7 @@ class SingletonScope(Scope):
|
|
|
1046
1195
|
super().__init__()
|
|
1047
1196
|
|
|
1048
1197
|
self.value = None
|
|
1049
|
-
self.lock = threading.
|
|
1198
|
+
self.lock = threading.RLock()
|
|
1050
1199
|
|
|
1051
1200
|
# override
|
|
1052
1201
|
|
|
@@ -1058,20 +1207,37 @@ class SingletonScope(Scope):
|
|
|
1058
1207
|
|
|
1059
1208
|
return self.value
|
|
1060
1209
|
|
|
1210
|
+
@scope("thread")
|
|
1211
|
+
class ThreadScope(Scope):
|
|
1212
|
+
__slots__ = [
|
|
1213
|
+
"_local"
|
|
1214
|
+
]
|
|
1215
|
+
|
|
1216
|
+
# constructor
|
|
1217
|
+
|
|
1218
|
+
def __init__(self):
|
|
1219
|
+
super().__init__()
|
|
1220
|
+
self._local = threading.local()
|
|
1221
|
+
|
|
1222
|
+
def get(self, provider: AbstractInstanceProvider, environment: Environment, arg_provider: Callable[[], list]):
|
|
1223
|
+
if not hasattr(self._local, "value"):
|
|
1224
|
+
self._local.value = provider.create(environment, *arg_provider())
|
|
1225
|
+
return self._local.value
|
|
1226
|
+
|
|
1061
1227
|
# internal class that is required to import technical instance providers
|
|
1062
1228
|
|
|
1063
1229
|
@environment()
|
|
1064
|
-
class
|
|
1230
|
+
class Boot:
|
|
1065
1231
|
# class
|
|
1066
1232
|
|
|
1067
1233
|
environment = None
|
|
1068
1234
|
|
|
1069
1235
|
@classmethod
|
|
1070
|
-
def
|
|
1071
|
-
if
|
|
1072
|
-
|
|
1236
|
+
def get_environment(cls):
|
|
1237
|
+
if Boot.environment is None:
|
|
1238
|
+
Boot.environment = Environment(Boot)
|
|
1073
1239
|
|
|
1074
|
-
return
|
|
1240
|
+
return Boot.environment
|
|
1075
1241
|
|
|
1076
1242
|
# properties
|
|
1077
1243
|
|