aspyx 1.5.1__py3-none-any.whl → 1.5.2__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 +45 -9
- aspyx/di/di.py +6 -7
- aspyx/di/threading/synchronized.py +3 -1
- aspyx/exception/exception_manager.py +19 -17
- aspyx/reflection/proxy.py +10 -3
- aspyx/reflection/reflection.py +8 -4
- {aspyx-1.5.1.dist-info → aspyx-1.5.2.dist-info}/METADATA +1 -1
- {aspyx-1.5.1.dist-info → aspyx-1.5.2.dist-info}/RECORD +10 -10
- {aspyx-1.5.1.dist-info → aspyx-1.5.2.dist-info}/WHEEL +0 -0
- {aspyx-1.5.1.dist-info → aspyx-1.5.2.dist-info}/licenses/LICENSE +0 -0
aspyx/di/aop/aop.py
CHANGED
|
@@ -3,6 +3,7 @@ This module provides aspect-oriented programming (AOP) capabilities for Python a
|
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
|
+
import functools
|
|
6
7
|
from abc import ABC, abstractmethod
|
|
7
8
|
import inspect
|
|
8
9
|
import re
|
|
@@ -64,7 +65,7 @@ class AspectTarget(ABC):
|
|
|
64
65
|
def __init__(self):
|
|
65
66
|
self._clazz = None
|
|
66
67
|
self._instance = None
|
|
67
|
-
self._async =
|
|
68
|
+
self._async : Optional[bool] = None
|
|
68
69
|
self._function = None
|
|
69
70
|
self._type = None
|
|
70
71
|
|
|
@@ -118,6 +119,16 @@ class AspectTarget(ABC):
|
|
|
118
119
|
self._async = True
|
|
119
120
|
return self
|
|
120
121
|
|
|
122
|
+
def that_are_sync(self) -> AspectTarget:
|
|
123
|
+
"""
|
|
124
|
+
matches methods that are sync
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
AspectTarget: self
|
|
128
|
+
"""
|
|
129
|
+
self._async = False
|
|
130
|
+
return self
|
|
131
|
+
|
|
121
132
|
def of_type(self, type: Type) -> AspectTarget:
|
|
122
133
|
"""
|
|
123
134
|
matches methods belonging to a class or classes that are subclasses of the specified type
|
|
@@ -217,10 +228,14 @@ class MethodAspectTarget(AspectTarget):
|
|
|
217
228
|
An AspectTarget matching methods
|
|
218
229
|
"""
|
|
219
230
|
|
|
220
|
-
|
|
231
|
+
__slots__ = ["belonging_to"]
|
|
221
232
|
|
|
222
|
-
|
|
233
|
+
# constructor
|
|
223
234
|
|
|
235
|
+
def __init__(self):
|
|
236
|
+
super().__init__()
|
|
237
|
+
|
|
238
|
+
self.belonging_to : list[ClassAspectTarget] = []
|
|
224
239
|
# public
|
|
225
240
|
|
|
226
241
|
def _matches_self(self, clazz : Type, func):
|
|
@@ -228,9 +243,21 @@ class MethodAspectTarget(AspectTarget):
|
|
|
228
243
|
|
|
229
244
|
method_descriptor = descriptor.get_method(func.__name__)
|
|
230
245
|
|
|
246
|
+
# classes
|
|
247
|
+
|
|
248
|
+
if len(self.belonging_to) > 0:
|
|
249
|
+
match = False
|
|
250
|
+
for classes in self.belonging_to:
|
|
251
|
+
if classes._matches(clazz, func):
|
|
252
|
+
match = True
|
|
253
|
+
break
|
|
254
|
+
|
|
255
|
+
if not match:
|
|
256
|
+
return False
|
|
257
|
+
|
|
231
258
|
# async
|
|
232
259
|
|
|
233
|
-
if self._async is not method_descriptor.is_async():
|
|
260
|
+
if self._async is not None and self._async is not method_descriptor.is_async():
|
|
234
261
|
return False
|
|
235
262
|
|
|
236
263
|
# type
|
|
@@ -261,7 +288,14 @@ class MethodAspectTarget(AspectTarget):
|
|
|
261
288
|
|
|
262
289
|
return True
|
|
263
290
|
|
|
264
|
-
|
|
291
|
+
# fluent
|
|
292
|
+
|
|
293
|
+
def declared_by(self, classes: ClassAspectTarget) -> MethodAspectTarget:
|
|
294
|
+
self.belonging_to.append(classes)
|
|
295
|
+
|
|
296
|
+
return self
|
|
297
|
+
|
|
298
|
+
def methods() -> MethodAspectTarget:
|
|
265
299
|
"""
|
|
266
300
|
Create a new AspectTarget instance to define method aspect targets.
|
|
267
301
|
|
|
@@ -270,7 +304,7 @@ def methods() -> AspectTarget:
|
|
|
270
304
|
"""
|
|
271
305
|
return MethodAspectTarget()
|
|
272
306
|
|
|
273
|
-
def classes() ->
|
|
307
|
+
def classes() -> ClassAspectTarget:
|
|
274
308
|
"""
|
|
275
309
|
Create a new AspectTarget instance to define class aspect targets.
|
|
276
310
|
|
|
@@ -563,7 +597,7 @@ def advice(cls):
|
|
|
563
597
|
Classes decorated with `@advice` are treated as advice classes.
|
|
564
598
|
They can contain methods decorated with `@before`, `@after`, `@around`, or `@error` to define aspects.
|
|
565
599
|
"""
|
|
566
|
-
Providers.register(ClassInstanceProvider(cls, True))
|
|
600
|
+
#Providers.register(ClassInstanceProvider(cls, True))
|
|
567
601
|
|
|
568
602
|
Decorators.add(cls, advice)
|
|
569
603
|
|
|
@@ -673,13 +707,15 @@ class AdviceProcessor(PostProcessor):
|
|
|
673
707
|
for member, aspects in aspect_dict.items():
|
|
674
708
|
Environment.logger.debug("add aspects for %s:%s", type(instance), member.__name__)
|
|
675
709
|
|
|
676
|
-
def wrap(jp):
|
|
710
|
+
def wrap(jp, member=member):
|
|
711
|
+
@functools.wraps(member)
|
|
677
712
|
def sync_wrapper(*args, **kwargs):
|
|
678
713
|
return Invocation(member, jp).call(*args, **kwargs)
|
|
679
714
|
|
|
680
715
|
return sync_wrapper
|
|
681
716
|
|
|
682
|
-
def wrap_async(jp):
|
|
717
|
+
def wrap_async(jp, member=member):
|
|
718
|
+
@functools.wraps(member)
|
|
683
719
|
async def async_wrapper(*args, **kwargs):
|
|
684
720
|
return await Invocation(member, jp).call_async(*args, **kwargs)
|
|
685
721
|
|
aspyx/di/di.py
CHANGED
|
@@ -552,7 +552,7 @@ class PostProcessor(LifecycleProcessor):
|
|
|
552
552
|
"""
|
|
553
553
|
__slots__ = []
|
|
554
554
|
|
|
555
|
-
|
|
555
|
+
@abstractmethod
|
|
556
556
|
def process(self, instance: object, environment: Environment):
|
|
557
557
|
pass
|
|
558
558
|
|
|
@@ -689,7 +689,7 @@ class Providers:
|
|
|
689
689
|
return True
|
|
690
690
|
|
|
691
691
|
def is_injectable(type: Type) -> bool:
|
|
692
|
-
if type
|
|
692
|
+
if type in [object, ABC]:
|
|
693
693
|
return False
|
|
694
694
|
|
|
695
695
|
if inspect.isabstract(type):
|
|
@@ -998,13 +998,13 @@ class Environment:
|
|
|
998
998
|
# inherit providers from parent
|
|
999
999
|
|
|
1000
1000
|
for provider_type, inherited_provider in self.parent.providers.items():
|
|
1001
|
+
provider = inherited_provider
|
|
1001
1002
|
if inherited_provider.get_scope() == "environment":
|
|
1002
1003
|
# replace with own environment instance provider
|
|
1003
1004
|
provider = EnvironmentInstanceProvider(self, cast(EnvironmentInstanceProvider, inherited_provider).provider)
|
|
1004
1005
|
provider.dependencies = [] # ??
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
add_provider(provider_type, inherited_provider)
|
|
1006
|
+
|
|
1007
|
+
add_provider(provider_type, provider)
|
|
1008
1008
|
|
|
1009
1009
|
# inherit processors as is unless they have an environment scope
|
|
1010
1010
|
|
|
@@ -1012,8 +1012,7 @@ class Environment:
|
|
|
1012
1012
|
if self.providers[type(processor)].get_scope() != "environment":
|
|
1013
1013
|
self.lifecycle_processors.append(processor)
|
|
1014
1014
|
else:
|
|
1015
|
-
#
|
|
1016
|
-
self.lifecycle_processors.append(self.get(type(processor)))
|
|
1015
|
+
self.get(type(processor)) # will automatically be appended
|
|
1017
1016
|
else:
|
|
1018
1017
|
self.providers[SingletonScope] = SingletonScopeInstanceProvider()
|
|
1019
1018
|
self.providers[RequestScope] = RequestScopeInstanceProvider()
|
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
import threading
|
|
7
7
|
from weakref import WeakKeyDictionary
|
|
8
8
|
|
|
9
|
+
from aspyx.di import injectable
|
|
9
10
|
from aspyx.reflection import Decorators
|
|
10
11
|
from aspyx.di.aop import advice, around, methods, Invocation
|
|
11
12
|
|
|
@@ -20,6 +21,7 @@ def synchronized():
|
|
|
20
21
|
return decorator
|
|
21
22
|
|
|
22
23
|
@advice
|
|
24
|
+
@injectable()
|
|
23
25
|
class SynchronizeAdvice:
|
|
24
26
|
# constructor
|
|
25
27
|
|
|
@@ -38,7 +40,7 @@ class SynchronizeAdvice:
|
|
|
38
40
|
|
|
39
41
|
# around
|
|
40
42
|
|
|
41
|
-
@around(methods().decorated_with(synchronized))
|
|
43
|
+
@around(methods().decorated_with(synchronized).that_are_sync())
|
|
42
44
|
def synchronize_sync(self, invocation: Invocation):
|
|
43
45
|
with self.get_lock(invocation.args[0]):
|
|
44
46
|
return invocation.proceed()
|
|
@@ -107,6 +107,24 @@ class ExceptionManager:
|
|
|
107
107
|
|
|
108
108
|
# internal
|
|
109
109
|
|
|
110
|
+
def collect_handlers(self, instance: Any):
|
|
111
|
+
type_descriptor = TypeDescriptor.for_type(type(instance))
|
|
112
|
+
|
|
113
|
+
# analyze methods
|
|
114
|
+
|
|
115
|
+
for method in type_descriptor.get_methods():
|
|
116
|
+
if method.has_decorator(handle):
|
|
117
|
+
if len(method.param_types) == 1:
|
|
118
|
+
exception_type = method.param_types[0]
|
|
119
|
+
|
|
120
|
+
self.handler.append(Handler(
|
|
121
|
+
exception_type,
|
|
122
|
+
instance,
|
|
123
|
+
method.method,
|
|
124
|
+
))
|
|
125
|
+
else:
|
|
126
|
+
print(f"handler {method.method} expected to have one parameter")
|
|
127
|
+
|
|
110
128
|
@inject_environment()
|
|
111
129
|
def set_environment(self, environment: Environment):
|
|
112
130
|
self.environment = environment
|
|
@@ -114,23 +132,7 @@ class ExceptionManager:
|
|
|
114
132
|
@on_running()
|
|
115
133
|
def setup(self):
|
|
116
134
|
for handler_class in self.exception_handler_classes:
|
|
117
|
-
|
|
118
|
-
instance = self.environment.get(handler_class)
|
|
119
|
-
|
|
120
|
-
# analyze methods
|
|
121
|
-
|
|
122
|
-
for method in type_descriptor.get_methods():
|
|
123
|
-
if method.has_decorator(handle):
|
|
124
|
-
if len(method.param_types) == 1:
|
|
125
|
-
exception_type = method.param_types[0]
|
|
126
|
-
|
|
127
|
-
self.handler.append(Handler(
|
|
128
|
-
exception_type,
|
|
129
|
-
instance,
|
|
130
|
-
method.method,
|
|
131
|
-
))
|
|
132
|
-
else:
|
|
133
|
-
print(f"handler {method.method} expected to have one parameter")
|
|
135
|
+
self.collect_handlers(self.environment.get(handler_class))
|
|
134
136
|
|
|
135
137
|
def get_handlers(self, clazz: Type) -> Optional[Chain]:
|
|
136
138
|
chain = self.cache.get(clazz, None)
|
aspyx/reflection/proxy.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Dynamic proxies for method interception and delegation.
|
|
3
3
|
"""
|
|
4
|
+
import functools
|
|
4
5
|
import inspect
|
|
6
|
+
from abc import ABC, abstractmethod
|
|
5
7
|
from typing import Generic, TypeVar, Type, Callable
|
|
6
8
|
|
|
7
9
|
T = TypeVar("T")
|
|
@@ -44,10 +46,12 @@ class DynamicProxy(Generic[T]):
|
|
|
44
46
|
self.args = args
|
|
45
47
|
self.kwargs = kwargs
|
|
46
48
|
|
|
47
|
-
class InvocationHandler:
|
|
49
|
+
class InvocationHandler(ABC):
|
|
50
|
+
@abstractmethod
|
|
48
51
|
def invoke(self, invocation: 'DynamicProxy.Invocation'):
|
|
49
52
|
pass
|
|
50
53
|
|
|
54
|
+
@abstractmethod
|
|
51
55
|
async def invoke_async(self, invocation: 'DynamicProxy.Invocation'):
|
|
52
56
|
return self.invoke(invocation)
|
|
53
57
|
|
|
@@ -73,13 +77,16 @@ class DynamicProxy(Generic[T]):
|
|
|
73
77
|
def __getattr__(self, name):
|
|
74
78
|
method = getattr(self.type, name)
|
|
75
79
|
|
|
76
|
-
if
|
|
80
|
+
if inspect.iscoroutinefunction(method):
|
|
81
|
+
|
|
82
|
+
@functools.wraps(method)
|
|
77
83
|
async def async_wrapper(*args, **kwargs):
|
|
78
|
-
return
|
|
84
|
+
return await self.invocation_handler.invoke_async(DynamicProxy.Invocation(self.type, method, *args, **kwargs))
|
|
79
85
|
|
|
80
86
|
return async_wrapper
|
|
81
87
|
|
|
82
88
|
else:
|
|
89
|
+
@functools.wraps(method)
|
|
83
90
|
def sync_wrapper(*args, **kwargs):
|
|
84
91
|
return self.invocation_handler.invoke(DynamicProxy.Invocation(self.type, method, *args, **kwargs))
|
|
85
92
|
|
aspyx/reflection/reflection.py
CHANGED
|
@@ -42,11 +42,15 @@ class Decorators:
|
|
|
42
42
|
decorator: the decorator
|
|
43
43
|
*args: any arguments supplied to the decorator
|
|
44
44
|
"""
|
|
45
|
-
|
|
46
|
-
if
|
|
45
|
+
current = func_or_class.__dict__.get('__decorators__')
|
|
46
|
+
if current is None:
|
|
47
47
|
setattr(func_or_class, '__decorators__', [DecoratorDescriptor(decorator, *args)])
|
|
48
48
|
else:
|
|
49
|
-
|
|
49
|
+
# Avoid mutating inherited list
|
|
50
|
+
if '__decorators__' not in func_or_class.__dict__:
|
|
51
|
+
current = list(current)
|
|
52
|
+
setattr(func_or_class, '__decorators__', current)
|
|
53
|
+
current.append(DecoratorDescriptor(decorator, *args))
|
|
50
54
|
|
|
51
55
|
@classmethod
|
|
52
56
|
def has_decorator(cls, func_or_class, callable: Callable) -> bool:
|
|
@@ -82,7 +86,7 @@ class Decorators:
|
|
|
82
86
|
if inspect.ismethod(func_or_class):
|
|
83
87
|
func_or_class = func_or_class.__func__ # unwrap bound method
|
|
84
88
|
|
|
85
|
-
#return getattr(func_or_class, '__decorators__', []) will return inherited as well
|
|
89
|
+
#return getattr(func_or_class, '__decorators__', []) #will return inherited as well
|
|
86
90
|
return func_or_class.__dict__.get('__decorators__', [])
|
|
87
91
|
|
|
88
92
|
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
aspyx/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
2
2
|
aspyx/di/__init__.py,sha256=AGVU2VBWQyBxSssvbk_GOKrYWIYtcmSoIlupz-Oqxi4,1138
|
|
3
|
-
aspyx/di/di.py,sha256=
|
|
3
|
+
aspyx/di/di.py,sha256=V_BAV6DmFCoepPqAXhBz2GW6NwYaKokHb03HMz6A5Sw,44639
|
|
4
4
|
aspyx/di/aop/__init__.py,sha256=rn6LSpzFtUOlgaBATyhLRWBzFmZ6XoVKA9B8SgQzYEI,746
|
|
5
|
-
aspyx/di/aop/aop.py,sha256=
|
|
5
|
+
aspyx/di/aop/aop.py,sha256=UqBpBI2nW6_8CLmY1j0F4cE2QVBQDgry9LWShVuxZKM,19367
|
|
6
6
|
aspyx/di/configuration/__init__.py,sha256=flM9A79J2wfA5I8goQbxs4tTqYustR9tn_9s0YO2WJQ,484
|
|
7
7
|
aspyx/di/configuration/configuration.py,sha256=cXW40bPXiUZ9hUtBoZkSATT3nLrDPWsSqxtgASIBQaM,4375
|
|
8
8
|
aspyx/di/configuration/env_configuration_source.py,sha256=FXPvREzq2ZER6_GG5xdpx154TQQDxZVf7LW7cvaylAk,1446
|
|
9
9
|
aspyx/di/configuration/yaml_configuration_source.py,sha256=NDl3SeoLMNVlzHgfP-Ysvhco1tRew_zFnBL5gGy2WRk,550
|
|
10
10
|
aspyx/di/threading/__init__.py,sha256=qrWdaq7MewQ2UmZy4J0Dn6BhY-ahfiG3xsv-EHqoqSE,191
|
|
11
|
-
aspyx/di/threading/synchronized.py,sha256=
|
|
11
|
+
aspyx/di/threading/synchronized.py,sha256=6JOg5BXWrRIS5nRPH9iWR7T-kUglO4qWBQpLwhy99pI,1325
|
|
12
12
|
aspyx/exception/__init__.py,sha256=OZwv-C3ZHD0Eg1rohCQMj575WLJ7lfYuk6PZD6sh1MA,211
|
|
13
|
-
aspyx/exception/exception_manager.py,sha256=
|
|
13
|
+
aspyx/exception/exception_manager.py,sha256=_pK4yTS5FJYj2SW1ijdBVD_6qrPUM9elMgZki2pf-Tw,5237
|
|
14
14
|
aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
|
|
15
|
-
aspyx/reflection/proxy.py,sha256=
|
|
16
|
-
aspyx/reflection/reflection.py,sha256=
|
|
15
|
+
aspyx/reflection/proxy.py,sha256=1-pgw-TNORFXbV0gowFZqGd-bcWv1ny69bJhq8TLsKs,2761
|
|
16
|
+
aspyx/reflection/reflection.py,sha256=BcqXJcMO36Gzq71O4aBsMqPynmBhYLI8V6J7DOInPAM,9142
|
|
17
17
|
aspyx/threading/__init__.py,sha256=3clmbCDP37GPan3dWtxTQvpg0Ti4aFzruAbUClkHGi0,147
|
|
18
18
|
aspyx/threading/thread_local.py,sha256=86dNtbA4k2B-rNUUnZgn3_pU0DAojgLrRnh8RL6zf1E,1196
|
|
19
19
|
aspyx/util/__init__.py,sha256=8H2yKkXu3nkRGeTerb8ialzKGfvzUx44XUWFUYcYuQM,125
|
|
20
20
|
aspyx/util/stringbuilder.py,sha256=a-0T4YEXSJFUuQ3ztKN1ZPARkh8dIGMSkNEEJHRN7dc,856
|
|
21
|
-
aspyx-1.5.
|
|
22
|
-
aspyx-1.5.
|
|
23
|
-
aspyx-1.5.
|
|
24
|
-
aspyx-1.5.
|
|
21
|
+
aspyx-1.5.2.dist-info/METADATA,sha256=mRlR5o-1WlYVvThk483KJTv738uNq5y02VkquUUsYDk,26490
|
|
22
|
+
aspyx-1.5.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
+
aspyx-1.5.2.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
24
|
+
aspyx-1.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|