aspyx 1.4.0__py3-none-any.whl → 1.5.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/__init__.py +1 -0
- aspyx/di/__init__.py +3 -3
- aspyx/di/aop/__init__.py +16 -2
- aspyx/di/aop/aop.py +64 -22
- aspyx/di/configuration/__init__.py +3 -3
- aspyx/di/configuration/configuration.py +7 -5
- aspyx/di/di.py +188 -44
- aspyx/exception/__init__.py +1 -1
- aspyx/exception/exception_manager.py +35 -18
- aspyx/reflection/proxy.py +35 -10
- aspyx/reflection/reflection.py +84 -20
- aspyx/threading/__init__.py +1 -1
- aspyx/threading/thread_local.py +6 -2
- aspyx/util/stringbuilder.py +6 -2
- aspyx-1.5.0.dist-info/METADATA +33 -0
- aspyx-1.5.0.dist-info/RECORD +24 -0
- {aspyx-1.4.0.dist-info → aspyx-1.5.0.dist-info}/WHEEL +1 -2
- aspyx-1.4.0.dist-info/METADATA +0 -825
- aspyx-1.4.0.dist-info/RECORD +0 -25
- aspyx-1.4.0.dist-info/top_level.txt +0 -1
- {aspyx-1.4.0.dist-info → aspyx-1.5.0.dist-info}/licenses/LICENSE +0 -0
aspyx/__init__.py
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#
|
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 InstanceProvider, 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,11 +17,11 @@ __all__ = [
|
|
|
17
17
|
"Environment",
|
|
18
18
|
"injectable",
|
|
19
19
|
"factory",
|
|
20
|
-
"
|
|
20
|
+
"module",
|
|
21
21
|
"inject",
|
|
22
22
|
"create",
|
|
23
23
|
"order",
|
|
24
|
-
|
|
24
|
+
"InstanceProvider",
|
|
25
25
|
"on_init",
|
|
26
26
|
"on_running",
|
|
27
27
|
"on_destroy",
|
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
|
@@ -25,6 +25,7 @@ class AspectType(Enum):
|
|
|
25
25
|
AspectType defines the types of aspect-oriented advice that can be applied to methods.
|
|
26
26
|
|
|
27
27
|
The available types are:
|
|
28
|
+
|
|
28
29
|
- BEFORE: Advice to be executed before the method invocation.
|
|
29
30
|
- AROUND: Advice that intercepts the method invocation.
|
|
30
31
|
- AFTER: Advice to be executed after the method invocation, regardless of its outcome.
|
|
@@ -98,7 +99,7 @@ class AspectTarget(ABC):
|
|
|
98
99
|
|
|
99
100
|
# fluent
|
|
100
101
|
|
|
101
|
-
def function(self, func):
|
|
102
|
+
def function(self, func) -> AspectTarget:
|
|
102
103
|
self._function = func
|
|
103
104
|
return self
|
|
104
105
|
|
|
@@ -107,39 +108,65 @@ class AspectTarget(ABC):
|
|
|
107
108
|
|
|
108
109
|
return self
|
|
109
110
|
|
|
110
|
-
def that_are_async(self):
|
|
111
|
+
def that_are_async(self) -> AspectTarget:
|
|
111
112
|
"""
|
|
112
113
|
matches methods that are async
|
|
113
|
-
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
AspectTarget: self
|
|
114
117
|
"""
|
|
115
118
|
self._async = True
|
|
116
119
|
return self
|
|
117
120
|
|
|
118
|
-
def of_type(self, type: Type):
|
|
121
|
+
def of_type(self, type: Type) -> AspectTarget:
|
|
119
122
|
"""
|
|
120
123
|
matches methods belonging to a class or classes that are subclasses of the specified type
|
|
121
|
-
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
type (Type): the type to match against
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
AspectTarget: self
|
|
122
130
|
"""
|
|
123
131
|
self.types.append(type)
|
|
124
132
|
return self
|
|
125
133
|
|
|
126
|
-
def decorated_with(self, decorator):
|
|
134
|
+
def decorated_with(self, decorator: Callable) -> AspectTarget:
|
|
127
135
|
"""
|
|
128
136
|
matches methods or classes that are decorated with the specified decorator
|
|
129
|
-
|
|
130
|
-
:
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
decorator (Callable): the decorator callable
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
AspectTarget: self
|
|
131
143
|
"""
|
|
132
144
|
self.decorators.append(decorator)
|
|
133
145
|
return self
|
|
134
146
|
|
|
135
|
-
def matches(self, pattern: str):
|
|
147
|
+
def matches(self, pattern: str) -> AspectTarget:
|
|
136
148
|
"""
|
|
137
149
|
Matches the target against a pattern.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
pattern (str): the pattern
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
AspectTarget: self
|
|
138
156
|
"""
|
|
139
157
|
self.patterns.append(re.compile(pattern))
|
|
140
158
|
return self
|
|
141
159
|
|
|
142
|
-
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
|
+
"""
|
|
143
170
|
self.names.append(name)
|
|
144
171
|
return self
|
|
145
172
|
|
|
@@ -234,15 +261,21 @@ class MethodAspectTarget(AspectTarget):
|
|
|
234
261
|
|
|
235
262
|
return True
|
|
236
263
|
|
|
237
|
-
def methods():
|
|
264
|
+
def methods() -> AspectTarget:
|
|
238
265
|
"""
|
|
239
266
|
Create a new AspectTarget instance to define method aspect targets.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
AspectTarget: the method target
|
|
240
270
|
"""
|
|
241
271
|
return MethodAspectTarget()
|
|
242
272
|
|
|
243
|
-
def classes():
|
|
273
|
+
def classes() -> AspectTarget:
|
|
244
274
|
"""
|
|
245
275
|
Create a new AspectTarget instance to define class aspect targets.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
AspectTarget: the method target
|
|
246
279
|
"""
|
|
247
280
|
return ClassAspectTarget()
|
|
248
281
|
|
|
@@ -405,7 +438,7 @@ class Invocation:
|
|
|
405
438
|
|
|
406
439
|
def proceed(self, *args, **kwargs):
|
|
407
440
|
"""
|
|
408
|
-
Proceed to the next
|
|
441
|
+
Proceed to the next aspect in the around chain up to the original method.
|
|
409
442
|
"""
|
|
410
443
|
if len(args) > 0 or len(kwargs) > 0: # as soon as we have args, we replace the current ones
|
|
411
444
|
self.args = args
|
|
@@ -417,7 +450,7 @@ class Invocation:
|
|
|
417
450
|
|
|
418
451
|
async def proceed_async(self, *args, **kwargs):
|
|
419
452
|
"""
|
|
420
|
-
Proceed to the next
|
|
453
|
+
Proceed to the next aspect in the around chain up to the original method.
|
|
421
454
|
"""
|
|
422
455
|
if len(args) > 0 or len(kwargs) > 0: # as soon as we have args, we replace the current ones
|
|
423
456
|
self.args = args
|
|
@@ -428,6 +461,9 @@ class Invocation:
|
|
|
428
461
|
return await self.current_aspect.next.call_async(self)
|
|
429
462
|
|
|
430
463
|
class Advices:
|
|
464
|
+
"""
|
|
465
|
+
Internal utility class that collects all advice s
|
|
466
|
+
"""
|
|
431
467
|
# static data
|
|
432
468
|
|
|
433
469
|
targets: list[AspectTarget] = []
|
|
@@ -443,7 +479,13 @@ class Advices:
|
|
|
443
479
|
|
|
444
480
|
@classmethod
|
|
445
481
|
def collect(cls, clazz, member, type: AspectType, environment: Environment):
|
|
446
|
-
aspects = [
|
|
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
|
+
]
|
|
447
489
|
|
|
448
490
|
# sort according to order
|
|
449
491
|
|
|
@@ -518,8 +560,8 @@ def sanity_check(clazz: Type, name: str):
|
|
|
518
560
|
|
|
519
561
|
def advice(cls):
|
|
520
562
|
"""
|
|
521
|
-
Classes decorated with
|
|
522
|
-
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.
|
|
523
565
|
"""
|
|
524
566
|
Providers.register(ClassInstanceProvider(cls, True))
|
|
525
567
|
|
|
@@ -528,7 +570,7 @@ def advice(cls):
|
|
|
528
570
|
for name, member in TypeDescriptor.for_type(cls).methods.items():
|
|
529
571
|
decorator = next((decorator for decorator in member.decorators if decorator.decorator in [before, after, around, error]), None)
|
|
530
572
|
if decorator is not None:
|
|
531
|
-
target = decorator.args[0] #
|
|
573
|
+
target = decorator.args[0] # multiple targets are already merged in a single! check _register
|
|
532
574
|
target._clazz = cls
|
|
533
575
|
sanity_check(cls, name)
|
|
534
576
|
Advices.targets.append(target) #??
|
|
@@ -550,7 +592,7 @@ def _register(decorator, targets: list[AspectTarget], func, aspect_type: AspectT
|
|
|
550
592
|
|
|
551
593
|
def before(*targets: AspectTarget):
|
|
552
594
|
"""
|
|
553
|
-
Methods decorated with
|
|
595
|
+
Methods decorated with `@before` will be executed before the target method is invoked.
|
|
554
596
|
"""
|
|
555
597
|
def decorator(func):
|
|
556
598
|
_register(before, targets, func, AspectType.BEFORE)
|
|
@@ -561,7 +603,7 @@ def before(*targets: AspectTarget):
|
|
|
561
603
|
|
|
562
604
|
def error(*targets: AspectTarget):
|
|
563
605
|
"""
|
|
564
|
-
Methods decorated with
|
|
606
|
+
Methods decorated with `@error` will be executed if the target method raises an exception."""
|
|
565
607
|
def decorator(func):
|
|
566
608
|
_register(error, targets, func, AspectType.ERROR)
|
|
567
609
|
|
|
@@ -571,7 +613,7 @@ def error(*targets: AspectTarget):
|
|
|
571
613
|
|
|
572
614
|
def after(*targets: AspectTarget):
|
|
573
615
|
"""
|
|
574
|
-
Methods decorated with
|
|
616
|
+
Methods decorated with `@after` will be executed after the target method is invoked.
|
|
575
617
|
"""
|
|
576
618
|
def decorator(func):
|
|
577
619
|
_register(after, targets, func, AspectType.AFTER)
|
|
@@ -582,7 +624,7 @@ def after(*targets: AspectTarget):
|
|
|
582
624
|
|
|
583
625
|
def around(*targets: AspectTarget):
|
|
584
626
|
"""
|
|
585
|
-
Methods decorated with
|
|
627
|
+
Methods decorated with `@around` will be executed around the target method.
|
|
586
628
|
Every around method must accept a single parameter of type Invocation and needs to call proceed
|
|
587
629
|
on this parameter to proceed to the next around method.
|
|
588
630
|
"""
|
|
@@ -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
|
]
|
|
@@ -66,10 +66,12 @@ class ConfigurationManager:
|
|
|
66
66
|
def get(self, path: str, type: Type[T], default : Optional[T]=None) -> T:
|
|
67
67
|
"""
|
|
68
68
|
Retrieve a configuration value by path and type, with optional coercion.
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
Args:
|
|
70
71
|
path (str): The path to the configuration value, e.g. "database.host".
|
|
71
72
|
type (Type[T]): The expected type.
|
|
72
73
|
default (Optional[T]): The default value to return if the path is not found.
|
|
74
|
+
|
|
73
75
|
Returns:
|
|
74
76
|
T: The configuration value coerced to the specified type, or the default value if not found.
|
|
75
77
|
"""
|
|
@@ -119,17 +121,17 @@ class ConfigurationSource(ABC):
|
|
|
119
121
|
|
|
120
122
|
# decorator
|
|
121
123
|
|
|
122
|
-
def
|
|
124
|
+
def inject_value(key: str, default=None):
|
|
123
125
|
"""
|
|
124
126
|
Decorator to inject a configuration value into a method.
|
|
125
127
|
|
|
126
|
-
|
|
128
|
+
Args:
|
|
127
129
|
key (str): The configuration key to inject.
|
|
128
130
|
default: The default value to use if the key is not found.
|
|
129
131
|
|
|
130
132
|
"""
|
|
131
133
|
def decorator(func):
|
|
132
|
-
Decorators.add(func,
|
|
134
|
+
Decorators.add(func, inject_value, key, default)
|
|
133
135
|
|
|
134
136
|
return func
|
|
135
137
|
|
|
@@ -139,7 +141,7 @@ def value(key: str, default=None):
|
|
|
139
141
|
@order(9)
|
|
140
142
|
class ConfigurationLifecycleCallable(LifecycleCallable):
|
|
141
143
|
def __init__(self, manager: ConfigurationManager):
|
|
142
|
-
super().__init__(
|
|
144
|
+
super().__init__(inject_value, Lifecycle.ON_INJECT)
|
|
143
145
|
|
|
144
146
|
self.manager = manager
|
|
145
147
|
|