aspyx 0.1.0__py3-none-any.whl → 1.0.1__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 +4 -1
- aspyx/di/aop/__init__.py +4 -1
- aspyx/di/aop/aop.py +98 -88
- aspyx/di/configuration/__init__.py +4 -1
- aspyx/di/configuration/configuration.py +21 -14
- aspyx/di/di.py +137 -109
- aspyx/reflection/__init__.py +4 -1
- aspyx/reflection/proxy.py +10 -7
- aspyx/reflection/reflection.py +35 -22
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/METADATA +147 -43
- aspyx-1.0.1.dist-info/RECORD +14 -0
- aspyx-0.1.0.dist-info/RECORD +0 -14
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/WHEEL +0 -0
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/top_level.txt +0 -0
aspyx/di/__init__.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides dependency injection and aop capabilities for Python applications.
|
|
3
|
+
"""
|
|
1
4
|
from .di import InjectorException, CallableProcessor, LifecycleCallable, Lifecycle, Providers, Environment, ClassInstanceProvider, injectable, factory, environment, inject, create, on_init, on_destroy, inject_environment, Factory, PostProcessor
|
|
2
5
|
|
|
3
6
|
# import something from the subpackages, so that teh decorators are executed
|
|
@@ -26,4 +29,4 @@ __all__ = [
|
|
|
26
29
|
"LifecycleCallable",
|
|
27
30
|
"InjectorException",
|
|
28
31
|
"Lifecycle"
|
|
29
|
-
]
|
|
32
|
+
]
|
aspyx/di/aop/__init__.py
CHANGED
aspyx/di/aop/aop.py
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides aspect-oriented programming (AOP) capabilities for Python applications.
|
|
3
|
+
"""
|
|
1
4
|
from __future__ import annotations
|
|
2
5
|
|
|
3
6
|
from abc import ABC, abstractmethod
|
|
4
7
|
import inspect
|
|
5
8
|
import re
|
|
9
|
+
import threading
|
|
6
10
|
import types
|
|
7
11
|
from dataclasses import dataclass
|
|
8
12
|
from enum import auto, Enum
|
|
9
13
|
from typing import Optional, Dict, Type, Callable
|
|
10
14
|
|
|
15
|
+
from aspyx.di.di import order
|
|
11
16
|
from aspyx.reflection import Decorators, TypeDescriptor
|
|
12
17
|
from aspyx.di import injectable, Providers, ClassInstanceProvider, Environment, PostProcessor
|
|
13
18
|
|
|
14
19
|
|
|
15
20
|
class AOPException(Exception):
|
|
16
21
|
"""
|
|
17
|
-
Exception raised for errors in the aop logic.
|
|
18
|
-
|
|
22
|
+
Exception raised for errors in the aop logic.
|
|
23
|
+
"""
|
|
19
24
|
|
|
20
25
|
class AspectType(Enum):
|
|
21
26
|
"""
|
|
@@ -51,6 +56,7 @@ class AspectTarget(ABC):
|
|
|
51
56
|
"names",
|
|
52
57
|
"patterns",
|
|
53
58
|
"types",
|
|
59
|
+
"other",
|
|
54
60
|
"decorators",
|
|
55
61
|
]
|
|
56
62
|
|
|
@@ -59,20 +65,38 @@ class AspectTarget(ABC):
|
|
|
59
65
|
def __init__(self):
|
|
60
66
|
self._clazz = None
|
|
61
67
|
self._instance = None
|
|
68
|
+
self._function = None
|
|
69
|
+
self._type = None
|
|
62
70
|
|
|
63
71
|
self.patterns = []
|
|
64
72
|
self.names = []
|
|
65
73
|
self.types = []
|
|
66
74
|
self.decorators = []
|
|
67
75
|
|
|
68
|
-
|
|
76
|
+
self.other : list[AspectTarget] = []
|
|
69
77
|
|
|
70
78
|
# abstract
|
|
71
79
|
|
|
72
|
-
@abstractmethod
|
|
73
80
|
def _matches(self, clazz : Type, func):
|
|
81
|
+
if not self._matches_self(clazz, func):
|
|
82
|
+
for target in self.other:
|
|
83
|
+
if target._matches(clazz, func):
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
@abstractmethod
|
|
91
|
+
def _matches_self(self, clazz: Type, func):
|
|
74
92
|
pass
|
|
75
93
|
|
|
94
|
+
# protected
|
|
95
|
+
|
|
96
|
+
def _add(self, target: AspectTarget):
|
|
97
|
+
self.other.append(target)
|
|
98
|
+
return self
|
|
99
|
+
|
|
76
100
|
# fluent
|
|
77
101
|
|
|
78
102
|
def function(self, func):
|
|
@@ -83,7 +107,7 @@ class AspectTarget(ABC):
|
|
|
83
107
|
self._type = type
|
|
84
108
|
|
|
85
109
|
return self
|
|
86
|
-
|
|
110
|
+
|
|
87
111
|
def of_type(self, type: Type):
|
|
88
112
|
self.types.append(type)
|
|
89
113
|
return self
|
|
@@ -91,7 +115,7 @@ class AspectTarget(ABC):
|
|
|
91
115
|
def decorated_with(self, decorator):
|
|
92
116
|
self.decorators.append(decorator)
|
|
93
117
|
return self
|
|
94
|
-
|
|
118
|
+
|
|
95
119
|
def matches(self, pattern: str):
|
|
96
120
|
"""
|
|
97
121
|
Matches the target against a pattern.
|
|
@@ -109,18 +133,11 @@ class ClassAspectTarget(AspectTarget):
|
|
|
109
133
|
__slots__ = [
|
|
110
134
|
]
|
|
111
135
|
|
|
112
|
-
# constructor
|
|
113
|
-
|
|
114
|
-
def __init__(self):
|
|
115
|
-
super().__init__()
|
|
116
|
-
|
|
117
|
-
pass
|
|
118
|
-
|
|
119
136
|
# public
|
|
120
137
|
|
|
121
|
-
def
|
|
122
|
-
|
|
123
|
-
|
|
138
|
+
def _matches_self(self, clazz : Type, func):
|
|
139
|
+
class_descriptor = TypeDescriptor.for_type(clazz)
|
|
140
|
+
#descriptor = TypeDescriptor.for_type(func)
|
|
124
141
|
# type
|
|
125
142
|
|
|
126
143
|
if len(self.types) > 0:
|
|
@@ -130,7 +147,7 @@ class ClassAspectTarget(AspectTarget):
|
|
|
130
147
|
# decorators
|
|
131
148
|
|
|
132
149
|
if len(self.decorators) > 0:
|
|
133
|
-
if next((decorator for decorator in self.decorators if
|
|
150
|
+
if next((decorator for decorator in self.decorators if class_descriptor.has_decorator(decorator)), None) is None:
|
|
134
151
|
return False
|
|
135
152
|
|
|
136
153
|
# names
|
|
@@ -138,46 +155,28 @@ class ClassAspectTarget(AspectTarget):
|
|
|
138
155
|
if len(self.names) > 0:
|
|
139
156
|
if next((name for name in self.names if name == clazz.__name__), None) is None:
|
|
140
157
|
return False
|
|
141
|
-
|
|
158
|
+
|
|
142
159
|
# patterns
|
|
143
160
|
|
|
144
161
|
if len(self.patterns) > 0:
|
|
145
162
|
if next((pattern for pattern in self.patterns if re.fullmatch(pattern, clazz.__name__) is not None), None) is None:
|
|
146
163
|
return False
|
|
147
|
-
|
|
148
|
-
# yipee
|
|
149
164
|
|
|
150
165
|
return True
|
|
151
|
-
|
|
152
|
-
# fluent
|
|
153
166
|
|
|
167
|
+
# fluent
|
|
154
168
|
|
|
155
|
-
|
|
156
169
|
class MethodAspectTarget(AspectTarget):
|
|
157
170
|
# properties
|
|
158
171
|
|
|
159
|
-
__slots__ = [
|
|
160
|
-
"_clazz",
|
|
161
|
-
"_instance",
|
|
162
|
-
"_type",
|
|
163
|
-
"_function",
|
|
164
|
-
"names",
|
|
165
|
-
"patterns",
|
|
166
|
-
"types",
|
|
167
|
-
"decorators",
|
|
168
|
-
]
|
|
169
|
-
|
|
170
|
-
# constructor
|
|
171
|
-
|
|
172
|
-
def __init__(self):
|
|
173
|
-
super().__init__()
|
|
172
|
+
__slots__ = [ ]
|
|
174
173
|
|
|
175
174
|
# public
|
|
176
175
|
|
|
177
|
-
def
|
|
176
|
+
def _matches_self(self, clazz : Type, func):
|
|
178
177
|
descriptor = TypeDescriptor.for_type(clazz)
|
|
179
178
|
|
|
180
|
-
|
|
179
|
+
method_descriptor = descriptor.get_method(func.__name__)
|
|
181
180
|
|
|
182
181
|
# type
|
|
183
182
|
|
|
@@ -188,7 +187,7 @@ class MethodAspectTarget(AspectTarget):
|
|
|
188
187
|
# decorators
|
|
189
188
|
|
|
190
189
|
if len(self.decorators) > 0:
|
|
191
|
-
if next((decorator for decorator in self.decorators if
|
|
190
|
+
if next((decorator for decorator in self.decorators if method_descriptor.has_decorator(decorator)), None) is None:
|
|
192
191
|
return False
|
|
193
192
|
|
|
194
193
|
# names
|
|
@@ -196,13 +195,13 @@ class MethodAspectTarget(AspectTarget):
|
|
|
196
195
|
if len(self.names) > 0:
|
|
197
196
|
if next((name for name in self.names if name == func.__name__), None) is None:
|
|
198
197
|
return False
|
|
199
|
-
|
|
198
|
+
|
|
200
199
|
# patterns
|
|
201
200
|
|
|
202
201
|
if len(self.patterns) > 0:
|
|
203
202
|
if next((pattern for pattern in self.patterns if re.fullmatch(pattern, func.__name__) is not None), None) is None:
|
|
204
203
|
return False
|
|
205
|
-
|
|
204
|
+
|
|
206
205
|
# yipee
|
|
207
206
|
|
|
208
207
|
return True
|
|
@@ -248,7 +247,7 @@ class FunctionJoinPoint(JoinPoint):
|
|
|
248
247
|
self.func = func
|
|
249
248
|
|
|
250
249
|
def call(self, invocation: 'Invocation'):
|
|
251
|
-
invocation.
|
|
250
|
+
invocation.current_join_point = self
|
|
252
251
|
|
|
253
252
|
return self.func(self.instance, invocation)
|
|
254
253
|
|
|
@@ -259,7 +258,7 @@ class MethodJoinPoint(FunctionJoinPoint):
|
|
|
259
258
|
super().__init__(instance, func, None)
|
|
260
259
|
|
|
261
260
|
def call(self, invocation: 'Invocation'):
|
|
262
|
-
invocation.
|
|
261
|
+
invocation.current_join_point = self
|
|
263
262
|
|
|
264
263
|
return self.func(*invocation.args, **invocation.kwargs)
|
|
265
264
|
|
|
@@ -283,20 +282,20 @@ class Invocation:
|
|
|
283
282
|
"kwargs",
|
|
284
283
|
"result",
|
|
285
284
|
"exception",
|
|
286
|
-
"
|
|
287
|
-
"
|
|
285
|
+
"join_points",
|
|
286
|
+
"current_join_point",
|
|
288
287
|
]
|
|
289
288
|
|
|
290
289
|
# constructor
|
|
291
290
|
|
|
292
|
-
def __init__(self,
|
|
291
|
+
def __init__(self, func, join_points: JoinPoints):
|
|
293
292
|
self.func = func
|
|
294
293
|
self.args : list[object] = []
|
|
295
294
|
self.kwargs = None
|
|
296
295
|
self.result = None
|
|
297
296
|
self.exception = None
|
|
298
|
-
self.
|
|
299
|
-
self.
|
|
297
|
+
self.join_points = join_points
|
|
298
|
+
self.current_join_point = None
|
|
300
299
|
|
|
301
300
|
def call(self, *args, **kwargs):
|
|
302
301
|
# remember args
|
|
@@ -306,28 +305,28 @@ class Invocation:
|
|
|
306
305
|
|
|
307
306
|
# run all before
|
|
308
307
|
|
|
309
|
-
for
|
|
310
|
-
|
|
308
|
+
for join_point in self.join_points.before:
|
|
309
|
+
join_point.call(self)
|
|
311
310
|
|
|
312
311
|
# run around's with the method being the last aspect!
|
|
313
312
|
|
|
314
313
|
try:
|
|
315
|
-
self.result = self.
|
|
314
|
+
self.result = self.join_points.around[0].call(self) # will follow the proceed chain
|
|
316
315
|
|
|
317
316
|
except Exception as e:
|
|
318
317
|
self.exception = e
|
|
319
|
-
for
|
|
320
|
-
|
|
318
|
+
for join_point in self.join_points.error:
|
|
319
|
+
join_point.call(self)
|
|
321
320
|
|
|
322
321
|
# run all before
|
|
323
322
|
|
|
324
|
-
for
|
|
325
|
-
|
|
323
|
+
for join_point in self.join_points.after:
|
|
324
|
+
join_point.call(self)
|
|
326
325
|
|
|
327
326
|
if self.exception is not None:
|
|
328
327
|
raise self.exception # rethrow the error
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
|
|
329
|
+
return self.result
|
|
331
330
|
|
|
332
331
|
def proceed(self, *args, **kwargs):
|
|
333
332
|
"""
|
|
@@ -339,7 +338,7 @@ class Invocation:
|
|
|
339
338
|
|
|
340
339
|
# next one please...
|
|
341
340
|
|
|
342
|
-
return self.
|
|
341
|
+
return self.current_join_point.next.call(self)
|
|
343
342
|
|
|
344
343
|
@injectable()
|
|
345
344
|
class Advice:
|
|
@@ -349,12 +348,14 @@ class Advice:
|
|
|
349
348
|
|
|
350
349
|
__slots__ = [
|
|
351
350
|
"cache",
|
|
351
|
+
"lock"
|
|
352
352
|
]
|
|
353
353
|
|
|
354
354
|
# constructor
|
|
355
355
|
|
|
356
356
|
def __init__(self):
|
|
357
|
-
self.cache : Dict[Type, Dict[Callable,JoinPoints]] =
|
|
357
|
+
self.cache : Dict[Type, Dict[Callable,JoinPoints]] = {}
|
|
358
|
+
self.lock = threading.RLock()
|
|
358
359
|
|
|
359
360
|
# methods
|
|
360
361
|
|
|
@@ -370,24 +371,27 @@ class Advice:
|
|
|
370
371
|
|
|
371
372
|
return aspects
|
|
372
373
|
|
|
373
|
-
|
|
374
|
-
def joinPoints4(self, instance, environment: Environment) -> Dict[Callable,JoinPoints]:
|
|
374
|
+
def join_points4(self, instance, environment: Environment) -> Dict[Callable,JoinPoints]:
|
|
375
375
|
clazz = type(instance)
|
|
376
376
|
|
|
377
377
|
result = self.cache.get(clazz, None)
|
|
378
378
|
if result is None:
|
|
379
|
-
|
|
379
|
+
with self.lock:
|
|
380
|
+
result = self.cache.get(clazz, None)
|
|
380
381
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if joinPoints is not None:
|
|
384
|
-
result[member] = joinPoints
|
|
382
|
+
if result is None:
|
|
383
|
+
result = {}
|
|
385
384
|
|
|
386
|
-
|
|
385
|
+
for _, member in inspect.getmembers(clazz, predicate=inspect.isfunction):
|
|
386
|
+
join_points = self.compute_join_points(clazz, member, environment)
|
|
387
|
+
if join_points is not None:
|
|
388
|
+
result[member] = join_points
|
|
389
|
+
|
|
390
|
+
self.cache[clazz] = result
|
|
387
391
|
|
|
388
392
|
# add around methods
|
|
389
393
|
|
|
390
|
-
value =
|
|
394
|
+
value = {}
|
|
391
395
|
|
|
392
396
|
for key, cjp in result.items():
|
|
393
397
|
jp = JoinPoints(
|
|
@@ -408,7 +412,7 @@ class Advice:
|
|
|
408
412
|
|
|
409
413
|
return value
|
|
410
414
|
|
|
411
|
-
def
|
|
415
|
+
def compute_join_points(self, clazz, member, environment: Environment) -> Optional[JoinPoints]:
|
|
412
416
|
befores = self.collect(clazz, member, AspectType.BEFORE, environment)
|
|
413
417
|
arounds = self.collect(clazz, member, AspectType.AROUND, environment)
|
|
414
418
|
afters = self.collect(clazz, member, AspectType.AFTER, environment)
|
|
@@ -424,9 +428,9 @@ class Advice:
|
|
|
424
428
|
else:
|
|
425
429
|
return None
|
|
426
430
|
|
|
427
|
-
def
|
|
431
|
+
def sanity_check(clazz: Type, name: str):
|
|
428
432
|
m = TypeDescriptor.for_type(clazz).get_method(name)
|
|
429
|
-
if len(m.
|
|
433
|
+
if len(m.param_types) != 1 or m.param_types[0] != Invocation:
|
|
430
434
|
raise AOPException(f"Method {clazz.__name__}.{name} expected to have one parameter of type Invocation")
|
|
431
435
|
|
|
432
436
|
# decorators
|
|
@@ -445,7 +449,7 @@ def advice(cls):
|
|
|
445
449
|
if decorator is not None:
|
|
446
450
|
target = decorator.args[0]
|
|
447
451
|
target._clazz = cls
|
|
448
|
-
|
|
452
|
+
sanity_check(cls, name)
|
|
449
453
|
Advice.targets.append(target)
|
|
450
454
|
|
|
451
455
|
return cls
|
|
@@ -453,57 +457,63 @@ def advice(cls):
|
|
|
453
457
|
|
|
454
458
|
# decorators
|
|
455
459
|
|
|
456
|
-
def _register(decorator,
|
|
457
|
-
target
|
|
460
|
+
def _register(decorator, targets: list[AspectTarget], func, aspect_type: AspectType):
|
|
461
|
+
target = targets[0]
|
|
462
|
+
|
|
463
|
+
for i in range(1, len(targets)):
|
|
464
|
+
target._add(targets[i])
|
|
465
|
+
|
|
466
|
+
target.function(func).type(aspect_type)
|
|
458
467
|
|
|
459
468
|
Decorators.add(func, decorator, target)
|
|
460
469
|
|
|
461
|
-
def before(
|
|
470
|
+
def before(*targets: AspectTarget):
|
|
462
471
|
"""
|
|
463
472
|
Methods decorated with @before will be executed before the target method is invoked.
|
|
464
473
|
"""
|
|
465
474
|
def decorator(func):
|
|
466
|
-
_register(before,
|
|
475
|
+
_register(before, targets, func, AspectType.BEFORE)
|
|
467
476
|
|
|
468
477
|
return func
|
|
469
478
|
|
|
470
479
|
return decorator
|
|
471
480
|
|
|
472
|
-
def error(
|
|
481
|
+
def error(*targets: AspectTarget):
|
|
473
482
|
"""
|
|
474
483
|
Methods decorated with @error will be executed if the target method raises an exception."""
|
|
475
484
|
def decorator(func):
|
|
476
|
-
_register(error,
|
|
485
|
+
_register(error, targets, func, AspectType.ERROR)
|
|
477
486
|
|
|
478
487
|
return func
|
|
479
488
|
|
|
480
489
|
return decorator
|
|
481
490
|
|
|
482
|
-
def after(
|
|
491
|
+
def after(*targets: AspectTarget):
|
|
483
492
|
"""
|
|
484
493
|
Methods decorated with @after will be executed after the target method is invoked.
|
|
485
494
|
"""
|
|
486
495
|
def decorator(func):
|
|
487
|
-
_register(after,
|
|
496
|
+
_register(after, targets, func, AspectType.AFTER)
|
|
488
497
|
|
|
489
498
|
return func
|
|
490
499
|
|
|
491
500
|
return decorator
|
|
492
501
|
|
|
493
|
-
def around(
|
|
502
|
+
def around(*targets: AspectTarget):
|
|
494
503
|
"""
|
|
495
504
|
Methods decorated with @around will be executed around the target method.
|
|
496
505
|
Every around method must accept a single parameter of type Invocation and needs to call proceed
|
|
497
506
|
on this parameter to proceed to the next around method.
|
|
498
507
|
"""
|
|
499
508
|
def decorator(func):
|
|
500
|
-
_register(around,
|
|
509
|
+
_register(around, targets, func, AspectType.AROUND)
|
|
501
510
|
|
|
502
511
|
return func
|
|
503
512
|
|
|
504
513
|
return decorator
|
|
505
514
|
|
|
506
515
|
@injectable()
|
|
516
|
+
@order(0)
|
|
507
517
|
class AdviceProcessor(PostProcessor):
|
|
508
518
|
# properties
|
|
509
519
|
|
|
@@ -521,12 +531,12 @@ class AdviceProcessor(PostProcessor):
|
|
|
521
531
|
# implement
|
|
522
532
|
|
|
523
533
|
def process(self, instance: object, environment: Environment):
|
|
524
|
-
|
|
534
|
+
join_point_dict = self.advice.join_points4(instance, environment)
|
|
525
535
|
|
|
526
|
-
for member,
|
|
527
|
-
Environment.logger.debug(
|
|
536
|
+
for member, join_points in join_point_dict.items():
|
|
537
|
+
Environment.logger.debug("add aspects for %s:%s", type(instance), member.__name__)
|
|
528
538
|
|
|
529
539
|
def wrap(jp):
|
|
530
540
|
return lambda *args, **kwargs: Invocation(member, jp).call(*args, **kwargs)
|
|
531
541
|
|
|
532
|
-
setattr(instance, member.__name__, types.MethodType(wrap(
|
|
542
|
+
setattr(instance, member.__name__, types.MethodType(wrap(join_points), instance))
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration handling module.
|
|
3
|
+
"""
|
|
1
4
|
from __future__ import annotations
|
|
2
5
|
|
|
3
6
|
from abc import ABC, abstractmethod
|
|
@@ -5,7 +8,8 @@ import os
|
|
|
5
8
|
from typing import Optional, Type, TypeVar
|
|
6
9
|
from dotenv import load_dotenv
|
|
7
10
|
|
|
8
|
-
from aspyx.di import injectable, Environment, CallableProcessor, LifecycleCallable, Lifecycle
|
|
11
|
+
from aspyx.di import injectable, Environment, CallableProcessor, LifecycleCallable, Lifecycle
|
|
12
|
+
from aspyx.di.di import order, inject
|
|
9
13
|
from aspyx.reflection import Decorators, DecoratorDescriptor, TypeDescriptor
|
|
10
14
|
|
|
11
15
|
T = TypeVar("T")
|
|
@@ -14,7 +18,6 @@ class ConfigurationException(Exception):
|
|
|
14
18
|
"""
|
|
15
19
|
Exception raised for errors in the configuration logic.
|
|
16
20
|
"""
|
|
17
|
-
pass
|
|
18
21
|
|
|
19
22
|
@injectable()
|
|
20
23
|
class ConfigurationManager:
|
|
@@ -29,7 +32,7 @@ class ConfigurationManager:
|
|
|
29
32
|
|
|
30
33
|
def __init__(self):
|
|
31
34
|
self.sources = []
|
|
32
|
-
self._data =
|
|
35
|
+
self._data = {}
|
|
33
36
|
self.coercions = {
|
|
34
37
|
int: int,
|
|
35
38
|
float: float,
|
|
@@ -81,17 +84,17 @@ class ConfigurationManager:
|
|
|
81
84
|
current = current[key]
|
|
82
85
|
|
|
83
86
|
return current
|
|
84
|
-
|
|
87
|
+
|
|
85
88
|
v = resolve_value(path, default)
|
|
86
89
|
|
|
87
90
|
if isinstance(v, type):
|
|
88
91
|
return v
|
|
89
|
-
|
|
92
|
+
|
|
90
93
|
if type in self.coercions:
|
|
91
94
|
try:
|
|
92
95
|
return self.coercions[type](v)
|
|
93
|
-
except Exception:
|
|
94
|
-
raise ConfigurationException(f"error during coercion to {type}")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
raise ConfigurationException(f"error during coercion to {type}") from e
|
|
95
98
|
else:
|
|
96
99
|
raise ConfigurationException(f"unknown coercion to {type}")
|
|
97
100
|
|
|
@@ -103,15 +106,18 @@ class ConfigurationSource(ABC):
|
|
|
103
106
|
|
|
104
107
|
__slots__ = []
|
|
105
108
|
|
|
106
|
-
def __init__(self
|
|
107
|
-
manager._register(self)
|
|
109
|
+
def __init__(self):
|
|
108
110
|
pass
|
|
109
111
|
|
|
112
|
+
@inject()
|
|
113
|
+
def set_manager(self, manager: ConfigurationManager):
|
|
114
|
+
manager._register(self)
|
|
115
|
+
|
|
110
116
|
@abstractmethod
|
|
111
117
|
def load(self) -> dict:
|
|
112
118
|
"""
|
|
113
|
-
return the configuration values of this source as a dictionary.
|
|
114
|
-
|
|
119
|
+
return the configuration values of this source as a dictionary.
|
|
120
|
+
"""
|
|
115
121
|
|
|
116
122
|
@injectable()
|
|
117
123
|
class EnvConfigurationSource(ConfigurationSource):
|
|
@@ -123,8 +129,8 @@ class EnvConfigurationSource(ConfigurationSource):
|
|
|
123
129
|
|
|
124
130
|
# constructor
|
|
125
131
|
|
|
126
|
-
def __init__(self
|
|
127
|
-
super().__init__(
|
|
132
|
+
def __init__(self):
|
|
133
|
+
super().__init__()
|
|
128
134
|
|
|
129
135
|
load_dotenv()
|
|
130
136
|
|
|
@@ -180,6 +186,7 @@ def value(key: str, default=None):
|
|
|
180
186
|
return decorator
|
|
181
187
|
|
|
182
188
|
@injectable()
|
|
189
|
+
@order(9)
|
|
183
190
|
class ConfigurationLifecycleCallable(LifecycleCallable):
|
|
184
191
|
def __init__(self, processor: CallableProcessor, manager: ConfigurationManager):
|
|
185
192
|
super().__init__(value, processor, Lifecycle.ON_INIT)
|
|
@@ -187,4 +194,4 @@ class ConfigurationLifecycleCallable(LifecycleCallable):
|
|
|
187
194
|
self.manager = manager
|
|
188
195
|
|
|
189
196
|
def args(self, decorator: DecoratorDescriptor, method: TypeDescriptor.MethodDescriptor, environment: Environment):
|
|
190
|
-
return [self.manager.get(decorator.args[0], method.
|
|
197
|
+
return [self.manager.get(decorator.args[0], method.param_types[0], decorator.args[1])]
|