aspyx 1.5.3__py3-none-any.whl → 1.6.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/aop/aop.py +1 -1
- aspyx/di/configuration/configuration.py +0 -3
- aspyx/di/configuration/env_configuration_source.py +0 -5
- aspyx/di/configuration/yaml_configuration_source.py +0 -2
- aspyx/di/di.py +15 -7
- aspyx/exception/__init__.py +2 -1
- aspyx/exception/exception_manager.py +11 -1
- aspyx/threading/context_local.py +10 -1
- aspyx/util/__init__.py +7 -1
- aspyx/util/logger.py +6 -2
- aspyx/util/serialization.py +137 -0
- {aspyx-1.5.3.dist-info → aspyx-1.6.1.dist-info}/METADATA +32 -65
- aspyx-1.6.1.dist-info/RECORD +27 -0
- aspyx-1.5.3.dist-info/RECORD +0 -26
- {aspyx-1.5.3.dist-info → aspyx-1.6.1.dist-info}/WHEEL +0 -0
- {aspyx-1.5.3.dist-info → aspyx-1.6.1.dist-info}/licenses/LICENSE +0 -0
aspyx/di/aop/aop.py
CHANGED
aspyx/di/di.py
CHANGED
|
@@ -8,6 +8,7 @@ import logging
|
|
|
8
8
|
import importlib
|
|
9
9
|
import pkgutil
|
|
10
10
|
import sys
|
|
11
|
+
import time
|
|
11
12
|
|
|
12
13
|
from abc import abstractmethod, ABC
|
|
13
14
|
from enum import Enum
|
|
@@ -405,12 +406,10 @@ class ClassInstanceProvider(InstanceProvider):
|
|
|
405
406
|
# check constructor
|
|
406
407
|
|
|
407
408
|
init = TypeDescriptor.for_type(self.type).get_method("__init__")
|
|
408
|
-
if init is None:
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
for param in init.param_types:
|
|
413
|
-
types.append(param)
|
|
409
|
+
if init is not None:
|
|
410
|
+
self.params = len(init.param_types)
|
|
411
|
+
for param in init.param_types:
|
|
412
|
+
types.append(param)
|
|
414
413
|
|
|
415
414
|
# check @inject
|
|
416
415
|
|
|
@@ -976,7 +975,7 @@ class Environment:
|
|
|
976
975
|
"""
|
|
977
976
|
|
|
978
977
|
def add_provider(type: Type, provider: AbstractInstanceProvider):
|
|
979
|
-
Environment.logger.
|
|
978
|
+
Environment.logger.info("\tadd provider %s for %s", provider, type)
|
|
980
979
|
|
|
981
980
|
self.providers[type] = provider
|
|
982
981
|
|
|
@@ -989,6 +988,8 @@ class Environment:
|
|
|
989
988
|
if self.parent is None and env is not Boot:
|
|
990
989
|
self.parent = Boot.get_environment() # inherit environment including its manged instances!
|
|
991
990
|
|
|
991
|
+
start = time.perf_counter()
|
|
992
|
+
|
|
992
993
|
self.features = features
|
|
993
994
|
self.providers: Dict[Type, AbstractInstanceProvider] = {}
|
|
994
995
|
self.instances = []
|
|
@@ -1138,6 +1139,13 @@ class Environment:
|
|
|
1138
1139
|
for instance in self.instances:
|
|
1139
1140
|
self.execute_processors(Lifecycle.ON_RUNNING, instance)
|
|
1140
1141
|
|
|
1142
|
+
# done
|
|
1143
|
+
|
|
1144
|
+
end = time.perf_counter()
|
|
1145
|
+
|
|
1146
|
+
Environment.logger.info("created environment for class %s in %s ms, created %s instances", env.__qualname__, 1000 * (end - start), len(self.instances))
|
|
1147
|
+
|
|
1148
|
+
|
|
1141
1149
|
def is_registered_type(self, type: Type) -> bool:
|
|
1142
1150
|
provider = self.providers.get(type, None)
|
|
1143
1151
|
return provider is not None and not isinstance(provider, AmbiguousProvider)
|
aspyx/exception/__init__.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module provides exception handling functions.
|
|
3
3
|
"""
|
|
4
|
-
from .exception_manager import exception_handler, handle, ExceptionManager
|
|
4
|
+
from .exception_manager import exception_handler, handle, catch, ExceptionManager
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
7
7
|
"exception_handler",
|
|
8
8
|
"handle",
|
|
9
|
+
"catch",
|
|
9
10
|
"ExceptionManager"
|
|
10
11
|
]
|
|
@@ -32,6 +32,16 @@ def handle():
|
|
|
32
32
|
|
|
33
33
|
return decorator
|
|
34
34
|
|
|
35
|
+
def catch():
|
|
36
|
+
"""
|
|
37
|
+
Any method annotated with @catch will be registered as an exception handler method.
|
|
38
|
+
"""
|
|
39
|
+
def decorator(func):
|
|
40
|
+
Decorators.add(func, handle)
|
|
41
|
+
return func
|
|
42
|
+
|
|
43
|
+
return decorator
|
|
44
|
+
|
|
35
45
|
class Handler:
|
|
36
46
|
# constructor
|
|
37
47
|
|
|
@@ -174,7 +184,7 @@ class ExceptionManager:
|
|
|
174
184
|
exception (BaseException): the exception
|
|
175
185
|
|
|
176
186
|
Returns:
|
|
177
|
-
BaseException: the resulting exception
|
|
187
|
+
BaseException: the resulting - possible transformed - exception
|
|
178
188
|
"""
|
|
179
189
|
chain = self.get_handlers(type(exception))
|
|
180
190
|
if chain is not None:
|
aspyx/threading/context_local.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
context utility
|
|
3
|
+
"""
|
|
1
4
|
import contextvars
|
|
2
5
|
from contextlib import contextmanager
|
|
3
6
|
from typing import Generic, Optional, TypeVar, Any
|
|
@@ -30,12 +33,18 @@ class ContextLocal(Generic[T]):
|
|
|
30
33
|
|
|
31
34
|
Args:
|
|
32
35
|
value: the value
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
a token that can be used as an argument to `reset`
|
|
33
39
|
"""
|
|
34
40
|
return self.var.set(value)
|
|
35
41
|
|
|
36
42
|
def reset(self, token) -> None:
|
|
37
43
|
"""
|
|
38
44
|
clear the value in the current thread
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
token: the token to clear
|
|
39
48
|
"""
|
|
40
49
|
self.var.reset(token)
|
|
41
50
|
|
|
@@ -45,4 +54,4 @@ class ContextLocal(Generic[T]):
|
|
|
45
54
|
try:
|
|
46
55
|
yield
|
|
47
56
|
finally:
|
|
48
|
-
self.reset(token)
|
|
57
|
+
self.reset(token)
|
aspyx/util/__init__.py
CHANGED
|
@@ -3,8 +3,14 @@ This module provides utility functions.
|
|
|
3
3
|
"""
|
|
4
4
|
from .stringbuilder import StringBuilder
|
|
5
5
|
from .logger import Logger
|
|
6
|
+
from .serialization import TypeSerializer, TypeDeserializer, get_serializer, get_deserializer
|
|
6
7
|
|
|
7
8
|
__all__ = [
|
|
8
9
|
"StringBuilder",
|
|
9
|
-
"Logger"
|
|
10
|
+
"Logger",
|
|
11
|
+
|
|
12
|
+
"TypeSerializer",
|
|
13
|
+
"TypeDeserializer",
|
|
14
|
+
"get_serializer",
|
|
15
|
+
"get_deserializer"
|
|
10
16
|
]
|
aspyx/util/logger.py
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Logging utility class
|
|
3
|
+
"""
|
|
1
4
|
import logging
|
|
5
|
+
import sys
|
|
2
6
|
from typing import Optional, Dict
|
|
3
7
|
|
|
4
8
|
class Logger:
|
|
@@ -8,8 +12,8 @@ class Logger:
|
|
|
8
12
|
def configure(cls,
|
|
9
13
|
default_level: int = logging.INFO,
|
|
10
14
|
format: str = "[%(asctime)s] %(levelname)s in %(filename)s:%(lineno)d - %(message)s",
|
|
11
|
-
levels: Optional[Dict[str, int]] = None):
|
|
15
|
+
levels: Optional[Dict[str, int]] = None, stream=sys.stdout):
|
|
12
16
|
logging.basicConfig(level=default_level, format=format)
|
|
13
17
|
if levels is not None:
|
|
14
18
|
for name, level in levels.items():
|
|
15
|
-
logging.getLogger(name).setLevel(level)
|
|
19
|
+
logging.getLogger(name).setLevel(level)
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
deserialization functions
|
|
3
|
+
"""
|
|
4
|
+
from dataclasses import is_dataclass, fields
|
|
5
|
+
from functools import lru_cache
|
|
6
|
+
from typing import get_origin, get_args, Union
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
class TypeDeserializer:
|
|
11
|
+
# constructor
|
|
12
|
+
|
|
13
|
+
def __init__(self, typ):
|
|
14
|
+
self.typ = typ
|
|
15
|
+
self.deserializer = self._build_deserializer(typ)
|
|
16
|
+
|
|
17
|
+
def __call__(self, value):
|
|
18
|
+
return self.deserializer(value)
|
|
19
|
+
|
|
20
|
+
# internal
|
|
21
|
+
|
|
22
|
+
def _build_deserializer(self, typ):
|
|
23
|
+
origin = get_origin(typ)
|
|
24
|
+
args = get_args(typ)
|
|
25
|
+
|
|
26
|
+
if origin is Union:
|
|
27
|
+
deserializers = [TypeDeserializer(arg) for arg in args if arg is not type(None)]
|
|
28
|
+
def deser_union(value):
|
|
29
|
+
if value is None:
|
|
30
|
+
return None
|
|
31
|
+
for d in deserializers:
|
|
32
|
+
try:
|
|
33
|
+
return d(value)
|
|
34
|
+
except Exception:
|
|
35
|
+
continue
|
|
36
|
+
return value
|
|
37
|
+
return deser_union
|
|
38
|
+
|
|
39
|
+
if isinstance(typ, type) and issubclass(typ, BaseModel):
|
|
40
|
+
return typ.model_validate
|
|
41
|
+
|
|
42
|
+
if is_dataclass(typ):
|
|
43
|
+
field_deserializers = {f.name: TypeDeserializer(f.type) for f in fields(typ)}
|
|
44
|
+
def deser_dataclass(value):
|
|
45
|
+
if is_dataclass(value):
|
|
46
|
+
return value
|
|
47
|
+
|
|
48
|
+
return typ(**{
|
|
49
|
+
k: field_deserializers[k](v) for k, v in value.items()
|
|
50
|
+
})
|
|
51
|
+
return deser_dataclass
|
|
52
|
+
|
|
53
|
+
if origin is list:
|
|
54
|
+
item_deser = TypeDeserializer(args[0]) if args else lambda x: x
|
|
55
|
+
return lambda v: [item_deser(item) for item in v]
|
|
56
|
+
|
|
57
|
+
if origin is dict:
|
|
58
|
+
key_deser = TypeDeserializer(args[0]) if args else lambda x: x
|
|
59
|
+
val_deser = TypeDeserializer(args[1]) if len(args) > 1 else lambda x: x
|
|
60
|
+
return lambda v: {key_deser(k): val_deser(val) for k, val in v.items()}
|
|
61
|
+
|
|
62
|
+
# Fallback
|
|
63
|
+
return lambda v: v
|
|
64
|
+
|
|
65
|
+
class TypeSerializer:
|
|
66
|
+
def __init__(self, typ):
|
|
67
|
+
self.typ = typ
|
|
68
|
+
self.serializer = self._build_serializer(typ)
|
|
69
|
+
|
|
70
|
+
def __call__(self, value):
|
|
71
|
+
return self.serializer(value)
|
|
72
|
+
|
|
73
|
+
def _build_serializer(self, typ):
|
|
74
|
+
origin = get_origin(typ)
|
|
75
|
+
args = get_args(typ)
|
|
76
|
+
|
|
77
|
+
if origin is Union:
|
|
78
|
+
serializers = [TypeSerializer(arg) for arg in args if arg is not type(None)]
|
|
79
|
+
def ser_union(value):
|
|
80
|
+
if value is None:
|
|
81
|
+
return None
|
|
82
|
+
for s in serializers:
|
|
83
|
+
try:
|
|
84
|
+
return s(value)
|
|
85
|
+
except Exception:
|
|
86
|
+
continue
|
|
87
|
+
return value
|
|
88
|
+
return ser_union
|
|
89
|
+
|
|
90
|
+
if isinstance(typ, type) and issubclass(typ, BaseModel):
|
|
91
|
+
return lambda v: v.model_dump() if v is not None else None
|
|
92
|
+
|
|
93
|
+
if is_dataclass(typ):
|
|
94
|
+
field_serializers = {f.name: TypeSerializer(f.type) for f in fields(typ)}
|
|
95
|
+
def ser_dataclass(obj):
|
|
96
|
+
if obj is None:
|
|
97
|
+
return None
|
|
98
|
+
return {k: field_serializers[k](getattr(obj, k)) for k in field_serializers}
|
|
99
|
+
return ser_dataclass
|
|
100
|
+
|
|
101
|
+
if origin is list:
|
|
102
|
+
item_ser = TypeSerializer(args[0]) if args else lambda x: x
|
|
103
|
+
return lambda v: [item_ser(item) for item in v] if v is not None else None
|
|
104
|
+
|
|
105
|
+
if origin is dict:
|
|
106
|
+
key_ser = TypeSerializer(args[0]) if args else lambda x: x
|
|
107
|
+
val_ser = TypeSerializer(args[1]) if len(args) > 1 else lambda x: x
|
|
108
|
+
return lambda v: {key_ser(k): val_ser(val) for k, val in v.items()} if v is not None else None
|
|
109
|
+
|
|
110
|
+
# Fallback: primitive Typen oder unbekannt
|
|
111
|
+
return lambda v: v
|
|
112
|
+
|
|
113
|
+
@lru_cache(maxsize=512)
|
|
114
|
+
def get_deserializer(typ) -> TypeDeserializer:
|
|
115
|
+
"""
|
|
116
|
+
return a function that is able to deserialize a value of the specified type
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
typ: the type
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
|
|
123
|
+
"""
|
|
124
|
+
return TypeDeserializer(typ)
|
|
125
|
+
|
|
126
|
+
@lru_cache(maxsize=512)
|
|
127
|
+
def get_serializer(typ) -> TypeSerializer:
|
|
128
|
+
"""
|
|
129
|
+
return a function that is able to deserialize a value of the specified type
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
typ: the type
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
return TypeSerializer(typ)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aspyx
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.1
|
|
4
4
|
Summary: A DI and AOP library for Python
|
|
5
5
|
Author-email: Andreas Ernst <andreas.ernst7@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -26,6 +26,8 @@ License: MIT License
|
|
|
26
26
|
SOFTWARE.
|
|
27
27
|
License-File: LICENSE
|
|
28
28
|
Requires-Python: >=3.9
|
|
29
|
+
Requires-Dist: cachetools~=5.5.2
|
|
30
|
+
Requires-Dist: pydantic<3.0,>=2.0
|
|
29
31
|
Requires-Dist: python-dotenv~=1.1.0
|
|
30
32
|
Requires-Dist: pyyaml~=6.0.2
|
|
31
33
|
Description-Content-Type: text/markdown
|
|
@@ -115,9 +117,6 @@ from aspyx.di import injectable, on_init, on_destroy, module, Environment
|
|
|
115
117
|
|
|
116
118
|
@injectable()
|
|
117
119
|
class Foo:
|
|
118
|
-
def __init__(self):
|
|
119
|
-
pass
|
|
120
|
-
|
|
121
120
|
def hello(self, msg: str):
|
|
122
121
|
print(f"hello {msg}")
|
|
123
122
|
|
|
@@ -136,9 +135,7 @@ class Bar:
|
|
|
136
135
|
|
|
137
136
|
@module()
|
|
138
137
|
class SampleModule:
|
|
139
|
-
|
|
140
|
-
pass
|
|
141
|
-
|
|
138
|
+
pass
|
|
142
139
|
|
|
143
140
|
# create environment
|
|
144
141
|
|
|
@@ -146,7 +143,7 @@ environment = Environment(SampleModule)
|
|
|
146
143
|
|
|
147
144
|
# fetch an instance
|
|
148
145
|
|
|
149
|
-
bar =
|
|
146
|
+
bar = environment.get(Bar)
|
|
150
147
|
|
|
151
148
|
bar.foo.hello("world")
|
|
152
149
|
```
|
|
@@ -204,10 +201,7 @@ class Foo:
|
|
|
204
201
|
def __init__(self):
|
|
205
202
|
pass
|
|
206
203
|
```
|
|
207
|
-
|
|
208
|
-
All referenced types will be injected by the environment.
|
|
209
|
-
|
|
210
|
-
Only eligible types are allowed, of course!
|
|
204
|
+
If the class defines a constructor, all parameters - which are expected to be registered as well - will be injected automatically.
|
|
211
205
|
|
|
212
206
|
The decorator accepts the keyword arguments
|
|
213
207
|
- `eager : boolean`
|
|
@@ -233,9 +227,6 @@ Classes that implement the `Factory` base class and are annotated with `@factory
|
|
|
233
227
|
```python
|
|
234
228
|
@factory()
|
|
235
229
|
class TestFactory(Factory[Foo]):
|
|
236
|
-
def __init__(self):
|
|
237
|
-
pass
|
|
238
|
-
|
|
239
230
|
def create(self) -> Foo:
|
|
240
231
|
return Foo()
|
|
241
232
|
```
|
|
@@ -250,9 +241,6 @@ Any `injectable` can define methods decorated with `@create()`, that will create
|
|
|
250
241
|
```python
|
|
251
242
|
@injectable()
|
|
252
243
|
class Foo:
|
|
253
|
-
def __init__(self):
|
|
254
|
-
pass
|
|
255
|
-
|
|
256
244
|
@create(scope="request")
|
|
257
245
|
def create(self) -> Baz:
|
|
258
246
|
return Baz()
|
|
@@ -288,8 +276,7 @@ constructor type argument called `module`.
|
|
|
288
276
|
```python
|
|
289
277
|
@module()
|
|
290
278
|
class SampleModule:
|
|
291
|
-
|
|
292
|
-
pass
|
|
279
|
+
pass
|
|
293
280
|
```
|
|
294
281
|
|
|
295
282
|
A module is a regular injectable class decorated with `@module` that controls the discovery of injectable classes, by filtering classes according to their module location relative to this class.
|
|
@@ -309,13 +296,11 @@ By adding the parameter `features: list[str]`, it is possible to filter injectab
|
|
|
309
296
|
@injectable()
|
|
310
297
|
@conditional(requires_feature("dev"))
|
|
311
298
|
class DevOnly:
|
|
312
|
-
|
|
313
|
-
pass
|
|
299
|
+
pass
|
|
314
300
|
|
|
315
301
|
@module()
|
|
316
302
|
class SampleModule():
|
|
317
|
-
|
|
318
|
-
pass
|
|
303
|
+
pass
|
|
319
304
|
|
|
320
305
|
environment = Environment(SampleModule, features=["dev"])
|
|
321
306
|
```
|
|
@@ -327,8 +312,7 @@ By adding an `imports: list[Type]` parameter, specifying other module types, it
|
|
|
327
312
|
```python
|
|
328
313
|
@module()
|
|
329
314
|
class SampleModule(imports=[OtherModule]):
|
|
330
|
-
|
|
331
|
-
pass
|
|
315
|
+
pass
|
|
332
316
|
```
|
|
333
317
|
|
|
334
318
|
Another possibility is to add a parent environment as an `Environment` constructor parameter
|
|
@@ -390,11 +374,8 @@ Different decorators are implemented, that call methods with computed values
|
|
|
390
374
|
```python
|
|
391
375
|
@injectable()
|
|
392
376
|
class Foo:
|
|
393
|
-
def __init__(self):
|
|
394
|
-
pass
|
|
395
|
-
|
|
396
377
|
@inject_environment()
|
|
397
|
-
def
|
|
378
|
+
def set_environment(self, env: Environment):
|
|
398
379
|
...
|
|
399
380
|
|
|
400
381
|
@inject()
|
|
@@ -584,9 +565,6 @@ A handy decorator `@synchronized` in combination with the respective advice is i
|
|
|
584
565
|
```python
|
|
585
566
|
@injectable()
|
|
586
567
|
class Foo:
|
|
587
|
-
def __init__(self):
|
|
588
|
-
pass
|
|
589
|
-
|
|
590
568
|
@synchronized()
|
|
591
569
|
def execute_synchronized(self):
|
|
592
570
|
...
|
|
@@ -599,9 +577,6 @@ It is possible to inject configuration values, by decorating methods with `@inje
|
|
|
599
577
|
```python
|
|
600
578
|
@injectable()
|
|
601
579
|
class Foo:
|
|
602
|
-
def __init__(self):
|
|
603
|
-
pass
|
|
604
|
-
|
|
605
580
|
@inject_value("HOME")
|
|
606
581
|
def inject_home(self, os: str):
|
|
607
582
|
...
|
|
@@ -613,13 +588,13 @@ Configuration values are managed centrally using a `ConfigurationManager`, which
|
|
|
613
588
|
|
|
614
589
|
```python
|
|
615
590
|
class ConfigurationSource(ABC):
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
...
|
|
591
|
+
@inject()
|
|
592
|
+
def set_manager(self, manager: ConfigurationManager):
|
|
593
|
+
manager._register(self)
|
|
620
594
|
|
|
621
595
|
@abstractmethod
|
|
622
596
|
def load(self) -> dict:
|
|
597
|
+
pass
|
|
623
598
|
```
|
|
624
599
|
|
|
625
600
|
The `load` method is able to return a tree-like structure by returning a `dict`.
|
|
@@ -668,11 +643,6 @@ Typically you create the required configuration sources in an environment class,
|
|
|
668
643
|
```python
|
|
669
644
|
@module()
|
|
670
645
|
class SampleModule:
|
|
671
|
-
# constructor
|
|
672
|
-
|
|
673
|
-
def __init__(self):
|
|
674
|
-
pass
|
|
675
|
-
|
|
676
646
|
@create()
|
|
677
647
|
def create_env_source(self) -> EnvConfigurationSource:
|
|
678
648
|
return EnvConfigurationSource()
|
|
@@ -731,41 +701,36 @@ def transactional(scope):
|
|
|
731
701
|
The class `ExceptionManager` is used to collect dynamic handlers for specific exceptions and is able to dispatch to the concrete functions
|
|
732
702
|
given a specific exception.
|
|
733
703
|
|
|
734
|
-
The handlers are declared by annoting a class with `@exception_handler` and decorating specific methods with `@
|
|
704
|
+
The handlers are declared by annoting a class with `@exception_handler` and decorating specific methods with `@catch`
|
|
735
705
|
|
|
736
706
|
**Example**:
|
|
707
|
+
|
|
737
708
|
```python
|
|
738
709
|
class DerivedException(Exception):
|
|
739
710
|
def __init__(self):
|
|
740
711
|
pass
|
|
741
712
|
|
|
713
|
+
|
|
742
714
|
@module()
|
|
743
715
|
class SampleModule:
|
|
744
|
-
# constructor
|
|
745
|
-
|
|
746
|
-
def __init__(self):
|
|
747
|
-
pass
|
|
748
|
-
|
|
749
716
|
@create()
|
|
750
717
|
def create_exception_manager(self) -> ExceptionManager:
|
|
751
718
|
return ExceptionManager()
|
|
752
719
|
|
|
720
|
+
|
|
753
721
|
@injectable()
|
|
754
722
|
@exception_handler()
|
|
755
723
|
class TestExceptionHandler:
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
@handle()
|
|
760
|
-
def handle_derived_exception(self, exception: DerivedException):
|
|
724
|
+
@catch()
|
|
725
|
+
def catch_derived_exception(self, exception: DerivedException):
|
|
761
726
|
ExceptionManager.proceed()
|
|
762
727
|
|
|
763
|
-
@
|
|
764
|
-
def
|
|
728
|
+
@catch()
|
|
729
|
+
def catch_exception(self, exception: Exception):
|
|
765
730
|
pass
|
|
766
731
|
|
|
767
|
-
@
|
|
768
|
-
def
|
|
732
|
+
@catch()
|
|
733
|
+
def catch_base_exception(self, exception: BaseException):
|
|
769
734
|
pass
|
|
770
735
|
|
|
771
736
|
|
|
@@ -778,9 +743,10 @@ class ExceptionAdvice:
|
|
|
778
743
|
def handle_error(self, invocation: Invocation):
|
|
779
744
|
self.exceptionManager.handle(invocation.exception)
|
|
780
745
|
|
|
781
|
-
environment = Environment(SampleEnvironment)
|
|
782
746
|
|
|
783
|
-
environment
|
|
747
|
+
environment = Environment(SampleEnvironment)
|
|
748
|
+
|
|
749
|
+
environment.read(ExceptionManager).handle(DerivedException())
|
|
784
750
|
```
|
|
785
751
|
|
|
786
752
|
The exception maanger will first call the most appropriate method.
|
|
@@ -792,9 +758,6 @@ Together with a simple around advice we can now add exception handling to any me
|
|
|
792
758
|
```python
|
|
793
759
|
@injectable()
|
|
794
760
|
class Service:
|
|
795
|
-
def __init__(self):
|
|
796
|
-
pass
|
|
797
|
-
|
|
798
761
|
def throw(self):
|
|
799
762
|
raise DerivedException()
|
|
800
763
|
|
|
@@ -837,3 +800,7 @@ class ExceptionAdvice:
|
|
|
837
800
|
**1.4.1**
|
|
838
801
|
|
|
839
802
|
- mkdocs
|
|
803
|
+
|
|
804
|
+
**1.6.1**
|
|
805
|
+
|
|
806
|
+
- default constructors not requires anymore
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
aspyx/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
2
|
+
aspyx/di/__init__.py,sha256=AGVU2VBWQyBxSssvbk_GOKrYWIYtcmSoIlupz-Oqxi4,1138
|
|
3
|
+
aspyx/di/di.py,sha256=HgmLl84s3FLKUs6UO5ULzLcwuyDIzo0Ev8WrPQm3INg,44821
|
|
4
|
+
aspyx/di/aop/__init__.py,sha256=rn6LSpzFtUOlgaBATyhLRWBzFmZ6XoVKA9B8SgQzYEI,746
|
|
5
|
+
aspyx/di/aop/aop.py,sha256=y300DG713Gcn97CdTKuBdFL2jaS5ouW6J0azZk0Byws,19181
|
|
6
|
+
aspyx/di/configuration/__init__.py,sha256=flM9A79J2wfA5I8goQbxs4tTqYustR9tn_9s0YO2WJQ,484
|
|
7
|
+
aspyx/di/configuration/configuration.py,sha256=GVk_oGi7H0COBD_q__ZydIXyl7PgxvwW2Lplr-t44ck,4337
|
|
8
|
+
aspyx/di/configuration/env_configuration_source.py,sha256=mVZXRTBn-zbkqGMV9Yn5EZsae4MlpAzc8nVozXaVwnE,1375
|
|
9
|
+
aspyx/di/configuration/yaml_configuration_source.py,sha256=FmtM3-Xi1waxurdI5NVWUNTCcrv6q_azk5XniOL0Pg0,522
|
|
10
|
+
aspyx/di/threading/__init__.py,sha256=qrWdaq7MewQ2UmZy4J0Dn6BhY-ahfiG3xsv-EHqoqSE,191
|
|
11
|
+
aspyx/di/threading/synchronized.py,sha256=6JOg5BXWrRIS5nRPH9iWR7T-kUglO4qWBQpLwhy99pI,1325
|
|
12
|
+
aspyx/exception/__init__.py,sha256=HfK0kk1Tcw9QaUYgIyMeBFDAIE83pkTrIGYUnoKJXPE,231
|
|
13
|
+
aspyx/exception/exception_manager.py,sha256=wTxLjSVZ_vYfeo06PKZZS9hbrv_dRCwQotSwowUHLZY,5475
|
|
14
|
+
aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
|
|
15
|
+
aspyx/reflection/proxy.py,sha256=1-pgw-TNORFXbV0gowFZqGd-bcWv1ny69bJhq8TLsKs,2761
|
|
16
|
+
aspyx/reflection/reflection.py,sha256=BcqXJcMO36Gzq71O4aBsMqPynmBhYLI8V6J7DOInPAM,9142
|
|
17
|
+
aspyx/threading/__init__.py,sha256=YWqLk-MOtSg4i3cdzRZUBR25okbbftRRxkaEdfrdMZo,207
|
|
18
|
+
aspyx/threading/context_local.py,sha256=2I-942IHbR0jCrM1N6Mo56VJwuNWMs2f3R8NJsHygBI,1270
|
|
19
|
+
aspyx/threading/thread_local.py,sha256=86dNtbA4k2B-rNUUnZgn3_pU0DAojgLrRnh8RL6zf1E,1196
|
|
20
|
+
aspyx/util/__init__.py,sha256=B7QK3alguZqExBFGzx-OpHpYeoIc3ZGAL7pJnq8NNvI,352
|
|
21
|
+
aspyx/util/logger.py,sha256=Hti5JyajdPXlf_1jvVT3e6Gf9eLyAsIVJRdNBMahbJs,608
|
|
22
|
+
aspyx/util/serialization.py,sha256=BRsg-2S7E3IukJqxZGm7FvjTvV5d6rcyrpJT3aKoVmM,4252
|
|
23
|
+
aspyx/util/stringbuilder.py,sha256=a-0T4YEXSJFUuQ3ztKN1ZPARkh8dIGMSkNEEJHRN7dc,856
|
|
24
|
+
aspyx-1.6.1.dist-info/METADATA,sha256=H7d-mtHxb57xtVyxbvcz2385TvqslMxGHXzCtbDxdk8,26017
|
|
25
|
+
aspyx-1.6.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
26
|
+
aspyx-1.6.1.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
27
|
+
aspyx-1.6.1.dist-info/RECORD,,
|
aspyx-1.5.3.dist-info/RECORD
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
aspyx/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
2
|
-
aspyx/di/__init__.py,sha256=AGVU2VBWQyBxSssvbk_GOKrYWIYtcmSoIlupz-Oqxi4,1138
|
|
3
|
-
aspyx/di/di.py,sha256=V_BAV6DmFCoepPqAXhBz2GW6NwYaKokHb03HMz6A5Sw,44639
|
|
4
|
-
aspyx/di/aop/__init__.py,sha256=rn6LSpzFtUOlgaBATyhLRWBzFmZ6XoVKA9B8SgQzYEI,746
|
|
5
|
-
aspyx/di/aop/aop.py,sha256=RaZRmQ1Ie6jZqYm0X93hWQ89xHn8vpDSnsmRgfAV68k,19180
|
|
6
|
-
aspyx/di/configuration/__init__.py,sha256=flM9A79J2wfA5I8goQbxs4tTqYustR9tn_9s0YO2WJQ,484
|
|
7
|
-
aspyx/di/configuration/configuration.py,sha256=cXW40bPXiUZ9hUtBoZkSATT3nLrDPWsSqxtgASIBQaM,4375
|
|
8
|
-
aspyx/di/configuration/env_configuration_source.py,sha256=FXPvREzq2ZER6_GG5xdpx154TQQDxZVf7LW7cvaylAk,1446
|
|
9
|
-
aspyx/di/configuration/yaml_configuration_source.py,sha256=NDl3SeoLMNVlzHgfP-Ysvhco1tRew_zFnBL5gGy2WRk,550
|
|
10
|
-
aspyx/di/threading/__init__.py,sha256=qrWdaq7MewQ2UmZy4J0Dn6BhY-ahfiG3xsv-EHqoqSE,191
|
|
11
|
-
aspyx/di/threading/synchronized.py,sha256=6JOg5BXWrRIS5nRPH9iWR7T-kUglO4qWBQpLwhy99pI,1325
|
|
12
|
-
aspyx/exception/__init__.py,sha256=OZwv-C3ZHD0Eg1rohCQMj575WLJ7lfYuk6PZD6sh1MA,211
|
|
13
|
-
aspyx/exception/exception_manager.py,sha256=CVOjdI-y4m5GiE3TvSbwLpXXoAYqEyQLMN7jQhJYRn8,5228
|
|
14
|
-
aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
|
|
15
|
-
aspyx/reflection/proxy.py,sha256=1-pgw-TNORFXbV0gowFZqGd-bcWv1ny69bJhq8TLsKs,2761
|
|
16
|
-
aspyx/reflection/reflection.py,sha256=BcqXJcMO36Gzq71O4aBsMqPynmBhYLI8V6J7DOInPAM,9142
|
|
17
|
-
aspyx/threading/__init__.py,sha256=YWqLk-MOtSg4i3cdzRZUBR25okbbftRRxkaEdfrdMZo,207
|
|
18
|
-
aspyx/threading/context_local.py,sha256=IBjqfmuGgQSMB3EhYW9etcKEnErMQKaAVHD45s1mqOA,1111
|
|
19
|
-
aspyx/threading/thread_local.py,sha256=86dNtbA4k2B-rNUUnZgn3_pU0DAojgLrRnh8RL6zf1E,1196
|
|
20
|
-
aspyx/util/__init__.py,sha256=Uu6uZySb5v9WHS8ZooHmRXAxK_BhhKPUotv-LZyGQAI,165
|
|
21
|
-
aspyx/util/logger.py,sha256=S358l7RpBTSZtN8nivVLgX6HAp32vOQV-SDHDH0n9gc,547
|
|
22
|
-
aspyx/util/stringbuilder.py,sha256=a-0T4YEXSJFUuQ3ztKN1ZPARkh8dIGMSkNEEJHRN7dc,856
|
|
23
|
-
aspyx-1.5.3.dist-info/METADATA,sha256=hPk9M5CUZDxxvrzf0s1hTnpfw-XTJu1gPqgTzAVf6w0,26490
|
|
24
|
-
aspyx-1.5.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
25
|
-
aspyx-1.5.3.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
26
|
-
aspyx-1.5.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|