aspyx 1.0.0__py3-none-any.whl → 1.1.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 +9 -5
- aspyx/di/aop/__init__.py +4 -1
- aspyx/di/aop/aop.py +56 -76
- aspyx/di/configuration/__init__.py +4 -1
- aspyx/di/configuration/configuration.py +22 -17
- aspyx/di/di.py +280 -171
- aspyx/di/util/__init__.py +8 -0
- aspyx/di/util/stringbuilder.py +31 -0
- aspyx/reflection/__init__.py +4 -1
- aspyx/reflection/proxy.py +10 -7
- aspyx/reflection/reflection.py +38 -25
- {aspyx-1.0.0.dist-info → aspyx-1.1.0.dist-info}/METADATA +126 -38
- aspyx-1.1.0.dist-info/RECORD +16 -0
- aspyx-1.0.0.dist-info/RECORD +0 -14
- {aspyx-1.0.0.dist-info → aspyx-1.1.0.dist-info}/WHEEL +0 -0
- {aspyx-1.0.0.dist-info → aspyx-1.1.0.dist-info}/licenses/LICENSE +0 -0
- {aspyx-1.0.0.dist-info → aspyx-1.1.0.dist-info}/top_level.txt +0 -0
aspyx/di/di.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The deoendency injection module provides a framework for managing dependencies and lifecycle of objects in Python applications.
|
|
3
|
+
"""
|
|
1
4
|
from __future__ import annotations
|
|
2
5
|
|
|
3
6
|
import inspect
|
|
@@ -8,6 +11,7 @@ from enum import Enum, auto
|
|
|
8
11
|
import threading
|
|
9
12
|
from typing import Type, Dict, TypeVar, Generic, Optional, cast, Callable
|
|
10
13
|
|
|
14
|
+
from aspyx.di.util import StringBuilder
|
|
11
15
|
from aspyx.reflection import Decorators, TypeDescriptor, DecoratorDescriptor
|
|
12
16
|
|
|
13
17
|
T = TypeVar("T")
|
|
@@ -25,8 +29,8 @@ class Factory(ABC, Generic[T]):
|
|
|
25
29
|
|
|
26
30
|
class InjectorException(Exception):
|
|
27
31
|
"""
|
|
28
|
-
Exception raised for errors in the injector.
|
|
29
|
-
|
|
32
|
+
Exception raised for errors in the injector.
|
|
33
|
+
"""
|
|
30
34
|
|
|
31
35
|
class AbstractInstanceProvider(ABC, Generic[T]):
|
|
32
36
|
"""
|
|
@@ -53,11 +57,11 @@ class AbstractInstanceProvider(ABC, Generic[T]):
|
|
|
53
57
|
pass
|
|
54
58
|
|
|
55
59
|
@abstractmethod
|
|
56
|
-
def create(self,
|
|
60
|
+
def create(self, environment: Environment, *args):
|
|
57
61
|
pass
|
|
58
62
|
|
|
59
63
|
@abstractmethod
|
|
60
|
-
def resolve(self, context: Providers.Context)
|
|
64
|
+
def resolve(self, context: Providers.Context):
|
|
61
65
|
pass
|
|
62
66
|
|
|
63
67
|
|
|
@@ -82,10 +86,15 @@ class InstanceProvider(AbstractInstanceProvider):
|
|
|
82
86
|
self.scope = scope
|
|
83
87
|
self.dependencies : Optional[list[AbstractInstanceProvider]] = None
|
|
84
88
|
|
|
89
|
+
# internal
|
|
90
|
+
|
|
91
|
+
def _is_resolved(self) -> bool:
|
|
92
|
+
return self.dependencies is not None
|
|
93
|
+
|
|
85
94
|
# implement AbstractInstanceProvider
|
|
86
95
|
|
|
87
|
-
def resolve(self, context: Providers.Context)
|
|
88
|
-
|
|
96
|
+
def resolve(self, context: Providers.Context):
|
|
97
|
+
pass
|
|
89
98
|
|
|
90
99
|
def get_module(self) -> str:
|
|
91
100
|
return self.host.__module__
|
|
@@ -175,8 +184,8 @@ class AmbiguousProvider(AbstractInstanceProvider):
|
|
|
175
184
|
def get_dependencies(self) -> list[AbstractInstanceProvider]:
|
|
176
185
|
return []
|
|
177
186
|
|
|
178
|
-
def resolve(self, context: Providers.Context)
|
|
179
|
-
|
|
187
|
+
def resolve(self, context: Providers.Context):
|
|
188
|
+
pass
|
|
180
189
|
|
|
181
190
|
def create(self, environment: Environment, *args):
|
|
182
191
|
raise InjectorException(f"multiple candidates for type {self.type}")
|
|
@@ -193,15 +202,15 @@ class Scopes:
|
|
|
193
202
|
|
|
194
203
|
@classmethod
|
|
195
204
|
def get(cls, scope: str, environment: Environment):
|
|
196
|
-
|
|
197
|
-
if
|
|
205
|
+
scope_type = Scopes.scopes.get(scope, None)
|
|
206
|
+
if scope_type is None:
|
|
198
207
|
raise InjectorException(f"unknown scope type {scope}")
|
|
199
208
|
|
|
200
|
-
return environment.get(
|
|
209
|
+
return environment.get(scope_type)
|
|
201
210
|
|
|
202
211
|
@classmethod
|
|
203
|
-
def register(cls,
|
|
204
|
-
Scopes.scopes[name] =
|
|
212
|
+
def register(cls, scope_type: Type, name: str):
|
|
213
|
+
Scopes.scopes[name] = scope_type
|
|
205
214
|
|
|
206
215
|
class Scope:
|
|
207
216
|
# properties
|
|
@@ -216,15 +225,15 @@ class Scope:
|
|
|
216
225
|
|
|
217
226
|
# public
|
|
218
227
|
|
|
219
|
-
def get(self, provider: AbstractInstanceProvider, environment: Environment,
|
|
220
|
-
return provider.create(environment, *
|
|
228
|
+
def get(self, provider: AbstractInstanceProvider, environment: Environment, arg_provider: Callable[[],list]):
|
|
229
|
+
return provider.create(environment, *arg_provider())
|
|
221
230
|
|
|
222
231
|
class EnvironmentInstanceProvider(AbstractInstanceProvider):
|
|
223
232
|
# properties
|
|
224
233
|
|
|
225
234
|
__slots__ = [
|
|
226
235
|
"environment",
|
|
227
|
-
"
|
|
236
|
+
"scope_instance",
|
|
228
237
|
"provider",
|
|
229
238
|
"dependencies",
|
|
230
239
|
]
|
|
@@ -238,12 +247,11 @@ class EnvironmentInstanceProvider(AbstractInstanceProvider):
|
|
|
238
247
|
self.provider = provider
|
|
239
248
|
self.dependencies : list[AbstractInstanceProvider] = []
|
|
240
249
|
|
|
241
|
-
self.
|
|
242
|
-
print()
|
|
250
|
+
self.scope_instance = Scopes.get(provider.get_scope(), environment)
|
|
243
251
|
|
|
244
252
|
# implement
|
|
245
253
|
|
|
246
|
-
def resolve(self, context: Providers.Context)
|
|
254
|
+
def resolve(self, context: Providers.Context):
|
|
247
255
|
pass # noop
|
|
248
256
|
|
|
249
257
|
def get_module(self) -> str:
|
|
@@ -260,21 +268,19 @@ class EnvironmentInstanceProvider(AbstractInstanceProvider):
|
|
|
260
268
|
|
|
261
269
|
# custom logic
|
|
262
270
|
|
|
263
|
-
def
|
|
271
|
+
def tweak_dependencies(self, providers: dict[Type, AbstractInstanceProvider]):
|
|
264
272
|
for dependency in self.provider.get_dependencies():
|
|
265
|
-
|
|
266
|
-
if
|
|
273
|
+
instance_provider = providers.get(dependency.get_type(), None)
|
|
274
|
+
if instance_provider is None:
|
|
267
275
|
raise InjectorException(f"missing import for {dependency.get_type()} ")
|
|
268
276
|
|
|
269
|
-
self.dependencies.append(
|
|
270
|
-
pass
|
|
271
|
-
pass
|
|
277
|
+
self.dependencies.append(instance_provider)
|
|
272
278
|
|
|
273
279
|
def get_dependencies(self) -> list[AbstractInstanceProvider]:
|
|
274
280
|
return self.provider.get_dependencies()
|
|
275
281
|
|
|
276
|
-
def create(self,
|
|
277
|
-
return self.
|
|
282
|
+
def create(self, environment: Environment, *args):
|
|
283
|
+
return self.scope_instance.get(self.provider, self.environment, lambda: [provider.create(environment) for provider in self.dependencies]) # already scope property!
|
|
278
284
|
|
|
279
285
|
def __str__(self):
|
|
280
286
|
return f"EnvironmentInstanceProvider({self.provider})"
|
|
@@ -297,11 +303,11 @@ class ClassInstanceProvider(InstanceProvider):
|
|
|
297
303
|
|
|
298
304
|
# implement
|
|
299
305
|
|
|
300
|
-
def resolve(self, context: Providers.Context)
|
|
301
|
-
|
|
302
|
-
self.dependencies = []
|
|
306
|
+
def resolve(self, context: Providers.Context):
|
|
307
|
+
context.add(self)
|
|
303
308
|
|
|
304
|
-
|
|
309
|
+
if not self._is_resolved():
|
|
310
|
+
self.dependencies = []
|
|
305
311
|
|
|
306
312
|
# check constructor
|
|
307
313
|
|
|
@@ -309,28 +315,26 @@ class ClassInstanceProvider(InstanceProvider):
|
|
|
309
315
|
if init is None:
|
|
310
316
|
raise InjectorException(f"{self.type.__name__} does not implement __init__")
|
|
311
317
|
|
|
312
|
-
for param in init.
|
|
313
|
-
provider = Providers.
|
|
318
|
+
for param in init.param_types:
|
|
319
|
+
provider = Providers.get_provider(param)
|
|
314
320
|
self.params += 1
|
|
315
|
-
if self.add_dependency(provider):
|
|
321
|
+
if self.add_dependency(provider): # a dependency can occur multiple times, e.g in __init__ and in an injected method
|
|
316
322
|
provider.resolve(context)
|
|
317
323
|
|
|
318
324
|
# check @inject
|
|
319
325
|
|
|
320
|
-
for method in TypeDescriptor.for_type(self.type).
|
|
326
|
+
for method in TypeDescriptor.for_type(self.type).get_methods():
|
|
321
327
|
if method.has_decorator(inject):
|
|
322
|
-
for param in method.
|
|
323
|
-
provider = Providers.
|
|
328
|
+
for param in method.param_types:
|
|
329
|
+
provider = Providers.get_provider(param)
|
|
324
330
|
|
|
325
331
|
if self.add_dependency(provider):
|
|
326
332
|
provider.resolve(context)
|
|
327
333
|
else: # check if the dependencies create a cycle
|
|
328
334
|
context.add(*self.dependencies)
|
|
329
335
|
|
|
330
|
-
return self
|
|
331
|
-
|
|
332
336
|
def create(self, environment: Environment, *args):
|
|
333
|
-
Environment.logger.debug(
|
|
337
|
+
Environment.logger.debug("%s create class %s", self, self.type.__qualname__)
|
|
334
338
|
|
|
335
339
|
return environment.created(self.type(*args[:self.params]))
|
|
336
340
|
|
|
@@ -357,22 +361,20 @@ class FunctionInstanceProvider(InstanceProvider):
|
|
|
357
361
|
|
|
358
362
|
# implement
|
|
359
363
|
|
|
360
|
-
def resolve(self, context: Providers.Context)
|
|
361
|
-
|
|
362
|
-
self.dependencies = []
|
|
364
|
+
def resolve(self, context: Providers.Context):
|
|
365
|
+
context.add(self)
|
|
363
366
|
|
|
364
|
-
|
|
367
|
+
if not self._is_resolved():
|
|
368
|
+
self.dependencies = []
|
|
365
369
|
|
|
366
|
-
provider = Providers.
|
|
370
|
+
provider = Providers.get_provider(self.host)
|
|
367
371
|
if self.add_dependency(provider):
|
|
368
372
|
provider.resolve(context)
|
|
369
373
|
else: # check if the dependencies crate a cycle
|
|
370
374
|
context.add(*self.dependencies)
|
|
371
375
|
|
|
372
|
-
return self
|
|
373
|
-
|
|
374
376
|
def create(self, environment: Environment, *args):
|
|
375
|
-
Environment.logger.debug(
|
|
377
|
+
Environment.logger.debug("%s create class %s", self, self.type.__qualname__)
|
|
376
378
|
|
|
377
379
|
instance = self.method(*args)
|
|
378
380
|
|
|
@@ -391,23 +393,23 @@ class FactoryInstanceProvider(InstanceProvider):
|
|
|
391
393
|
# class method
|
|
392
394
|
|
|
393
395
|
@classmethod
|
|
394
|
-
def
|
|
395
|
-
return TypeDescriptor.for_type(clazz).
|
|
396
|
+
def get_factory_type(cls, clazz):
|
|
397
|
+
return TypeDescriptor.for_type(clazz).get_method("create", local=True).return_type
|
|
396
398
|
|
|
397
399
|
# constructor
|
|
398
400
|
|
|
399
401
|
def __init__(self, factory: Type, eager: bool, scope: str):
|
|
400
|
-
super().__init__(factory, FactoryInstanceProvider.
|
|
402
|
+
super().__init__(factory, FactoryInstanceProvider.get_factory_type(factory), eager, scope)
|
|
401
403
|
|
|
402
404
|
# implement
|
|
403
405
|
|
|
404
|
-
def resolve(self, context: Providers.Context)
|
|
405
|
-
|
|
406
|
-
self.dependencies = []
|
|
406
|
+
def resolve(self, context: Providers.Context):
|
|
407
|
+
context.add(self)
|
|
407
408
|
|
|
408
|
-
|
|
409
|
+
if not self._is_resolved():
|
|
410
|
+
self.dependencies = []
|
|
409
411
|
|
|
410
|
-
provider = Providers.
|
|
412
|
+
provider = Providers.get_provider(self.host)
|
|
411
413
|
if self.add_dependency(provider):
|
|
412
414
|
provider.resolve(context)
|
|
413
415
|
|
|
@@ -417,7 +419,7 @@ class FactoryInstanceProvider(InstanceProvider):
|
|
|
417
419
|
return self
|
|
418
420
|
|
|
419
421
|
def create(self, environment: Environment, *args):
|
|
420
|
-
Environment.logger.debug(
|
|
422
|
+
Environment.logger.debug("%s create class %s", self, self.type.__qualname__)
|
|
421
423
|
|
|
422
424
|
return environment.created(args[0].create())
|
|
423
425
|
|
|
@@ -432,8 +434,10 @@ class Lifecycle(Enum):
|
|
|
432
434
|
|
|
433
435
|
__slots__ = []
|
|
434
436
|
|
|
435
|
-
|
|
436
|
-
|
|
437
|
+
ON_INJECT = 0
|
|
438
|
+
ON_INIT = 1
|
|
439
|
+
ON_RUNNING = 2
|
|
440
|
+
ON_DESTROY = 3
|
|
437
441
|
|
|
438
442
|
class LifecycleProcessor(ABC):
|
|
439
443
|
"""
|
|
@@ -453,7 +457,7 @@ class LifecycleProcessor(ABC):
|
|
|
453
457
|
# methods
|
|
454
458
|
|
|
455
459
|
@abstractmethod
|
|
456
|
-
def
|
|
460
|
+
def process_lifecycle(self, lifecycle: Lifecycle, instance: object, environment: Environment) -> object:
|
|
457
461
|
pass
|
|
458
462
|
|
|
459
463
|
class PostProcessor(LifecycleProcessor):
|
|
@@ -462,15 +466,11 @@ class PostProcessor(LifecycleProcessor):
|
|
|
462
466
|
"""
|
|
463
467
|
__slots__ = []
|
|
464
468
|
|
|
465
|
-
# constructor
|
|
466
|
-
|
|
467
|
-
def __init__(self):
|
|
468
|
-
super().__init__()
|
|
469
469
|
|
|
470
470
|
def process(self, instance: object, environment: Environment):
|
|
471
471
|
pass
|
|
472
472
|
|
|
473
|
-
def
|
|
473
|
+
def process_lifecycle(self, lifecycle: Lifecycle, instance: object, environment: Environment) -> object:
|
|
474
474
|
if lifecycle == Lifecycle.ON_INIT:
|
|
475
475
|
self.process(instance, environment)
|
|
476
476
|
|
|
@@ -490,11 +490,11 @@ class Providers:
|
|
|
490
490
|
def add(self, *providers: AbstractInstanceProvider):
|
|
491
491
|
for provider in providers:
|
|
492
492
|
if next((p for p in self.dependencies if p.get_type() is provider.get_type()), None) is not None:
|
|
493
|
-
raise InjectorException(self.
|
|
493
|
+
raise InjectorException(self.cycle_report(provider))
|
|
494
494
|
|
|
495
495
|
self.dependencies.append(provider)
|
|
496
496
|
|
|
497
|
-
def
|
|
497
|
+
def cycle_report(self, provider: AbstractInstanceProvider):
|
|
498
498
|
cycle = ""
|
|
499
499
|
|
|
500
500
|
first = True
|
|
@@ -506,23 +506,23 @@ class Providers:
|
|
|
506
506
|
|
|
507
507
|
cycle += f"{p.get_type().__name__}"
|
|
508
508
|
|
|
509
|
-
cycle += f"
|
|
509
|
+
cycle += f" <> {provider.get_type().__name__}"
|
|
510
510
|
|
|
511
511
|
return cycle
|
|
512
512
|
|
|
513
513
|
|
|
514
514
|
# class properties
|
|
515
515
|
|
|
516
|
-
check: list[AbstractInstanceProvider] =
|
|
516
|
+
check: list[AbstractInstanceProvider] = []
|
|
517
517
|
|
|
518
|
-
providers : Dict[Type,AbstractInstanceProvider] =
|
|
519
|
-
cache: Dict[Type, AbstractInstanceProvider] =
|
|
518
|
+
providers : Dict[Type,AbstractInstanceProvider] = {}
|
|
519
|
+
cache: Dict[Type, AbstractInstanceProvider] = {}
|
|
520
520
|
|
|
521
521
|
resolved = False
|
|
522
522
|
|
|
523
523
|
@classmethod
|
|
524
524
|
def register(cls, provider: AbstractInstanceProvider):
|
|
525
|
-
Environment.logger.debug(
|
|
525
|
+
Environment.logger.debug("register provider %s(%s)", provider.get_type().__qualname__, provider.get_type().__name__)
|
|
526
526
|
|
|
527
527
|
# local functions
|
|
528
528
|
|
|
@@ -541,7 +541,7 @@ class Providers:
|
|
|
541
541
|
|
|
542
542
|
return True
|
|
543
543
|
|
|
544
|
-
def
|
|
544
|
+
def cache_provider_for_type(provider: AbstractInstanceProvider, type: Type):
|
|
545
545
|
existing_provider = Providers.cache.get(type)
|
|
546
546
|
if existing_provider is None:
|
|
547
547
|
Providers.cache[type] = provider
|
|
@@ -557,9 +557,9 @@ class Providers:
|
|
|
557
557
|
|
|
558
558
|
# recursion
|
|
559
559
|
|
|
560
|
-
for
|
|
561
|
-
if is_injectable(
|
|
562
|
-
|
|
560
|
+
for super_class in type.__bases__:
|
|
561
|
+
if is_injectable(super_class):
|
|
562
|
+
cache_provider_for_type(provider, super_class)
|
|
563
563
|
|
|
564
564
|
# go
|
|
565
565
|
|
|
@@ -569,7 +569,7 @@ class Providers:
|
|
|
569
569
|
|
|
570
570
|
# cache providers
|
|
571
571
|
|
|
572
|
-
|
|
572
|
+
cache_provider_for_type(provider, provider.get_type())
|
|
573
573
|
|
|
574
574
|
@classmethod
|
|
575
575
|
def resolve(cls):
|
|
@@ -578,28 +578,26 @@ class Providers:
|
|
|
578
578
|
|
|
579
579
|
Providers.check.clear()
|
|
580
580
|
|
|
581
|
-
#Providers.report()
|
|
582
|
-
|
|
583
581
|
@classmethod
|
|
584
582
|
def report(cls):
|
|
585
583
|
for provider in Providers.cache.values():
|
|
586
584
|
print(f"provider {provider.get_type().__qualname__}")
|
|
587
585
|
|
|
588
586
|
@classmethod
|
|
589
|
-
def
|
|
587
|
+
def get_provider(cls, type: Type) -> AbstractInstanceProvider:
|
|
590
588
|
provider = Providers.cache.get(type, None)
|
|
591
589
|
if provider is None:
|
|
592
590
|
raise InjectorException(f"{type.__name__} not registered as injectable")
|
|
593
591
|
|
|
594
592
|
return provider
|
|
595
593
|
|
|
596
|
-
def
|
|
594
|
+
def register_factories(cls: Type):
|
|
597
595
|
descriptor = TypeDescriptor.for_type(cls)
|
|
598
596
|
|
|
599
|
-
for method in descriptor.
|
|
597
|
+
for method in descriptor.get_methods():
|
|
600
598
|
if method.has_decorator(create):
|
|
601
599
|
create_decorator = method.get_decorator(create)
|
|
602
|
-
Providers.register(FunctionInstanceProvider(cls, method.method, method.
|
|
600
|
+
Providers.register(FunctionInstanceProvider(cls, method.method, method.return_type, create_decorator.args[0],
|
|
603
601
|
create_decorator.args[1]))
|
|
604
602
|
def order(prio = 0):
|
|
605
603
|
def decorator(cls):
|
|
@@ -618,8 +616,6 @@ def injectable(eager=True, scope="singleton"):
|
|
|
618
616
|
|
|
619
617
|
Providers.register(ClassInstanceProvider(cls, eager, scope))
|
|
620
618
|
|
|
621
|
-
#TODO registerFactories(cls)
|
|
622
|
-
|
|
623
619
|
return cls
|
|
624
620
|
|
|
625
621
|
return decorator
|
|
@@ -657,6 +653,15 @@ def on_init():
|
|
|
657
653
|
|
|
658
654
|
return decorator
|
|
659
655
|
|
|
656
|
+
def on_running():
|
|
657
|
+
"""
|
|
658
|
+
Methods annotated with @on_running will be called when the container up and running."""
|
|
659
|
+
def decorator(func):
|
|
660
|
+
Decorators.add(func, on_running)
|
|
661
|
+
return func
|
|
662
|
+
|
|
663
|
+
return decorator
|
|
664
|
+
|
|
660
665
|
def on_destroy():
|
|
661
666
|
"""
|
|
662
667
|
Methods annotated with @on_destroy will be called when the instance is destroyed.
|
|
@@ -681,7 +686,7 @@ def environment(imports: Optional[list[Type]] = None):
|
|
|
681
686
|
Decorators.add(cls, environment, imports)
|
|
682
687
|
Decorators.add(cls, injectable) # do we need that?
|
|
683
688
|
|
|
684
|
-
|
|
689
|
+
register_factories(cls)
|
|
685
690
|
|
|
686
691
|
return cls
|
|
687
692
|
|
|
@@ -721,7 +726,7 @@ class Environment:
|
|
|
721
726
|
__slots__ = [
|
|
722
727
|
"type",
|
|
723
728
|
"providers",
|
|
724
|
-
"
|
|
729
|
+
"lifecycle_processors",
|
|
725
730
|
"parent",
|
|
726
731
|
"instances"
|
|
727
732
|
]
|
|
@@ -743,25 +748,25 @@ class Environment:
|
|
|
743
748
|
if self.parent is None and env is not BootEnvironment:
|
|
744
749
|
self.parent = BootEnvironment.get_instance() # inherit environment including its manged instances!
|
|
745
750
|
|
|
746
|
-
self.providers: Dict[Type, AbstractInstanceProvider] =
|
|
747
|
-
self.
|
|
751
|
+
self.providers: Dict[Type, AbstractInstanceProvider] = {}
|
|
752
|
+
self.lifecycle_processors: list[LifecycleProcessor] = []
|
|
748
753
|
|
|
749
754
|
if self.parent is not None:
|
|
750
755
|
self.providers |= self.parent.providers
|
|
751
|
-
self.
|
|
756
|
+
self.lifecycle_processors += self.parent.lifecycle_processors
|
|
752
757
|
|
|
753
758
|
self.instances = []
|
|
754
759
|
|
|
755
760
|
Environment.instance = self
|
|
756
761
|
|
|
757
|
-
# resolve providers on a static basis. This is
|
|
762
|
+
# resolve providers on a static basis. This is executed for all new providers ( in case of new modules multiple times) !
|
|
758
763
|
|
|
759
764
|
Providers.resolve()
|
|
760
765
|
|
|
761
766
|
loaded = set()
|
|
762
767
|
|
|
763
768
|
def add_provider(type: Type, provider: AbstractInstanceProvider):
|
|
764
|
-
Environment.logger.debug(
|
|
769
|
+
Environment.logger.debug("\tadd provider %s for %s", provider, type)
|
|
765
770
|
|
|
766
771
|
self.providers[type] = provider
|
|
767
772
|
|
|
@@ -773,7 +778,7 @@ class Environment:
|
|
|
773
778
|
|
|
774
779
|
def load_environment(env: Type):
|
|
775
780
|
if env not in loaded:
|
|
776
|
-
Environment.logger.debug(
|
|
781
|
+
Environment.logger.debug("load environment %s", env.__qualname__)
|
|
777
782
|
|
|
778
783
|
loaded.add(env)
|
|
779
784
|
|
|
@@ -794,31 +799,31 @@ class Environment:
|
|
|
794
799
|
|
|
795
800
|
# load providers
|
|
796
801
|
|
|
797
|
-
|
|
802
|
+
local_providers = {type: provider for type, provider in Providers.cache.items() if provider.get_module().startswith(scan)}
|
|
798
803
|
|
|
799
804
|
# register providers
|
|
800
805
|
|
|
801
|
-
# make sure, that for every type
|
|
806
|
+
# make sure, that for every type only a single EnvironmentInstanceProvider is created!
|
|
802
807
|
# otherwise inheritance will fuck it up
|
|
803
808
|
|
|
804
|
-
|
|
809
|
+
environment_providers : dict[AbstractInstanceProvider, EnvironmentInstanceProvider] = {}
|
|
805
810
|
|
|
806
|
-
for type, provider in
|
|
807
|
-
|
|
808
|
-
if
|
|
809
|
-
|
|
810
|
-
|
|
811
|
+
for type, provider in local_providers.items():
|
|
812
|
+
environment_provider = environment_providers.get(provider, None)
|
|
813
|
+
if environment_provider is None:
|
|
814
|
+
environment_provider = EnvironmentInstanceProvider(self, provider)
|
|
815
|
+
environment_providers[provider] = environment_provider
|
|
811
816
|
|
|
812
|
-
add_provider(type,
|
|
817
|
+
add_provider(type, environment_provider)
|
|
813
818
|
|
|
814
819
|
# tweak dependencies
|
|
815
820
|
|
|
816
|
-
for type, provider in
|
|
817
|
-
cast(EnvironmentInstanceProvider, self.providers[type]).
|
|
821
|
+
for type, provider in local_providers.items():
|
|
822
|
+
cast(EnvironmentInstanceProvider, self.providers[type]).tweak_dependencies(self.providers)
|
|
818
823
|
|
|
819
824
|
# return local providers
|
|
820
825
|
|
|
821
|
-
return
|
|
826
|
+
return environment_providers.values()
|
|
822
827
|
else:
|
|
823
828
|
return []
|
|
824
829
|
|
|
@@ -827,29 +832,29 @@ class Environment:
|
|
|
827
832
|
for provider in load_environment(env):
|
|
828
833
|
if provider.is_eager():
|
|
829
834
|
provider.create(self)
|
|
835
|
+
|
|
836
|
+
# running callback
|
|
837
|
+
|
|
838
|
+
for instance in self.instances:
|
|
839
|
+
self.execute_processors(Lifecycle.ON_RUNNING, instance)
|
|
840
|
+
|
|
830
841
|
# internal
|
|
831
842
|
|
|
832
|
-
def
|
|
833
|
-
for processor in self.
|
|
834
|
-
processor.
|
|
843
|
+
def execute_processors(self, lifecycle: Lifecycle, instance: T) -> T:
|
|
844
|
+
for processor in self.lifecycle_processors:
|
|
845
|
+
processor.process_lifecycle(lifecycle, instance, self)
|
|
835
846
|
|
|
836
847
|
return instance
|
|
837
848
|
|
|
838
849
|
def created(self, instance: T) -> T:
|
|
839
|
-
def get_order(type: TypeDescriptor) -> int:
|
|
840
|
-
if type.has_decorator(order):
|
|
841
|
-
return type.get_decorator(order).args[0]
|
|
842
|
-
else:
|
|
843
|
-
return 10
|
|
844
|
-
|
|
845
850
|
# remember lifecycle processors
|
|
846
851
|
|
|
847
852
|
if isinstance(instance, LifecycleProcessor):
|
|
848
|
-
self.
|
|
853
|
+
self.lifecycle_processors.append(instance)
|
|
849
854
|
|
|
850
855
|
# sort immediately
|
|
851
856
|
|
|
852
|
-
self.
|
|
857
|
+
self.lifecycle_processors.sort(key=lambda processor: processor.order)
|
|
853
858
|
|
|
854
859
|
# remember instance
|
|
855
860
|
|
|
@@ -857,16 +862,60 @@ class Environment:
|
|
|
857
862
|
|
|
858
863
|
# execute processors
|
|
859
864
|
|
|
860
|
-
|
|
865
|
+
self.execute_processors(Lifecycle.ON_INJECT, instance)
|
|
866
|
+
self.execute_processors(Lifecycle.ON_INIT, instance)
|
|
867
|
+
|
|
868
|
+
return instance
|
|
861
869
|
|
|
862
870
|
# public
|
|
863
871
|
|
|
872
|
+
def report(self):
|
|
873
|
+
builder = StringBuilder()
|
|
874
|
+
|
|
875
|
+
builder.append(f"Environment {self.type.__name__}")
|
|
876
|
+
|
|
877
|
+
if self.parent is not None:
|
|
878
|
+
builder.append(f" parent {self.parent.type.__name__}")
|
|
879
|
+
|
|
880
|
+
builder.append("\n")
|
|
881
|
+
|
|
882
|
+
# post processors
|
|
883
|
+
|
|
884
|
+
builder.append("Processors \n")
|
|
885
|
+
for processor in self.lifecycle_processors:
|
|
886
|
+
builder.append(f"- {processor.__class__.__name__}\n")
|
|
887
|
+
|
|
888
|
+
# providers
|
|
889
|
+
|
|
890
|
+
builder.append("Providers \n")
|
|
891
|
+
for result_type, provider in self.providers.items():
|
|
892
|
+
if cast(EnvironmentInstanceProvider, provider).environment is self:
|
|
893
|
+
builder.append(f"- {result_type.__name__}: {cast(EnvironmentInstanceProvider, provider).provider}\n")
|
|
894
|
+
|
|
895
|
+
# instances
|
|
896
|
+
|
|
897
|
+
builder.append("Instances \n")
|
|
898
|
+
|
|
899
|
+
result = {}
|
|
900
|
+
for obj in self.instances:
|
|
901
|
+
cls = type(obj)
|
|
902
|
+
result[cls] = result.get(cls, 0) + 1
|
|
903
|
+
|
|
904
|
+
for cls, count in result.items():
|
|
905
|
+
builder.append(f"- {cls.__name__}: {count} \n")
|
|
906
|
+
|
|
907
|
+
# done
|
|
908
|
+
|
|
909
|
+
result = str(builder)
|
|
910
|
+
|
|
911
|
+
return result
|
|
912
|
+
|
|
864
913
|
def destroy(self):
|
|
865
914
|
"""
|
|
866
915
|
destroy all managed instances by calling the appropriate lifecycle methods
|
|
867
916
|
"""
|
|
868
917
|
for instance in self.instances:
|
|
869
|
-
self.
|
|
918
|
+
self.execute_processors(Lifecycle.ON_DESTROY, instance)
|
|
870
919
|
|
|
871
920
|
self.instances.clear() # make the cy happy
|
|
872
921
|
|
|
@@ -881,7 +930,7 @@ class Environment:
|
|
|
881
930
|
"""
|
|
882
931
|
provider = self.providers.get(type, None)
|
|
883
932
|
if provider is None:
|
|
884
|
-
Environment.logger.error(
|
|
933
|
+
Environment.logger.error("%s is not supported", type)
|
|
885
934
|
raise InjectorException(f"{type} is not supported")
|
|
886
935
|
|
|
887
936
|
return provider.create(self)
|
|
@@ -893,112 +942,160 @@ class LifecycleCallable:
|
|
|
893
942
|
"order"
|
|
894
943
|
]
|
|
895
944
|
|
|
896
|
-
def __init__(self, decorator,
|
|
945
|
+
def __init__(self, decorator, lifecycle: Lifecycle):
|
|
897
946
|
self.decorator = decorator
|
|
898
947
|
self.lifecycle = lifecycle
|
|
899
948
|
self.order = 0
|
|
900
949
|
|
|
901
950
|
if TypeDescriptor.for_type(type(self)).has_decorator(order):
|
|
902
|
-
self.order =
|
|
951
|
+
self.order = TypeDescriptor.for_type(type(self)).get_decorator(order).args[0]
|
|
903
952
|
|
|
904
|
-
|
|
953
|
+
AbstractCallableProcessor.register(self)
|
|
905
954
|
|
|
906
955
|
def args(self, decorator: DecoratorDescriptor, method: TypeDescriptor.MethodDescriptor, environment: Environment):
|
|
907
956
|
return []
|
|
908
957
|
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
class CallableProcessor(LifecycleProcessor):
|
|
958
|
+
|
|
959
|
+
class AbstractCallableProcessor(LifecycleProcessor):
|
|
912
960
|
# local classes
|
|
913
961
|
|
|
914
962
|
class MethodCall:
|
|
915
963
|
__slots__ = [
|
|
916
964
|
"decorator",
|
|
917
965
|
"method",
|
|
918
|
-
"
|
|
966
|
+
"lifecycle_callable"
|
|
919
967
|
]
|
|
920
968
|
|
|
921
969
|
# constructor
|
|
922
970
|
|
|
923
|
-
def __init__(self, method: TypeDescriptor.MethodDescriptor, decorator: DecoratorDescriptor,
|
|
971
|
+
def __init__(self, method: TypeDescriptor.MethodDescriptor, decorator: DecoratorDescriptor, lifecycle_callable: LifecycleCallable):
|
|
924
972
|
self.decorator = decorator
|
|
925
973
|
self.method = method
|
|
926
|
-
self.
|
|
974
|
+
self.lifecycle_callable = lifecycle_callable
|
|
927
975
|
|
|
928
976
|
def execute(self, instance, environment: Environment):
|
|
929
|
-
self.method.method(instance, *self.
|
|
977
|
+
self.method.method(instance, *self.lifecycle_callable.args(self.decorator, self.method, environment))
|
|
930
978
|
|
|
931
979
|
def __str__(self):
|
|
932
980
|
return f"MethodCall({self.method.method.__name__})"
|
|
933
981
|
|
|
934
|
-
#
|
|
982
|
+
# static data
|
|
935
983
|
|
|
936
|
-
|
|
937
|
-
|
|
984
|
+
callables : Dict[object, LifecycleCallable] = {}
|
|
985
|
+
cache : Dict[Type, list[list[AbstractCallableProcessor.MethodCall]]] = {}
|
|
938
986
|
|
|
939
|
-
|
|
940
|
-
|
|
987
|
+
# static methods
|
|
988
|
+
|
|
989
|
+
@classmethod
|
|
990
|
+
def register(cls, callable: LifecycleCallable):
|
|
991
|
+
AbstractCallableProcessor.callables[callable.decorator] = callable
|
|
941
992
|
|
|
942
|
-
|
|
993
|
+
@classmethod
|
|
994
|
+
def compute_callables(cls, type: Type) -> list[list[AbstractCallableProcessor.MethodCall]]:
|
|
943
995
|
descriptor = TypeDescriptor.for_type(type)
|
|
944
996
|
|
|
945
|
-
result = []
|
|
997
|
+
result = [[], [], [], []] # per lifecycle
|
|
946
998
|
|
|
947
|
-
for method in descriptor.
|
|
999
|
+
for method in descriptor.get_methods():
|
|
948
1000
|
for decorator in method.decorators:
|
|
949
|
-
|
|
950
|
-
|
|
1001
|
+
callable = AbstractCallableProcessor.callables.get(decorator.decorator)
|
|
1002
|
+
if callable is not None: # any callable for this decorator?
|
|
1003
|
+
result[callable.lifecycle.value].append(
|
|
1004
|
+
AbstractCallableProcessor.MethodCall(method, decorator, callable))
|
|
951
1005
|
|
|
952
1006
|
# sort according to order
|
|
953
1007
|
|
|
954
|
-
result.sort(key=lambda call: call.
|
|
1008
|
+
result[0].sort(key=lambda call: call.lifecycle_callable.order)
|
|
1009
|
+
result[1].sort(key=lambda call: call.lifecycle_callable.order)
|
|
1010
|
+
result[2].sort(key=lambda call: call.lifecycle_callable.order)
|
|
1011
|
+
result[3].sort(key=lambda call: call.lifecycle_callable.order)
|
|
955
1012
|
|
|
956
1013
|
# done
|
|
957
1014
|
|
|
958
1015
|
return result
|
|
959
1016
|
|
|
960
|
-
|
|
961
|
-
|
|
1017
|
+
@classmethod
|
|
1018
|
+
def callables_for(cls, type: Type) -> list[list[AbstractCallableProcessor.MethodCall]]:
|
|
1019
|
+
callables = AbstractCallableProcessor.cache.get(type, None)
|
|
962
1020
|
if callables is None:
|
|
963
|
-
callables =
|
|
964
|
-
|
|
1021
|
+
callables = AbstractCallableProcessor.compute_callables(type)
|
|
1022
|
+
AbstractCallableProcessor.cache[type] = callables
|
|
965
1023
|
|
|
966
1024
|
return callables
|
|
967
1025
|
|
|
968
|
-
|
|
969
|
-
|
|
1026
|
+
# constructor
|
|
1027
|
+
|
|
1028
|
+
def __init__(self, lifecycle: Lifecycle):
|
|
1029
|
+
super().__init__()
|
|
1030
|
+
|
|
1031
|
+
self.lifecycle = lifecycle
|
|
970
1032
|
|
|
971
1033
|
# implement
|
|
972
1034
|
|
|
973
|
-
def
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
1035
|
+
def process_lifecycle(self, lifecycle: Lifecycle, instance: object, environment: Environment) -> object:
|
|
1036
|
+
if lifecycle is self.lifecycle:
|
|
1037
|
+
callables = self.callables_for(type(instance))
|
|
1038
|
+
|
|
1039
|
+
for callable in callables[lifecycle.value]:
|
|
977
1040
|
callable.execute(instance, environment)
|
|
978
1041
|
|
|
1042
|
+
@injectable()
|
|
1043
|
+
@order(1)
|
|
1044
|
+
class OnInjectCallableProcessor(AbstractCallableProcessor):
|
|
1045
|
+
def __init__(self):
|
|
1046
|
+
super().__init__(Lifecycle.ON_INJECT)
|
|
1047
|
+
|
|
1048
|
+
@injectable()
|
|
1049
|
+
@order(2)
|
|
1050
|
+
class OnInitCallableProcessor(AbstractCallableProcessor):
|
|
1051
|
+
def __init__(self):
|
|
1052
|
+
super().__init__(Lifecycle.ON_INIT)
|
|
1053
|
+
|
|
1054
|
+
@injectable()
|
|
1055
|
+
@order(3)
|
|
1056
|
+
class OnRunningCallableProcessor(AbstractCallableProcessor):
|
|
1057
|
+
def __init__(self):
|
|
1058
|
+
super().__init__(Lifecycle.ON_RUNNING)
|
|
1059
|
+
|
|
1060
|
+
@injectable()
|
|
1061
|
+
@order(4)
|
|
1062
|
+
class OnDestroyCallableProcessor(AbstractCallableProcessor):
|
|
1063
|
+
def __init__(self):
|
|
1064
|
+
super().__init__(Lifecycle.ON_DESTROY)
|
|
1065
|
+
|
|
1066
|
+
# the callables
|
|
1067
|
+
|
|
979
1068
|
@injectable()
|
|
980
1069
|
@order(1000)
|
|
981
1070
|
class OnInitLifecycleCallable(LifecycleCallable):
|
|
982
1071
|
__slots__ = []
|
|
983
1072
|
|
|
984
|
-
def __init__(self
|
|
985
|
-
super().__init__(on_init,
|
|
1073
|
+
def __init__(self):
|
|
1074
|
+
super().__init__(on_init, Lifecycle.ON_INIT)
|
|
986
1075
|
|
|
987
1076
|
@injectable()
|
|
988
1077
|
@order(1001)
|
|
989
1078
|
class OnDestroyLifecycleCallable(LifecycleCallable):
|
|
990
1079
|
__slots__ = []
|
|
991
1080
|
|
|
992
|
-
def __init__(self
|
|
993
|
-
super().__init__(on_destroy,
|
|
1081
|
+
def __init__(self):
|
|
1082
|
+
super().__init__(on_destroy, Lifecycle.ON_DESTROY)
|
|
1083
|
+
|
|
1084
|
+
@injectable()
|
|
1085
|
+
@order(1002)
|
|
1086
|
+
class OnRunningLifecycleCallable(LifecycleCallable):
|
|
1087
|
+
__slots__ = []
|
|
1088
|
+
|
|
1089
|
+
def __init__(self):
|
|
1090
|
+
super().__init__(on_running, Lifecycle.ON_RUNNING)
|
|
994
1091
|
|
|
995
1092
|
@injectable()
|
|
996
1093
|
@order(9)
|
|
997
1094
|
class EnvironmentAwareLifecycleCallable(LifecycleCallable):
|
|
998
1095
|
__slots__ = []
|
|
999
1096
|
|
|
1000
|
-
def __init__(self
|
|
1001
|
-
super().__init__(inject_environment,
|
|
1097
|
+
def __init__(self):
|
|
1098
|
+
super().__init__(inject_environment, Lifecycle.ON_INJECT)
|
|
1002
1099
|
|
|
1003
1100
|
def args(self, decorator: DecoratorDescriptor, method: TypeDescriptor.MethodDescriptor, environment: Environment):
|
|
1004
1101
|
return [environment]
|
|
@@ -1008,13 +1105,13 @@ class EnvironmentAwareLifecycleCallable(LifecycleCallable):
|
|
|
1008
1105
|
class InjectLifecycleCallable(LifecycleCallable):
|
|
1009
1106
|
__slots__ = []
|
|
1010
1107
|
|
|
1011
|
-
def __init__(self
|
|
1012
|
-
super().__init__(inject,
|
|
1108
|
+
def __init__(self):
|
|
1109
|
+
super().__init__(inject, Lifecycle.ON_INJECT)
|
|
1013
1110
|
|
|
1014
1111
|
# override
|
|
1015
1112
|
|
|
1016
1113
|
def args(self, decorator: DecoratorDescriptor, method: TypeDescriptor.MethodDescriptor, environment: Environment):
|
|
1017
|
-
return [environment.get(type) for type in method.
|
|
1114
|
+
return [environment.get(type) for type in method.param_types]
|
|
1018
1115
|
|
|
1019
1116
|
def scope(name: str):
|
|
1020
1117
|
def decorator(cls):
|
|
@@ -1036,15 +1133,10 @@ class RequestScope(Scope):
|
|
|
1036
1133
|
__slots__ = [
|
|
1037
1134
|
]
|
|
1038
1135
|
|
|
1039
|
-
# constructor
|
|
1040
|
-
|
|
1041
|
-
def __init__(self):
|
|
1042
|
-
super().__init__()
|
|
1043
|
-
|
|
1044
1136
|
# public
|
|
1045
1137
|
|
|
1046
|
-
def get(self, provider: AbstractInstanceProvider, environment: Environment,
|
|
1047
|
-
return provider.create(environment, *
|
|
1138
|
+
def get(self, provider: AbstractInstanceProvider, environment: Environment, arg_provider: Callable[[],list]):
|
|
1139
|
+
return provider.create(environment, *arg_provider())
|
|
1048
1140
|
|
|
1049
1141
|
@scope("singleton")
|
|
1050
1142
|
class SingletonScope(Scope):
|
|
@@ -1061,17 +1153,34 @@ class SingletonScope(Scope):
|
|
|
1061
1153
|
super().__init__()
|
|
1062
1154
|
|
|
1063
1155
|
self.value = None
|
|
1064
|
-
self.lock = threading.
|
|
1156
|
+
self.lock = threading.RLock()
|
|
1065
1157
|
|
|
1066
1158
|
# override
|
|
1067
1159
|
|
|
1068
|
-
def get(self, provider: AbstractInstanceProvider, environment: Environment,
|
|
1160
|
+
def get(self, provider: AbstractInstanceProvider, environment: Environment, arg_provider: Callable[[],list]):
|
|
1069
1161
|
if self.value is None:
|
|
1070
1162
|
with self.lock:
|
|
1071
|
-
if self.value is None:
|
|
1072
|
-
self.value = provider.create(environment, *
|
|
1163
|
+
if self.value is None:
|
|
1164
|
+
self.value = provider.create(environment, *arg_provider())
|
|
1073
1165
|
|
|
1074
1166
|
return self.value
|
|
1167
|
+
|
|
1168
|
+
@scope("thread")
|
|
1169
|
+
class ThreadScope(Scope):
|
|
1170
|
+
__slots__ = [
|
|
1171
|
+
"_local"
|
|
1172
|
+
]
|
|
1173
|
+
|
|
1174
|
+
# constructor
|
|
1175
|
+
|
|
1176
|
+
def __init__(self):
|
|
1177
|
+
super().__init__()
|
|
1178
|
+
self._local = threading.local()
|
|
1179
|
+
|
|
1180
|
+
def get(self, provider: AbstractInstanceProvider, environment: Environment, arg_provider: Callable[[], list]):
|
|
1181
|
+
if not hasattr(self._local, "value"):
|
|
1182
|
+
self._local.value = provider.create(environment, *arg_provider())
|
|
1183
|
+
return self._local.value
|
|
1075
1184
|
|
|
1076
1185
|
# internal class that is required to import technical instance providers
|
|
1077
1186
|
|
|
@@ -1095,4 +1204,4 @@ class BootEnvironment:
|
|
|
1095
1204
|
# constructor
|
|
1096
1205
|
|
|
1097
1206
|
def __init__(self):
|
|
1098
|
-
pass
|
|
1207
|
+
pass
|