aspyx 1.2.0__py3-none-any.whl → 1.4.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 +0 -0
- aspyx/di/__init__.py +8 -4
- aspyx/di/aop/aop.py +181 -74
- aspyx/di/configuration/env_configuration_source.py +1 -1
- aspyx/di/configuration/yaml_configuration_source.py +1 -1
- aspyx/di/di.py +389 -198
- aspyx/di/threading/__init__.py +11 -0
- aspyx/di/threading/synchronized.py +49 -0
- aspyx/exception/__init__.py +10 -0
- aspyx/exception/exception_manager.py +168 -0
- aspyx/reflection/reflection.py +41 -2
- aspyx/threading/__init__.py +10 -0
- aspyx/threading/thread_local.py +47 -0
- aspyx/{di/util → util}/stringbuilder.py +11 -0
- {aspyx-1.2.0.dist-info → aspyx-1.4.0.dist-info}/METADATA +254 -52
- aspyx-1.4.0.dist-info/RECORD +25 -0
- aspyx-1.2.0.dist-info/RECORD +0 -18
- /aspyx/{di/util → util}/__init__.py +0 -0
- {aspyx-1.2.0.dist-info → aspyx-1.4.0.dist-info}/WHEEL +0 -0
- {aspyx-1.2.0.dist-info → aspyx-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {aspyx-1.2.0.dist-info → aspyx-1.4.0.dist-info}/top_level.txt +0 -0
aspyx/__init__.py
ADDED
|
File without changes
|
aspyx/di/__init__.py
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module provides dependency injection and aop capabilities for Python applications.
|
|
3
3
|
"""
|
|
4
|
-
from .di import DIException, AbstractCallableProcessor, LifecycleCallable, Lifecycle, Providers, Environment, ClassInstanceProvider, injectable, factory, environment, inject, order, create, on_init, on_running, on_destroy, inject_environment, Factory, PostProcessor
|
|
4
|
+
from .di import conditional, requires_class, requires_feature, DIException, AbstractCallableProcessor, LifecycleCallable, Lifecycle, Providers, Environment, ClassInstanceProvider, injectable, factory, environment, inject, order, create, on_init, on_running, on_destroy, inject_environment, Factory, PostProcessor
|
|
5
5
|
|
|
6
|
-
# import something from the subpackages, so that
|
|
6
|
+
# import something from the subpackages, so that the decorators are executed
|
|
7
7
|
|
|
8
8
|
from .configuration import ConfigurationManager
|
|
9
9
|
from .aop import before
|
|
10
|
+
from .threading import SynchronizeAdvice
|
|
10
11
|
|
|
11
|
-
imports = [ConfigurationManager, before]
|
|
12
|
+
imports = [ConfigurationManager, before, SynchronizeAdvice]
|
|
12
13
|
|
|
13
14
|
__all__ = [
|
|
14
15
|
"ClassInstanceProvider",
|
|
@@ -30,5 +31,8 @@ __all__ = [
|
|
|
30
31
|
"AbstractCallableProcessor",
|
|
31
32
|
"LifecycleCallable",
|
|
32
33
|
"DIException",
|
|
33
|
-
"Lifecycle"
|
|
34
|
+
"Lifecycle",
|
|
35
|
+
"conditional",
|
|
36
|
+
"requires_class",
|
|
37
|
+
"requires_feature"
|
|
34
38
|
]
|
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
|
"""
|
|
@@ -50,7 +48,7 @@ class AspectTarget(ABC):
|
|
|
50
48
|
__slots__ = [
|
|
51
49
|
"_function",
|
|
52
50
|
"_type",
|
|
53
|
-
|
|
51
|
+
"_async",
|
|
54
52
|
"_clazz",
|
|
55
53
|
"_instance",
|
|
56
54
|
"names",
|
|
@@ -65,6 +63,7 @@ class AspectTarget(ABC):
|
|
|
65
63
|
def __init__(self):
|
|
66
64
|
self._clazz = None
|
|
67
65
|
self._instance = None
|
|
66
|
+
self._async = False
|
|
68
67
|
self._function = None
|
|
69
68
|
self._type = None
|
|
70
69
|
|
|
@@ -108,11 +107,28 @@ class AspectTarget(ABC):
|
|
|
108
107
|
|
|
109
108
|
return self
|
|
110
109
|
|
|
110
|
+
def that_are_async(self):
|
|
111
|
+
"""
|
|
112
|
+
matches methods that are async
|
|
113
|
+
:return: self
|
|
114
|
+
"""
|
|
115
|
+
self._async = True
|
|
116
|
+
return self
|
|
117
|
+
|
|
111
118
|
def of_type(self, type: Type):
|
|
119
|
+
"""
|
|
120
|
+
matches methods belonging to a class or classes that are subclasses of the specified type
|
|
121
|
+
:return: self
|
|
122
|
+
"""
|
|
112
123
|
self.types.append(type)
|
|
113
124
|
return self
|
|
114
125
|
|
|
115
126
|
def decorated_with(self, decorator):
|
|
127
|
+
"""
|
|
128
|
+
matches methods or classes that are decorated with the specified decorator
|
|
129
|
+
:param decorator: the decorator callable
|
|
130
|
+
:return:
|
|
131
|
+
"""
|
|
116
132
|
self.decorators.append(decorator)
|
|
117
133
|
return self
|
|
118
134
|
|
|
@@ -128,6 +144,9 @@ class AspectTarget(ABC):
|
|
|
128
144
|
return self
|
|
129
145
|
|
|
130
146
|
class ClassAspectTarget(AspectTarget):
|
|
147
|
+
"""
|
|
148
|
+
An AspectTarget matching classes
|
|
149
|
+
"""
|
|
131
150
|
# properties
|
|
132
151
|
|
|
133
152
|
__slots__ = [
|
|
@@ -167,6 +186,10 @@ class ClassAspectTarget(AspectTarget):
|
|
|
167
186
|
# fluent
|
|
168
187
|
|
|
169
188
|
class MethodAspectTarget(AspectTarget):
|
|
189
|
+
"""
|
|
190
|
+
An AspectTarget matching methods
|
|
191
|
+
"""
|
|
192
|
+
|
|
170
193
|
# properties
|
|
171
194
|
|
|
172
195
|
__slots__ = [ ]
|
|
@@ -178,6 +201,11 @@ class MethodAspectTarget(AspectTarget):
|
|
|
178
201
|
|
|
179
202
|
method_descriptor = descriptor.get_method(func.__name__)
|
|
180
203
|
|
|
204
|
+
# async
|
|
205
|
+
|
|
206
|
+
if self._async is not method_descriptor.is_async():
|
|
207
|
+
return False
|
|
208
|
+
|
|
181
209
|
# type
|
|
182
210
|
|
|
183
211
|
if len(self.types) > 0:
|
|
@@ -219,14 +247,14 @@ def classes():
|
|
|
219
247
|
return ClassAspectTarget()
|
|
220
248
|
|
|
221
249
|
|
|
222
|
-
class
|
|
250
|
+
class Aspect:
|
|
223
251
|
__slots__ = [
|
|
224
252
|
"next",
|
|
225
253
|
]
|
|
226
254
|
|
|
227
255
|
# constructor
|
|
228
256
|
|
|
229
|
-
def __init__(self, next: '
|
|
257
|
+
def __init__(self, next: 'Aspect'):
|
|
230
258
|
self.next = next
|
|
231
259
|
|
|
232
260
|
# public
|
|
@@ -234,45 +262,61 @@ class JoinPoint:
|
|
|
234
262
|
def call(self, invocation: 'Invocation'):
|
|
235
263
|
pass
|
|
236
264
|
|
|
237
|
-
|
|
265
|
+
async def call_async(self, invocation: 'Invocation'):
|
|
266
|
+
pass
|
|
267
|
+
|
|
268
|
+
class FunctionAspect(Aspect):
|
|
238
269
|
__slots__ = [
|
|
239
270
|
"instance",
|
|
240
271
|
"func",
|
|
272
|
+
"order",
|
|
241
273
|
]
|
|
242
274
|
|
|
243
|
-
def __init__(self, instance, func,
|
|
244
|
-
super().__init__(
|
|
275
|
+
def __init__(self, instance, func, next_aspect: Optional['Aspect']):
|
|
276
|
+
super().__init__(next_aspect)
|
|
245
277
|
|
|
246
278
|
self.instance = instance
|
|
247
279
|
self.func = func
|
|
248
280
|
|
|
281
|
+
self.order = next((decorator.args[0] for decorator in Decorators.get(func) if decorator.decorator is order), 0)
|
|
282
|
+
|
|
249
283
|
def call(self, invocation: 'Invocation'):
|
|
250
|
-
invocation.
|
|
284
|
+
invocation.current_aspect = self
|
|
251
285
|
|
|
252
286
|
return self.func(self.instance, invocation)
|
|
253
287
|
|
|
254
|
-
|
|
288
|
+
async def call_async(self, invocation: 'Invocation'):
|
|
289
|
+
invocation.current_aspect = self
|
|
290
|
+
|
|
291
|
+
return await self.func(self.instance, invocation)
|
|
292
|
+
|
|
293
|
+
class MethodAspect(FunctionAspect):
|
|
255
294
|
__slots__ = []
|
|
256
295
|
|
|
257
296
|
def __init__(self, instance, func):
|
|
258
297
|
super().__init__(instance, func, None)
|
|
259
298
|
|
|
260
299
|
def call(self, invocation: 'Invocation'):
|
|
261
|
-
invocation.
|
|
300
|
+
invocation.current_aspect = self
|
|
262
301
|
|
|
263
302
|
return self.func(*invocation.args, **invocation.kwargs)
|
|
264
303
|
|
|
304
|
+
async def call_async(self, invocation: 'Invocation'):
|
|
305
|
+
invocation.current_aspect = self
|
|
306
|
+
|
|
307
|
+
return await self.func(*invocation.args, **invocation.kwargs)
|
|
308
|
+
|
|
265
309
|
@dataclass
|
|
266
|
-
class
|
|
267
|
-
before: list[
|
|
268
|
-
around: list[
|
|
269
|
-
error: list[
|
|
270
|
-
after: list[
|
|
310
|
+
class Aspects:
|
|
311
|
+
before: list[Aspect]
|
|
312
|
+
around: list[Aspect]
|
|
313
|
+
error: list[Aspect]
|
|
314
|
+
after: list[Aspect]
|
|
271
315
|
|
|
272
316
|
class Invocation:
|
|
273
317
|
"""
|
|
274
318
|
Invocation stores the relevant data of a single method invocation.
|
|
275
|
-
It holds the arguments, keyword arguments, result, error, and the
|
|
319
|
+
It holds the arguments, keyword arguments, result, error, and the aspects that define the aspect behavior.
|
|
276
320
|
"""
|
|
277
321
|
# properties
|
|
278
322
|
|
|
@@ -282,20 +326,20 @@ class Invocation:
|
|
|
282
326
|
"kwargs",
|
|
283
327
|
"result",
|
|
284
328
|
"exception",
|
|
285
|
-
"
|
|
286
|
-
"
|
|
329
|
+
"aspects",
|
|
330
|
+
"current_aspect",
|
|
287
331
|
]
|
|
288
332
|
|
|
289
333
|
# constructor
|
|
290
334
|
|
|
291
|
-
def __init__(self, func,
|
|
335
|
+
def __init__(self, func, aspects: Aspects):
|
|
292
336
|
self.func = func
|
|
293
337
|
self.args : list[object] = []
|
|
294
338
|
self.kwargs = None
|
|
295
339
|
self.result = None
|
|
296
340
|
self.exception = None
|
|
297
|
-
self.
|
|
298
|
-
self.
|
|
341
|
+
self.aspects = aspects
|
|
342
|
+
self.current_aspect = None
|
|
299
343
|
|
|
300
344
|
def call(self, *args, **kwargs):
|
|
301
345
|
# remember args
|
|
@@ -305,23 +349,54 @@ class Invocation:
|
|
|
305
349
|
|
|
306
350
|
# run all before
|
|
307
351
|
|
|
308
|
-
for
|
|
309
|
-
|
|
352
|
+
for aspect in self.aspects.before:
|
|
353
|
+
aspect.call(self)
|
|
310
354
|
|
|
311
355
|
# run around's with the method being the last aspect!
|
|
312
356
|
|
|
313
357
|
try:
|
|
314
|
-
self.result = self.
|
|
358
|
+
self.result = self.aspects.around[0].call(self) # will follow the proceed chain
|
|
315
359
|
|
|
316
360
|
except Exception as e:
|
|
317
361
|
self.exception = e
|
|
318
|
-
for
|
|
319
|
-
|
|
362
|
+
for aspect in self.aspects.error:
|
|
363
|
+
aspect.call(self)
|
|
320
364
|
|
|
321
365
|
# run all before
|
|
322
366
|
|
|
323
|
-
for
|
|
324
|
-
|
|
367
|
+
for aspect in self.aspects.after:
|
|
368
|
+
aspect.call(self)
|
|
369
|
+
|
|
370
|
+
if self.exception is not None:
|
|
371
|
+
raise self.exception # rethrow the error
|
|
372
|
+
|
|
373
|
+
return self.result
|
|
374
|
+
|
|
375
|
+
async def call_async(self, *args, **kwargs):
|
|
376
|
+
# remember args
|
|
377
|
+
|
|
378
|
+
self.args = args
|
|
379
|
+
self.kwargs = kwargs
|
|
380
|
+
|
|
381
|
+
# run all before
|
|
382
|
+
|
|
383
|
+
for aspect in self.aspects.before:
|
|
384
|
+
aspect.call(self)
|
|
385
|
+
|
|
386
|
+
# run around's with the method being the last aspect!
|
|
387
|
+
|
|
388
|
+
try:
|
|
389
|
+
self.result = await self.aspects.around[0].call_async(self) # will follow the proceed chain
|
|
390
|
+
|
|
391
|
+
except Exception as e:
|
|
392
|
+
self.exception = e
|
|
393
|
+
for aspect in self.aspects.error:
|
|
394
|
+
aspect.call(self)
|
|
395
|
+
|
|
396
|
+
# run all before
|
|
397
|
+
|
|
398
|
+
for aspect in self.aspects.after:
|
|
399
|
+
aspect.call(self)
|
|
325
400
|
|
|
326
401
|
if self.exception is not None:
|
|
327
402
|
raise self.exception # rethrow the error
|
|
@@ -338,29 +413,41 @@ class Invocation:
|
|
|
338
413
|
|
|
339
414
|
# next one please...
|
|
340
415
|
|
|
341
|
-
return self.
|
|
416
|
+
return self.current_aspect.next.call(self)
|
|
417
|
+
|
|
418
|
+
async def proceed_async(self, *args, **kwargs):
|
|
419
|
+
"""
|
|
420
|
+
Proceed to the next join point in the around chain up to the original method.
|
|
421
|
+
"""
|
|
422
|
+
if len(args) > 0 or len(kwargs) > 0: # as soon as we have args, we replace the current ones
|
|
423
|
+
self.args = args
|
|
424
|
+
self.kwargs = kwargs
|
|
425
|
+
|
|
426
|
+
# next one please...
|
|
427
|
+
|
|
428
|
+
return await self.current_aspect.next.call_async(self)
|
|
342
429
|
|
|
343
|
-
|
|
344
|
-
class Advice:
|
|
430
|
+
class Advices:
|
|
345
431
|
# static data
|
|
346
432
|
|
|
347
433
|
targets: list[AspectTarget] = []
|
|
348
434
|
|
|
349
|
-
__slots__ = [
|
|
350
|
-
"cache",
|
|
351
|
-
"lock"
|
|
352
|
-
]
|
|
435
|
+
__slots__ = []
|
|
353
436
|
|
|
354
437
|
# constructor
|
|
355
438
|
|
|
356
439
|
def __init__(self):
|
|
357
|
-
|
|
358
|
-
self.lock = threading.RLock()
|
|
440
|
+
pass
|
|
359
441
|
|
|
360
442
|
# methods
|
|
361
443
|
|
|
362
|
-
|
|
363
|
-
|
|
444
|
+
@classmethod
|
|
445
|
+
def collect(cls, clazz, member, type: AspectType, environment: Environment):
|
|
446
|
+
aspects = [FunctionAspect(environment.get(target._clazz), target._function, None) for target in Advices.targets if target._type == type and target._clazz is not clazz and environment.providers.get(target._clazz) is not None and target._matches(clazz, member)]
|
|
447
|
+
|
|
448
|
+
# sort according to order
|
|
449
|
+
|
|
450
|
+
aspects = sorted(aspects, key=lambda aspect: aspect.order)
|
|
364
451
|
|
|
365
452
|
# link
|
|
366
453
|
|
|
@@ -371,30 +458,23 @@ class Advice:
|
|
|
371
458
|
|
|
372
459
|
return aspects
|
|
373
460
|
|
|
374
|
-
|
|
461
|
+
@classmethod
|
|
462
|
+
def aspects_for(cls, instance, environment: Environment) -> Dict[Callable,Aspects]:
|
|
375
463
|
clazz = type(instance)
|
|
376
464
|
|
|
377
|
-
result =
|
|
378
|
-
if result is None:
|
|
379
|
-
with self.lock:
|
|
380
|
-
result = self.cache.get(clazz, None)
|
|
381
|
-
|
|
382
|
-
if result is None:
|
|
383
|
-
result = {}
|
|
384
|
-
|
|
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
|
|
465
|
+
result = {}
|
|
389
466
|
|
|
390
|
-
|
|
467
|
+
for _, member in inspect.getmembers(clazz, predicate=inspect.isfunction):
|
|
468
|
+
aspects = cls.compute_aspects(clazz, member, environment)
|
|
469
|
+
if aspects is not None:
|
|
470
|
+
result[member] = aspects
|
|
391
471
|
|
|
392
472
|
# add around methods
|
|
393
473
|
|
|
394
474
|
value = {}
|
|
395
475
|
|
|
396
476
|
for key, cjp in result.items():
|
|
397
|
-
jp =
|
|
477
|
+
jp = Aspects(
|
|
398
478
|
before=cjp.before,
|
|
399
479
|
around=cjp.around,
|
|
400
480
|
error=cjp.error,
|
|
@@ -402,7 +482,7 @@ class Advice:
|
|
|
402
482
|
|
|
403
483
|
# add method to around
|
|
404
484
|
|
|
405
|
-
jp.around.append(
|
|
485
|
+
jp.around.append(MethodAspect(instance, key))
|
|
406
486
|
if len(jp.around) > 1:
|
|
407
487
|
jp.around[len(jp.around) - 2].next = jp.around[len(jp.around) - 1]
|
|
408
488
|
|
|
@@ -412,14 +492,15 @@ class Advice:
|
|
|
412
492
|
|
|
413
493
|
return value
|
|
414
494
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
495
|
+
@classmethod
|
|
496
|
+
def compute_aspects(cls, clazz, member, environment: Environment) -> Optional[Aspects]:
|
|
497
|
+
befores = cls.collect(clazz, member, AspectType.BEFORE, environment)
|
|
498
|
+
arounds = cls.collect(clazz, member, AspectType.AROUND, environment)
|
|
499
|
+
afters = cls.collect(clazz, member, AspectType.AFTER, environment)
|
|
500
|
+
errors = cls.collect(clazz, member, AspectType.ERROR, environment)
|
|
420
501
|
|
|
421
502
|
if len(befores) > 0 or len(arounds) > 0 or len(afters) > 0 or len(errors) > 0:
|
|
422
|
-
return
|
|
503
|
+
return Aspects(
|
|
423
504
|
before=befores,
|
|
424
505
|
around=arounds,
|
|
425
506
|
error=errors,
|
|
@@ -437,7 +518,7 @@ def sanity_check(clazz: Type, name: str):
|
|
|
437
518
|
|
|
438
519
|
def advice(cls):
|
|
439
520
|
"""
|
|
440
|
-
Classes decorated with @advice are treated as
|
|
521
|
+
Classes decorated with @advice are treated as advice classes.
|
|
441
522
|
They can contain methods decorated with @before, @after, @around, or @error to define aspects.
|
|
442
523
|
"""
|
|
443
524
|
Providers.register(ClassInstanceProvider(cls, True))
|
|
@@ -447,10 +528,10 @@ def advice(cls):
|
|
|
447
528
|
for name, member in TypeDescriptor.for_type(cls).methods.items():
|
|
448
529
|
decorator = next((decorator for decorator in member.decorators if decorator.decorator in [before, after, around, error]), None)
|
|
449
530
|
if decorator is not None:
|
|
450
|
-
target = decorator.args[0]
|
|
531
|
+
target = decorator.args[0] # ? ...?? TODO, can be multiple
|
|
451
532
|
target._clazz = cls
|
|
452
533
|
sanity_check(cls, name)
|
|
453
|
-
|
|
534
|
+
Advices.targets.append(target) #??
|
|
454
535
|
|
|
455
536
|
return cls
|
|
456
537
|
|
|
@@ -512,31 +593,57 @@ def around(*targets: AspectTarget):
|
|
|
512
593
|
|
|
513
594
|
return decorator
|
|
514
595
|
|
|
515
|
-
@injectable()
|
|
596
|
+
@injectable(scope="environment")
|
|
516
597
|
@order(0)
|
|
517
598
|
class AdviceProcessor(PostProcessor):
|
|
518
599
|
# properties
|
|
519
600
|
|
|
520
601
|
__slots__ = [
|
|
521
|
-
"
|
|
602
|
+
"lock",
|
|
603
|
+
"cache"
|
|
522
604
|
]
|
|
523
605
|
|
|
524
606
|
# constructor
|
|
525
607
|
|
|
526
|
-
def __init__(self
|
|
608
|
+
def __init__(self):
|
|
527
609
|
super().__init__()
|
|
528
610
|
|
|
529
|
-
self.
|
|
611
|
+
self.cache : Dict[Type, Dict[Callable,Aspects]] = {}
|
|
612
|
+
self.lock = threading.RLock()
|
|
613
|
+
|
|
614
|
+
# local
|
|
615
|
+
|
|
616
|
+
def aspects_for(self, instance, environment: Environment) -> Dict[Callable,Aspects]:
|
|
617
|
+
clazz = type(instance)
|
|
618
|
+
result = self.cache.get(clazz, None)
|
|
619
|
+
if result is None:
|
|
620
|
+
with self.lock:
|
|
621
|
+
result = Advices.aspects_for(instance, environment)
|
|
622
|
+
self.cache[clazz] = result # TOID der cache ist zu dick?????
|
|
623
|
+
|
|
624
|
+
return result
|
|
530
625
|
|
|
531
626
|
# implement
|
|
532
627
|
|
|
533
628
|
def process(self, instance: object, environment: Environment):
|
|
534
|
-
|
|
629
|
+
aspect_dict = self.aspects_for(instance, environment)
|
|
535
630
|
|
|
536
|
-
for member,
|
|
631
|
+
for member, aspects in aspect_dict.items():
|
|
537
632
|
Environment.logger.debug("add aspects for %s:%s", type(instance), member.__name__)
|
|
538
633
|
|
|
539
634
|
def wrap(jp):
|
|
540
|
-
|
|
635
|
+
def sync_wrapper(*args, **kwargs):
|
|
636
|
+
return Invocation(member, jp).call(*args, **kwargs)
|
|
637
|
+
|
|
638
|
+
return sync_wrapper
|
|
639
|
+
|
|
640
|
+
def wrap_async(jp):
|
|
641
|
+
async def async_wrapper(*args, **kwargs):
|
|
642
|
+
return await Invocation(member, jp).call_async(*args, **kwargs)
|
|
643
|
+
|
|
644
|
+
return async_wrapper
|
|
541
645
|
|
|
542
|
-
|
|
646
|
+
if inspect.iscoroutinefunction(member):
|
|
647
|
+
setattr(instance, member.__name__, types.MethodType(wrap_async(aspects), instance))
|
|
648
|
+
else:
|
|
649
|
+
setattr(instance, member.__name__, types.MethodType(wrap(aspects), instance))
|