aspyx 1.3.0__py3-none-any.whl → 1.4.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/__init__.py +0 -0
- aspyx/di/__init__.py +2 -2
- aspyx/di/aop/__init__.py +16 -2
- aspyx/di/aop/aop.py +169 -102
- aspyx/di/configuration/__init__.py +3 -3
- aspyx/di/configuration/configuration.py +7 -5
- aspyx/di/di.py +224 -46
- aspyx/di/threading/synchronized.py +8 -5
- aspyx/exception/__init__.py +10 -0
- aspyx/exception/exception_manager.py +185 -0
- aspyx/reflection/proxy.py +2 -4
- aspyx/reflection/reflection.py +81 -5
- aspyx/threading/__init__.py +10 -0
- aspyx/threading/thread_local.py +51 -0
- aspyx/{di/util → util}/stringbuilder.py +15 -0
- {aspyx-1.3.0.dist-info → aspyx-1.4.1.dist-info}/METADATA +235 -93
- aspyx-1.4.1.dist-info/RECORD +25 -0
- aspyx-1.3.0.dist-info/RECORD +0 -20
- /aspyx/{di/util → util}/__init__.py +0 -0
- {aspyx-1.3.0.dist-info → aspyx-1.4.1.dist-info}/WHEEL +0 -0
- {aspyx-1.3.0.dist-info → aspyx-1.4.1.dist-info}/licenses/LICENSE +0 -0
- {aspyx-1.3.0.dist-info → aspyx-1.4.1.dist-info}/top_level.txt +0 -0
aspyx/__init__.py
ADDED
|
File without changes
|
aspyx/di/__init__.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module provides dependency injection and aop capabilities for Python applications.
|
|
3
3
|
"""
|
|
4
|
-
from .di import conditional, requires_class, requires_feature, DIException, AbstractCallableProcessor, LifecycleCallable, Lifecycle, Providers, Environment, ClassInstanceProvider, injectable, factory,
|
|
4
|
+
from .di import conditional, requires_class, requires_feature, DIException, AbstractCallableProcessor, LifecycleCallable, Lifecycle, Providers, Environment, ClassInstanceProvider, injectable, factory, module, inject, order, create, on_init, on_running, on_destroy, inject_environment, Factory, PostProcessor
|
|
5
5
|
|
|
6
6
|
# import something from the subpackages, so that the decorators are executed
|
|
7
7
|
|
|
@@ -17,7 +17,7 @@ __all__ = [
|
|
|
17
17
|
"Environment",
|
|
18
18
|
"injectable",
|
|
19
19
|
"factory",
|
|
20
|
-
"
|
|
20
|
+
"module",
|
|
21
21
|
"inject",
|
|
22
22
|
"create",
|
|
23
23
|
"order",
|
aspyx/di/aop/__init__.py
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
"""
|
|
2
|
-
AOP module
|
|
2
|
+
The AOP module gives you the possibility to define aspects that will participate in method execution flows.
|
|
3
|
+
|
|
4
|
+
**Example**: all method executions of methods named "foo" will include a `before` aspect, that will be executed before the original method
|
|
5
|
+
|
|
6
|
+
```python
|
|
7
|
+
@advice
|
|
8
|
+
class Advice:
|
|
9
|
+
@before(methods().named("foo"))
|
|
10
|
+
def before_call(self, invocation: Invocation):
|
|
11
|
+
...
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Note, that this requires that both the advice and the targeted methods need to be managed by an environment.
|
|
3
16
|
"""
|
|
4
|
-
from .aop import before, after, classes, around, error, advice, methods, Invocation
|
|
17
|
+
from .aop import before, after, classes, around, error, advice, methods, Invocation, AspectTarget
|
|
5
18
|
__all__ = [
|
|
6
19
|
"before",
|
|
7
20
|
"after",
|
|
@@ -11,4 +24,5 @@ __all__ = [
|
|
|
11
24
|
"classes",
|
|
12
25
|
"methods",
|
|
13
26
|
"Invocation",
|
|
27
|
+
"AspectTarget"
|
|
14
28
|
]
|
aspyx/di/aop/aop.py
CHANGED
|
@@ -12,10 +12,8 @@ from dataclasses import dataclass
|
|
|
12
12
|
from enum import auto, Enum
|
|
13
13
|
from typing import Optional, Dict, Type, Callable
|
|
14
14
|
|
|
15
|
-
from aspyx.di.di import order
|
|
16
15
|
from aspyx.reflection import Decorators, TypeDescriptor
|
|
17
|
-
from aspyx.di import injectable, Providers, ClassInstanceProvider, Environment, PostProcessor
|
|
18
|
-
|
|
16
|
+
from aspyx.di import injectable, order, Providers, ClassInstanceProvider, Environment, PostProcessor
|
|
19
17
|
|
|
20
18
|
class AOPException(Exception):
|
|
21
19
|
"""
|
|
@@ -27,6 +25,7 @@ class AspectType(Enum):
|
|
|
27
25
|
AspectType defines the types of aspect-oriented advice that can be applied to methods.
|
|
28
26
|
|
|
29
27
|
The available types are:
|
|
28
|
+
|
|
30
29
|
- BEFORE: Advice to be executed before the method invocation.
|
|
31
30
|
- AROUND: Advice that intercepts the method invocation.
|
|
32
31
|
- AFTER: Advice to be executed after the method invocation, regardless of its outcome.
|
|
@@ -100,7 +99,7 @@ class AspectTarget(ABC):
|
|
|
100
99
|
|
|
101
100
|
# fluent
|
|
102
101
|
|
|
103
|
-
def function(self, func):
|
|
102
|
+
def function(self, func) -> AspectTarget:
|
|
104
103
|
self._function = func
|
|
105
104
|
return self
|
|
106
105
|
|
|
@@ -109,30 +108,72 @@ class AspectTarget(ABC):
|
|
|
109
108
|
|
|
110
109
|
return self
|
|
111
110
|
|
|
112
|
-
def that_are_async(self):
|
|
111
|
+
def that_are_async(self) -> AspectTarget:
|
|
112
|
+
"""
|
|
113
|
+
matches methods that are async
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
AspectTarget: self
|
|
117
|
+
"""
|
|
113
118
|
self._async = True
|
|
114
119
|
return self
|
|
115
120
|
|
|
116
|
-
def of_type(self, type: Type):
|
|
121
|
+
def of_type(self, type: Type) -> AspectTarget:
|
|
122
|
+
"""
|
|
123
|
+
matches methods belonging to a class or classes that are subclasses of the specified type
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
type (Type): the type to match against
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
AspectTarget: self
|
|
130
|
+
"""
|
|
117
131
|
self.types.append(type)
|
|
118
132
|
return self
|
|
119
133
|
|
|
120
|
-
def decorated_with(self, decorator):
|
|
134
|
+
def decorated_with(self, decorator: Callable) -> AspectTarget:
|
|
135
|
+
"""
|
|
136
|
+
matches methods or classes that are decorated with the specified decorator
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
decorator (Callable): the decorator callable
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
AspectTarget: self
|
|
143
|
+
"""
|
|
121
144
|
self.decorators.append(decorator)
|
|
122
145
|
return self
|
|
123
146
|
|
|
124
|
-
def matches(self, pattern: str):
|
|
147
|
+
def matches(self, pattern: str) -> AspectTarget:
|
|
125
148
|
"""
|
|
126
149
|
Matches the target against a pattern.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
pattern (str): the pattern
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
AspectTarget: self
|
|
127
156
|
"""
|
|
128
157
|
self.patterns.append(re.compile(pattern))
|
|
129
158
|
return self
|
|
130
159
|
|
|
131
|
-
def named(self, name: str):
|
|
160
|
+
def named(self, name: str) -> AspectTarget:
|
|
161
|
+
"""
|
|
162
|
+
Matches the target against a name.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
name (str): the name
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
AspectTarget: self
|
|
169
|
+
"""
|
|
132
170
|
self.names.append(name)
|
|
133
171
|
return self
|
|
134
172
|
|
|
135
173
|
class ClassAspectTarget(AspectTarget):
|
|
174
|
+
"""
|
|
175
|
+
An AspectTarget matching classes
|
|
176
|
+
"""
|
|
136
177
|
# properties
|
|
137
178
|
|
|
138
179
|
__slots__ = [
|
|
@@ -172,6 +213,10 @@ class ClassAspectTarget(AspectTarget):
|
|
|
172
213
|
# fluent
|
|
173
214
|
|
|
174
215
|
class MethodAspectTarget(AspectTarget):
|
|
216
|
+
"""
|
|
217
|
+
An AspectTarget matching methods
|
|
218
|
+
"""
|
|
219
|
+
|
|
175
220
|
# properties
|
|
176
221
|
|
|
177
222
|
__slots__ = [ ]
|
|
@@ -185,9 +230,6 @@ class MethodAspectTarget(AspectTarget):
|
|
|
185
230
|
|
|
186
231
|
# async
|
|
187
232
|
|
|
188
|
-
name = method_descriptor.method.__name__
|
|
189
|
-
is_async = method_descriptor.is_async()
|
|
190
|
-
|
|
191
233
|
if self._async is not method_descriptor.is_async():
|
|
192
234
|
return False
|
|
193
235
|
|
|
@@ -219,27 +261,33 @@ class MethodAspectTarget(AspectTarget):
|
|
|
219
261
|
|
|
220
262
|
return True
|
|
221
263
|
|
|
222
|
-
def methods():
|
|
264
|
+
def methods() -> AspectTarget:
|
|
223
265
|
"""
|
|
224
266
|
Create a new AspectTarget instance to define method aspect targets.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
AspectTarget: the method target
|
|
225
270
|
"""
|
|
226
271
|
return MethodAspectTarget()
|
|
227
272
|
|
|
228
|
-
def classes():
|
|
273
|
+
def classes() -> AspectTarget:
|
|
229
274
|
"""
|
|
230
275
|
Create a new AspectTarget instance to define class aspect targets.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
AspectTarget: the method target
|
|
231
279
|
"""
|
|
232
280
|
return ClassAspectTarget()
|
|
233
281
|
|
|
234
282
|
|
|
235
|
-
class
|
|
283
|
+
class Aspect:
|
|
236
284
|
__slots__ = [
|
|
237
285
|
"next",
|
|
238
286
|
]
|
|
239
287
|
|
|
240
288
|
# constructor
|
|
241
289
|
|
|
242
|
-
def __init__(self, next: '
|
|
290
|
+
def __init__(self, next: 'Aspect'):
|
|
243
291
|
self.next = next
|
|
244
292
|
|
|
245
293
|
# public
|
|
@@ -250,55 +298,58 @@ class JoinPoint:
|
|
|
250
298
|
async def call_async(self, invocation: 'Invocation'):
|
|
251
299
|
pass
|
|
252
300
|
|
|
253
|
-
class
|
|
301
|
+
class FunctionAspect(Aspect):
|
|
254
302
|
__slots__ = [
|
|
255
303
|
"instance",
|
|
256
304
|
"func",
|
|
305
|
+
"order",
|
|
257
306
|
]
|
|
258
307
|
|
|
259
|
-
def __init__(self, instance, func,
|
|
260
|
-
super().__init__(
|
|
308
|
+
def __init__(self, instance, func, next_aspect: Optional['Aspect']):
|
|
309
|
+
super().__init__(next_aspect)
|
|
261
310
|
|
|
262
311
|
self.instance = instance
|
|
263
312
|
self.func = func
|
|
264
313
|
|
|
314
|
+
self.order = next((decorator.args[0] for decorator in Decorators.get(func) if decorator.decorator is order), 0)
|
|
315
|
+
|
|
265
316
|
def call(self, invocation: 'Invocation'):
|
|
266
|
-
invocation.
|
|
317
|
+
invocation.current_aspect = self
|
|
267
318
|
|
|
268
319
|
return self.func(self.instance, invocation)
|
|
269
320
|
|
|
270
321
|
async def call_async(self, invocation: 'Invocation'):
|
|
271
|
-
invocation.
|
|
322
|
+
invocation.current_aspect = self
|
|
272
323
|
|
|
273
324
|
return await self.func(self.instance, invocation)
|
|
274
325
|
|
|
275
|
-
class
|
|
326
|
+
class MethodAspect(FunctionAspect):
|
|
276
327
|
__slots__ = []
|
|
277
328
|
|
|
278
329
|
def __init__(self, instance, func):
|
|
279
330
|
super().__init__(instance, func, None)
|
|
280
331
|
|
|
281
332
|
def call(self, invocation: 'Invocation'):
|
|
282
|
-
invocation.
|
|
333
|
+
invocation.current_aspect = self
|
|
283
334
|
|
|
284
335
|
return self.func(*invocation.args, **invocation.kwargs)
|
|
285
336
|
|
|
286
337
|
async def call_async(self, invocation: 'Invocation'):
|
|
287
|
-
invocation.
|
|
338
|
+
invocation.current_aspect = self
|
|
288
339
|
|
|
289
340
|
return await self.func(*invocation.args, **invocation.kwargs)
|
|
290
341
|
|
|
291
342
|
@dataclass
|
|
292
|
-
class
|
|
293
|
-
before: list[
|
|
294
|
-
around: list[
|
|
295
|
-
error: list[
|
|
296
|
-
after: list[
|
|
343
|
+
class Aspects:
|
|
344
|
+
before: list[Aspect]
|
|
345
|
+
around: list[Aspect]
|
|
346
|
+
error: list[Aspect]
|
|
347
|
+
after: list[Aspect]
|
|
297
348
|
|
|
298
349
|
class Invocation:
|
|
299
350
|
"""
|
|
300
351
|
Invocation stores the relevant data of a single method invocation.
|
|
301
|
-
It holds the arguments, keyword arguments, result, error, and the
|
|
352
|
+
It holds the arguments, keyword arguments, result, error, and the aspects that define the aspect behavior.
|
|
302
353
|
"""
|
|
303
354
|
# properties
|
|
304
355
|
|
|
@@ -308,20 +359,20 @@ class Invocation:
|
|
|
308
359
|
"kwargs",
|
|
309
360
|
"result",
|
|
310
361
|
"exception",
|
|
311
|
-
"
|
|
312
|
-
"
|
|
362
|
+
"aspects",
|
|
363
|
+
"current_aspect",
|
|
313
364
|
]
|
|
314
365
|
|
|
315
366
|
# constructor
|
|
316
367
|
|
|
317
|
-
def __init__(self, func,
|
|
368
|
+
def __init__(self, func, aspects: Aspects):
|
|
318
369
|
self.func = func
|
|
319
370
|
self.args : list[object] = []
|
|
320
371
|
self.kwargs = None
|
|
321
372
|
self.result = None
|
|
322
373
|
self.exception = None
|
|
323
|
-
self.
|
|
324
|
-
self.
|
|
374
|
+
self.aspects = aspects
|
|
375
|
+
self.current_aspect = None
|
|
325
376
|
|
|
326
377
|
def call(self, *args, **kwargs):
|
|
327
378
|
# remember args
|
|
@@ -331,23 +382,23 @@ class Invocation:
|
|
|
331
382
|
|
|
332
383
|
# run all before
|
|
333
384
|
|
|
334
|
-
for
|
|
335
|
-
|
|
385
|
+
for aspect in self.aspects.before:
|
|
386
|
+
aspect.call(self)
|
|
336
387
|
|
|
337
388
|
# run around's with the method being the last aspect!
|
|
338
389
|
|
|
339
390
|
try:
|
|
340
|
-
self.result = self.
|
|
391
|
+
self.result = self.aspects.around[0].call(self) # will follow the proceed chain
|
|
341
392
|
|
|
342
393
|
except Exception as e:
|
|
343
394
|
self.exception = e
|
|
344
|
-
for
|
|
345
|
-
|
|
395
|
+
for aspect in self.aspects.error:
|
|
396
|
+
aspect.call(self)
|
|
346
397
|
|
|
347
398
|
# run all before
|
|
348
399
|
|
|
349
|
-
for
|
|
350
|
-
|
|
400
|
+
for aspect in self.aspects.after:
|
|
401
|
+
aspect.call(self)
|
|
351
402
|
|
|
352
403
|
if self.exception is not None:
|
|
353
404
|
raise self.exception # rethrow the error
|
|
@@ -362,23 +413,23 @@ class Invocation:
|
|
|
362
413
|
|
|
363
414
|
# run all before
|
|
364
415
|
|
|
365
|
-
for
|
|
366
|
-
|
|
416
|
+
for aspect in self.aspects.before:
|
|
417
|
+
aspect.call(self)
|
|
367
418
|
|
|
368
419
|
# run around's with the method being the last aspect!
|
|
369
420
|
|
|
370
421
|
try:
|
|
371
|
-
self.result = await self.
|
|
422
|
+
self.result = await self.aspects.around[0].call_async(self) # will follow the proceed chain
|
|
372
423
|
|
|
373
424
|
except Exception as e:
|
|
374
425
|
self.exception = e
|
|
375
|
-
for
|
|
376
|
-
|
|
426
|
+
for aspect in self.aspects.error:
|
|
427
|
+
aspect.call(self)
|
|
377
428
|
|
|
378
429
|
# run all before
|
|
379
430
|
|
|
380
|
-
for
|
|
381
|
-
|
|
431
|
+
for aspect in self.aspects.after:
|
|
432
|
+
aspect.call(self)
|
|
382
433
|
|
|
383
434
|
if self.exception is not None:
|
|
384
435
|
raise self.exception # rethrow the error
|
|
@@ -387,7 +438,7 @@ class Invocation:
|
|
|
387
438
|
|
|
388
439
|
def proceed(self, *args, **kwargs):
|
|
389
440
|
"""
|
|
390
|
-
Proceed to the next
|
|
441
|
+
Proceed to the next aspect in the around chain up to the original method.
|
|
391
442
|
"""
|
|
392
443
|
if len(args) > 0 or len(kwargs) > 0: # as soon as we have args, we replace the current ones
|
|
393
444
|
self.args = args
|
|
@@ -395,11 +446,11 @@ class Invocation:
|
|
|
395
446
|
|
|
396
447
|
# next one please...
|
|
397
448
|
|
|
398
|
-
return self.
|
|
449
|
+
return self.current_aspect.next.call(self)
|
|
399
450
|
|
|
400
451
|
async def proceed_async(self, *args, **kwargs):
|
|
401
452
|
"""
|
|
402
|
-
Proceed to the next
|
|
453
|
+
Proceed to the next aspect in the around chain up to the original method.
|
|
403
454
|
"""
|
|
404
455
|
if len(args) > 0 or len(kwargs) > 0: # as soon as we have args, we replace the current ones
|
|
405
456
|
self.args = args
|
|
@@ -407,29 +458,38 @@ class Invocation:
|
|
|
407
458
|
|
|
408
459
|
# next one please...
|
|
409
460
|
|
|
410
|
-
return await self.
|
|
461
|
+
return await self.current_aspect.next.call_async(self)
|
|
411
462
|
|
|
412
|
-
|
|
413
|
-
|
|
463
|
+
class Advices:
|
|
464
|
+
"""
|
|
465
|
+
Internal utility class that collects all advice s
|
|
466
|
+
"""
|
|
414
467
|
# static data
|
|
415
468
|
|
|
416
469
|
targets: list[AspectTarget] = []
|
|
417
470
|
|
|
418
|
-
__slots__ = [
|
|
419
|
-
"cache",
|
|
420
|
-
"lock"
|
|
421
|
-
]
|
|
471
|
+
__slots__ = []
|
|
422
472
|
|
|
423
473
|
# constructor
|
|
424
474
|
|
|
425
475
|
def __init__(self):
|
|
426
|
-
|
|
427
|
-
self.lock = threading.RLock()
|
|
476
|
+
pass
|
|
428
477
|
|
|
429
478
|
# methods
|
|
430
479
|
|
|
431
|
-
|
|
432
|
-
|
|
480
|
+
@classmethod
|
|
481
|
+
def collect(cls, clazz, member, type: AspectType, environment: Environment):
|
|
482
|
+
aspects = [
|
|
483
|
+
FunctionAspect(environment.get(target._clazz), target._function, None) for target in Advices.targets
|
|
484
|
+
if target._type == type
|
|
485
|
+
and target._clazz is not clazz
|
|
486
|
+
and environment.providers.get(target._clazz) is not None
|
|
487
|
+
and target._matches(clazz, member)
|
|
488
|
+
]
|
|
489
|
+
|
|
490
|
+
# sort according to order
|
|
491
|
+
|
|
492
|
+
aspects = sorted(aspects, key=lambda aspect: aspect.order)
|
|
433
493
|
|
|
434
494
|
# link
|
|
435
495
|
|
|
@@ -440,31 +500,23 @@ class Advice:
|
|
|
440
500
|
|
|
441
501
|
return aspects
|
|
442
502
|
|
|
443
|
-
|
|
503
|
+
@classmethod
|
|
504
|
+
def aspects_for(cls, instance, environment: Environment) -> Dict[Callable,Aspects]:
|
|
444
505
|
clazz = type(instance)
|
|
445
506
|
|
|
446
|
-
result =
|
|
447
|
-
if result is None:
|
|
448
|
-
with self.lock:
|
|
449
|
-
result = self.cache.get(clazz, None)
|
|
450
|
-
|
|
451
|
-
if result is None:
|
|
452
|
-
result = {}
|
|
453
|
-
self.cache[clazz] = result
|
|
454
|
-
|
|
455
|
-
for _, member in inspect.getmembers(clazz, predicate=inspect.isfunction):
|
|
456
|
-
join_points = self.compute_join_points(clazz, member, environment)
|
|
457
|
-
if join_points is not None:
|
|
458
|
-
result[member] = join_points
|
|
459
|
-
|
|
507
|
+
result = {}
|
|
460
508
|
|
|
509
|
+
for _, member in inspect.getmembers(clazz, predicate=inspect.isfunction):
|
|
510
|
+
aspects = cls.compute_aspects(clazz, member, environment)
|
|
511
|
+
if aspects is not None:
|
|
512
|
+
result[member] = aspects
|
|
461
513
|
|
|
462
514
|
# add around methods
|
|
463
515
|
|
|
464
516
|
value = {}
|
|
465
517
|
|
|
466
518
|
for key, cjp in result.items():
|
|
467
|
-
jp =
|
|
519
|
+
jp = Aspects(
|
|
468
520
|
before=cjp.before,
|
|
469
521
|
around=cjp.around,
|
|
470
522
|
error=cjp.error,
|
|
@@ -472,7 +524,7 @@ class Advice:
|
|
|
472
524
|
|
|
473
525
|
# add method to around
|
|
474
526
|
|
|
475
|
-
jp.around.append(
|
|
527
|
+
jp.around.append(MethodAspect(instance, key))
|
|
476
528
|
if len(jp.around) > 1:
|
|
477
529
|
jp.around[len(jp.around) - 2].next = jp.around[len(jp.around) - 1]
|
|
478
530
|
|
|
@@ -482,14 +534,15 @@ class Advice:
|
|
|
482
534
|
|
|
483
535
|
return value
|
|
484
536
|
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
537
|
+
@classmethod
|
|
538
|
+
def compute_aspects(cls, clazz, member, environment: Environment) -> Optional[Aspects]:
|
|
539
|
+
befores = cls.collect(clazz, member, AspectType.BEFORE, environment)
|
|
540
|
+
arounds = cls.collect(clazz, member, AspectType.AROUND, environment)
|
|
541
|
+
afters = cls.collect(clazz, member, AspectType.AFTER, environment)
|
|
542
|
+
errors = cls.collect(clazz, member, AspectType.ERROR, environment)
|
|
490
543
|
|
|
491
544
|
if len(befores) > 0 or len(arounds) > 0 or len(afters) > 0 or len(errors) > 0:
|
|
492
|
-
return
|
|
545
|
+
return Aspects(
|
|
493
546
|
before=befores,
|
|
494
547
|
around=arounds,
|
|
495
548
|
error=errors,
|
|
@@ -507,8 +560,8 @@ def sanity_check(clazz: Type, name: str):
|
|
|
507
560
|
|
|
508
561
|
def advice(cls):
|
|
509
562
|
"""
|
|
510
|
-
Classes decorated with
|
|
511
|
-
They can contain methods decorated with
|
|
563
|
+
Classes decorated with `@advice` are treated as advice classes.
|
|
564
|
+
They can contain methods decorated with `@before`, `@after`, `@around`, or `@error` to define aspects.
|
|
512
565
|
"""
|
|
513
566
|
Providers.register(ClassInstanceProvider(cls, True))
|
|
514
567
|
|
|
@@ -517,10 +570,10 @@ def advice(cls):
|
|
|
517
570
|
for name, member in TypeDescriptor.for_type(cls).methods.items():
|
|
518
571
|
decorator = next((decorator for decorator in member.decorators if decorator.decorator in [before, after, around, error]), None)
|
|
519
572
|
if decorator is not None:
|
|
520
|
-
target = decorator.args[0]
|
|
573
|
+
target = decorator.args[0] # multiple targets are already merged in a single! check _register
|
|
521
574
|
target._clazz = cls
|
|
522
575
|
sanity_check(cls, name)
|
|
523
|
-
|
|
576
|
+
Advices.targets.append(target) #??
|
|
524
577
|
|
|
525
578
|
return cls
|
|
526
579
|
|
|
@@ -539,7 +592,7 @@ def _register(decorator, targets: list[AspectTarget], func, aspect_type: AspectT
|
|
|
539
592
|
|
|
540
593
|
def before(*targets: AspectTarget):
|
|
541
594
|
"""
|
|
542
|
-
Methods decorated with
|
|
595
|
+
Methods decorated with `@before` will be executed before the target method is invoked.
|
|
543
596
|
"""
|
|
544
597
|
def decorator(func):
|
|
545
598
|
_register(before, targets, func, AspectType.BEFORE)
|
|
@@ -550,7 +603,7 @@ def before(*targets: AspectTarget):
|
|
|
550
603
|
|
|
551
604
|
def error(*targets: AspectTarget):
|
|
552
605
|
"""
|
|
553
|
-
Methods decorated with
|
|
606
|
+
Methods decorated with `@error` will be executed if the target method raises an exception."""
|
|
554
607
|
def decorator(func):
|
|
555
608
|
_register(error, targets, func, AspectType.ERROR)
|
|
556
609
|
|
|
@@ -560,7 +613,7 @@ def error(*targets: AspectTarget):
|
|
|
560
613
|
|
|
561
614
|
def after(*targets: AspectTarget):
|
|
562
615
|
"""
|
|
563
|
-
Methods decorated with
|
|
616
|
+
Methods decorated with `@after` will be executed after the target method is invoked.
|
|
564
617
|
"""
|
|
565
618
|
def decorator(func):
|
|
566
619
|
_register(after, targets, func, AspectType.AFTER)
|
|
@@ -571,7 +624,7 @@ def after(*targets: AspectTarget):
|
|
|
571
624
|
|
|
572
625
|
def around(*targets: AspectTarget):
|
|
573
626
|
"""
|
|
574
|
-
Methods decorated with
|
|
627
|
+
Methods decorated with `@around` will be executed around the target method.
|
|
575
628
|
Every around method must accept a single parameter of type Invocation and needs to call proceed
|
|
576
629
|
on this parameter to proceed to the next around method.
|
|
577
630
|
"""
|
|
@@ -582,28 +635,42 @@ def around(*targets: AspectTarget):
|
|
|
582
635
|
|
|
583
636
|
return decorator
|
|
584
637
|
|
|
585
|
-
@injectable()
|
|
638
|
+
@injectable(scope="environment")
|
|
586
639
|
@order(0)
|
|
587
640
|
class AdviceProcessor(PostProcessor):
|
|
588
641
|
# properties
|
|
589
642
|
|
|
590
643
|
__slots__ = [
|
|
591
|
-
"
|
|
644
|
+
"lock",
|
|
645
|
+
"cache"
|
|
592
646
|
]
|
|
593
647
|
|
|
594
648
|
# constructor
|
|
595
649
|
|
|
596
|
-
def __init__(self
|
|
650
|
+
def __init__(self):
|
|
597
651
|
super().__init__()
|
|
598
652
|
|
|
599
|
-
self.
|
|
653
|
+
self.cache : Dict[Type, Dict[Callable,Aspects]] = {}
|
|
654
|
+
self.lock = threading.RLock()
|
|
655
|
+
|
|
656
|
+
# local
|
|
657
|
+
|
|
658
|
+
def aspects_for(self, instance, environment: Environment) -> Dict[Callable,Aspects]:
|
|
659
|
+
clazz = type(instance)
|
|
660
|
+
result = self.cache.get(clazz, None)
|
|
661
|
+
if result is None:
|
|
662
|
+
with self.lock:
|
|
663
|
+
result = Advices.aspects_for(instance, environment)
|
|
664
|
+
self.cache[clazz] = result # TOID der cache ist zu dick?????
|
|
665
|
+
|
|
666
|
+
return result
|
|
600
667
|
|
|
601
668
|
# implement
|
|
602
669
|
|
|
603
670
|
def process(self, instance: object, environment: Environment):
|
|
604
|
-
|
|
671
|
+
aspect_dict = self.aspects_for(instance, environment)
|
|
605
672
|
|
|
606
|
-
for member,
|
|
673
|
+
for member, aspects in aspect_dict.items():
|
|
607
674
|
Environment.logger.debug("add aspects for %s:%s", type(instance), member.__name__)
|
|
608
675
|
|
|
609
676
|
def wrap(jp):
|
|
@@ -619,6 +686,6 @@ class AdviceProcessor(PostProcessor):
|
|
|
619
686
|
return async_wrapper
|
|
620
687
|
|
|
621
688
|
if inspect.iscoroutinefunction(member):
|
|
622
|
-
setattr(instance, member.__name__, types.MethodType(wrap_async(
|
|
689
|
+
setattr(instance, member.__name__, types.MethodType(wrap_async(aspects), instance))
|
|
623
690
|
else:
|
|
624
|
-
setattr(instance, member.__name__, types.MethodType(wrap(
|
|
691
|
+
setattr(instance, member.__name__, types.MethodType(wrap(aspects), instance))
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
This module contains functionality to read configuration values from different sources and to retrieve or inject them.
|
|
3
3
|
"""
|
|
4
|
-
from .configuration import ConfigurationManager, ConfigurationSource,
|
|
4
|
+
from .configuration import ConfigurationManager, ConfigurationSource, inject_value
|
|
5
5
|
from .env_configuration_source import EnvConfigurationSource
|
|
6
6
|
from .yaml_configuration_source import YamlConfigurationSource
|
|
7
7
|
|
|
@@ -10,5 +10,5 @@ __all__ = [
|
|
|
10
10
|
"ConfigurationSource",
|
|
11
11
|
"EnvConfigurationSource",
|
|
12
12
|
"YamlConfigurationSource",
|
|
13
|
-
"
|
|
13
|
+
"inject_value"
|
|
14
14
|
]
|