aspyx 1.2.0__py3-none-any.whl → 1.3.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/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 teh decorators are executed
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
@@ -50,7 +50,7 @@ class AspectTarget(ABC):
50
50
  __slots__ = [
51
51
  "_function",
52
52
  "_type",
53
-
53
+ "_async",
54
54
  "_clazz",
55
55
  "_instance",
56
56
  "names",
@@ -65,6 +65,7 @@ class AspectTarget(ABC):
65
65
  def __init__(self):
66
66
  self._clazz = None
67
67
  self._instance = None
68
+ self._async = False
68
69
  self._function = None
69
70
  self._type = None
70
71
 
@@ -108,6 +109,10 @@ class AspectTarget(ABC):
108
109
 
109
110
  return self
110
111
 
112
+ def that_are_async(self):
113
+ self._async = True
114
+ return self
115
+
111
116
  def of_type(self, type: Type):
112
117
  self.types.append(type)
113
118
  return self
@@ -178,6 +183,14 @@ class MethodAspectTarget(AspectTarget):
178
183
 
179
184
  method_descriptor = descriptor.get_method(func.__name__)
180
185
 
186
+ # async
187
+
188
+ name = method_descriptor.method.__name__
189
+ is_async = method_descriptor.is_async()
190
+
191
+ if self._async is not method_descriptor.is_async():
192
+ return False
193
+
181
194
  # type
182
195
 
183
196
  if len(self.types) > 0:
@@ -234,6 +247,9 @@ class JoinPoint:
234
247
  def call(self, invocation: 'Invocation'):
235
248
  pass
236
249
 
250
+ async def call_async(self, invocation: 'Invocation'):
251
+ pass
252
+
237
253
  class FunctionJoinPoint(JoinPoint):
238
254
  __slots__ = [
239
255
  "instance",
@@ -251,6 +267,11 @@ class FunctionJoinPoint(JoinPoint):
251
267
 
252
268
  return self.func(self.instance, invocation)
253
269
 
270
+ async def call_async(self, invocation: 'Invocation'):
271
+ invocation.current_join_point = self
272
+
273
+ return await self.func(self.instance, invocation)
274
+
254
275
  class MethodJoinPoint(FunctionJoinPoint):
255
276
  __slots__ = []
256
277
 
@@ -262,6 +283,11 @@ class MethodJoinPoint(FunctionJoinPoint):
262
283
 
263
284
  return self.func(*invocation.args, **invocation.kwargs)
264
285
 
286
+ async def call_async(self, invocation: 'Invocation'):
287
+ invocation.current_join_point = self
288
+
289
+ return await self.func(*invocation.args, **invocation.kwargs)
290
+
265
291
  @dataclass
266
292
  class JoinPoints:
267
293
  before: list[JoinPoint]
@@ -328,6 +354,37 @@ class Invocation:
328
354
 
329
355
  return self.result
330
356
 
357
+ async def call_async(self, *args, **kwargs):
358
+ # remember args
359
+
360
+ self.args = args
361
+ self.kwargs = kwargs
362
+
363
+ # run all before
364
+
365
+ for join_point in self.join_points.before:
366
+ join_point.call(self)
367
+
368
+ # run around's with the method being the last aspect!
369
+
370
+ try:
371
+ self.result = await self.join_points.around[0].call_async(self) # will follow the proceed chain
372
+
373
+ except Exception as e:
374
+ self.exception = e
375
+ for join_point in self.join_points.error:
376
+ join_point.call(self)
377
+
378
+ # run all before
379
+
380
+ for join_point in self.join_points.after:
381
+ join_point.call(self)
382
+
383
+ if self.exception is not None:
384
+ raise self.exception # rethrow the error
385
+
386
+ return self.result
387
+
331
388
  def proceed(self, *args, **kwargs):
332
389
  """
333
390
  Proceed to the next join point in the around chain up to the original method.
@@ -340,6 +397,18 @@ class Invocation:
340
397
 
341
398
  return self.current_join_point.next.call(self)
342
399
 
400
+ async def proceed_async(self, *args, **kwargs):
401
+ """
402
+ Proceed to the next join point in the around chain up to the original method.
403
+ """
404
+ if len(args) > 0 or len(kwargs) > 0: # as soon as we have args, we replace the current ones
405
+ self.args = args
406
+ self.kwargs = kwargs
407
+
408
+ # next one please...
409
+
410
+ return await self.current_join_point.next.call_async(self)
411
+
343
412
  @injectable()
344
413
  class Advice:
345
414
  # static data
@@ -381,13 +450,14 @@ class Advice:
381
450
 
382
451
  if result is None:
383
452
  result = {}
453
+ self.cache[clazz] = result
384
454
 
385
455
  for _, member in inspect.getmembers(clazz, predicate=inspect.isfunction):
386
456
  join_points = self.compute_join_points(clazz, member, environment)
387
457
  if join_points is not None:
388
458
  result[member] = join_points
389
459
 
390
- self.cache[clazz] = result
460
+
391
461
 
392
462
  # add around methods
393
463
 
@@ -533,10 +603,22 @@ class AdviceProcessor(PostProcessor):
533
603
  def process(self, instance: object, environment: Environment):
534
604
  join_point_dict = self.advice.join_points4(instance, environment)
535
605
 
536
- for member, joinPoints in join_point_dict.items():
606
+ for member, join_points in join_point_dict.items():
537
607
  Environment.logger.debug("add aspects for %s:%s", type(instance), member.__name__)
538
608
 
539
609
  def wrap(jp):
540
- return lambda *args, **kwargs: Invocation(member, jp).call(*args, **kwargs)
610
+ def sync_wrapper(*args, **kwargs):
611
+ return Invocation(member, jp).call(*args, **kwargs)
612
+
613
+ return sync_wrapper
614
+
615
+ def wrap_async(jp):
616
+ async def async_wrapper(*args, **kwargs):
617
+ return await Invocation(member, jp).call_async(*args, **kwargs)
618
+
619
+ return async_wrapper
541
620
 
542
- setattr(instance, member.__name__, types.MethodType(wrap(joinPoints), instance))
621
+ if inspect.iscoroutinefunction(member):
622
+ setattr(instance, member.__name__, types.MethodType(wrap_async(join_points), instance))
623
+ else:
624
+ setattr(instance, member.__name__, types.MethodType(wrap(join_points), instance))
@@ -52,4 +52,4 @@ class EnvConfigurationSource(ConfigurationSource):
52
52
  else:
53
53
  exploded[key] = value
54
54
 
55
- return exploded
55
+ return exploded
@@ -23,4 +23,4 @@ class YamlConfigurationSource(ConfigurationSource):
23
23
 
24
24
  def load(self) -> dict:
25
25
  with open(self.file, "r") as file:
26
- return yaml.safe_load(file)
26
+ return yaml.safe_load(file)