aspyx 1.4.1__py3-none-any.whl → 1.5.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/__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, module, inject, order, create, on_init, on_running, on_destroy, inject_environment, Factory, PostProcessor
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
 
@@ -21,7 +21,7 @@ __all__ = [
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/di.py CHANGED
@@ -316,30 +316,44 @@ class EnvironmentInstanceProvider(AbstractInstanceProvider):
316
316
 
317
317
  self.environment = environment
318
318
  self.provider = provider
319
- self.dependencies = []
319
+ self.dependencies : Optional[list[AbstractInstanceProvider]] = None # FOO
320
320
  self.scope_instance = Scopes.get(provider.get_scope(), environment)
321
321
 
322
+ # public
323
+
324
+ def print_tree(self, prefix=""):
325
+ children = self.dependencies
326
+ last_index = len(children) - 1
327
+ print(prefix + "+- " + self.report())
328
+
329
+ for i, child in enumerate(children):
330
+ if i == last_index:
331
+ # Last child
332
+ child_prefix = prefix + " "
333
+ else:
334
+ # Not last child
335
+ child_prefix = prefix + "| "
336
+
337
+
338
+ cast(EnvironmentInstanceProvider, child).print_tree(child_prefix)
339
+
322
340
  # implement
323
341
 
324
342
  def resolve(self, context: Providers.ResolveContext):
325
- context.add(self)
326
-
327
- if not context.is_resolved(self):
328
- context.provider_dependencies[self] = [] #?
343
+ if self.dependencies is None:
344
+ self.dependencies = []
345
+ context.push(self)
346
+ try:
347
+ type_and_params = self.provider.get_dependencies()
348
+ #params = type_and_params[1]
349
+ for type in type_and_params[0]:
350
+ provider = context.require_provider(type)
329
351
 
330
- type_and_params = self.provider.get_dependencies()
331
- params = type_and_params[1]
332
- for type in type_and_params[0]:
333
- if params > 0:
334
- params -= 1
335
- self.dependencies.append(context.get_provider(type))
352
+ self.dependencies.append(provider)
336
353
 
337
- provider = context.add_provider_dependency(self, type)
338
- if provider is not None:
339
354
  provider.resolve(context)
340
-
341
- else:
342
- context.add(*context.get_provider_dependencies(self))
355
+ finally:
356
+ context.pop()
343
357
 
344
358
  def get_module(self) -> str:
345
359
  return self.provider.get_module()
@@ -403,7 +417,11 @@ class ClassInstanceProvider(InstanceProvider):
403
417
  for method in TypeDescriptor.for_type(self.type).get_methods():
404
418
  if method.has_decorator(inject):
405
419
  for param in method.param_types:
420
+ if not Providers.is_registered(param):
421
+ raise DIRegistrationException(f"{self.type.__name__}.{method.method.__name__} declares an unknown parameter type {param.__name__}")
422
+
406
423
  types.append(param)
424
+ # done
407
425
 
408
426
  return types, self.params
409
427
 
@@ -431,28 +449,28 @@ class FunctionInstanceProvider(InstanceProvider):
431
449
 
432
450
  # constructor
433
451
 
434
- def __init__(self, clazz : Type, method, return_type : Type[T], eager = True, scope = "singleton"):
435
- super().__init__(clazz, return_type, eager, scope)
452
+ def __init__(self, clazz : Type, method: TypeDescriptor.MethodDescriptor, eager = True, scope = "singleton"):
453
+ super().__init__(clazz, method.return_type, eager, scope)
436
454
 
437
- self.method = method
455
+ self.method : TypeDescriptor.MethodDescriptor = method
438
456
 
439
457
  # implement
440
458
 
441
459
  def get_dependencies(self) -> (list[Type],int):
442
- return [self.host], 1
460
+ return [self.host, *self.method.param_types], 1 + len(self.method.param_types)
443
461
 
444
462
  def create(self, environment: Environment, *args):
445
463
  Environment.logger.debug("%s create class %s", self, self.type.__qualname__)
446
464
 
447
- instance = self.method(*args) # args[0]=self
465
+ instance = self.method.method(*args) # args[0]=self
448
466
 
449
467
  return environment.created(instance)
450
468
 
451
469
  def report(self) -> str:
452
- return f"{self.host.__name__}.{self.method.__name__}"
470
+ return f"{self.host.__name__}.{self.method.get_name()}({', '.join(t.__name__ for t in self.method.param_types)}) -> {self.type.__qualname__}"
453
471
 
454
472
  def __str__(self):
455
- return f"FunctionInstanceProvider({self.host.__name__}.{self.method.__name__} -> {self.type.__name__})"
473
+ return f"FunctionInstanceProvider({self.host.__name__}.{self.method.get_name()}({', '.join(t.__name__ for t in self.method.param_types)}) -> {self.type.__name__})"
456
474
 
457
475
  class FactoryInstanceProvider(InstanceProvider):
458
476
  """
@@ -483,7 +501,7 @@ class FactoryInstanceProvider(InstanceProvider):
483
501
  return environment.created(args[0].create())
484
502
 
485
503
  def report(self) -> str:
486
- return f"{self.host.__name__}.create"
504
+ return f"{self.host.__name__}.create() -> {self.type.__name__} "
487
505
 
488
506
  def __str__(self):
489
507
  return f"FactoryInstanceProvider({self.host.__name__} -> {self.type.__name__})"
@@ -545,69 +563,45 @@ class PostProcessor(LifecycleProcessor):
545
563
 
546
564
  class Providers:
547
565
  """
548
- The Providers class is a static class that manages the registration and resolution of InstanceProviders.
566
+ The Providers class is a static class used in the context of the registration and resolution of InstanceProviders.
549
567
  """
550
568
  # local class
551
569
 
552
570
  class ResolveContext:
553
571
  __slots__ = [
554
- "dependencies",
555
572
  "providers",
556
- "provider_dependencies"
573
+ "path"
557
574
  ]
558
575
 
559
576
  # constructor
560
577
 
561
578
  def __init__(self, providers: Dict[Type, EnvironmentInstanceProvider]):
562
- self.dependencies : list[EnvironmentInstanceProvider] = []
563
579
  self.providers = providers
564
- self.provider_dependencies : dict[EnvironmentInstanceProvider, list[EnvironmentInstanceProvider]] = {}
580
+ self.path = []
565
581
 
566
582
  # public
567
583
 
568
- def is_resolved(self, provider: EnvironmentInstanceProvider) -> bool:
569
- return self.provider_dependencies.get(provider, None) is not None
570
-
571
- def get_provider_dependencies(self, provider: EnvironmentInstanceProvider) -> list[EnvironmentInstanceProvider]:
572
- return self.provider_dependencies[provider]
573
-
574
- def add_provider_dependency(self, provider: EnvironmentInstanceProvider, type: Type) -> Optional[EnvironmentInstanceProvider]:
575
- provider_dependencies = self.provider_dependencies.get(provider, None)
576
- if provider_dependencies is None:
577
- provider_dependencies = []
578
- self.provider_dependencies[provider] = provider_dependencies
584
+ def push(self, provider):
585
+ self.path.append(provider)
579
586
 
580
- provider = self.get_provider(type)
587
+ def pop(self):
588
+ self.path.pop()
581
589
 
582
- if any(issubclass(provider.get_type(), dependency.get_type()) for dependency in provider_dependencies):
583
- return None
584
-
585
- provider_dependencies.append(provider)
586
-
587
- return provider
588
-
589
- def next(self):
590
- self.dependencies.clear()
591
-
592
- def get_provider(self, type: Type) -> EnvironmentInstanceProvider:
590
+ def require_provider(self, type: Type) -> EnvironmentInstanceProvider:
593
591
  provider = self.providers.get(type, None)
594
592
  if provider is None:
595
593
  raise DIRegistrationException(f"Provider for {type} is not defined")
596
594
 
597
- return provider
595
+ if provider in self.path:
596
+ raise DIRegistrationException(self.cycle_report(provider))
598
597
 
599
- def add(self, *providers: EnvironmentInstanceProvider):
600
- for provider in providers:
601
- if next((p for p in self.dependencies if p.get_type() is provider.get_type()), None) is not None:
602
- raise DIRegistrationException(self.cycle_report(provider))
603
-
604
- self.dependencies.append(provider)
598
+ return provider
605
599
 
606
600
  def cycle_report(self, provider: AbstractInstanceProvider):
607
601
  cycle = ""
608
602
 
609
603
  first = True
610
- for p in self.dependencies:
604
+ for p in self.path:
611
605
  if not first:
612
606
  cycle += " -> "
613
607
 
@@ -638,6 +632,10 @@ class Providers:
638
632
  else:
639
633
  candidates.append(provider)
640
634
 
635
+ @classmethod
636
+ def is_registered(cls,type: Type) -> bool:
637
+ return Providers.providers.get(type, None) is not None
638
+
641
639
  # add factories lazily
642
640
 
643
641
  @classmethod
@@ -652,7 +650,7 @@ class Providers:
652
650
  cache: Dict[Type,AbstractInstanceProvider] = {}
653
651
 
654
652
  context: ConditionContext = {
655
- "requires_feature": lambda feature : environment.has_feature(feature),
653
+ "requires_feature": environment.has_feature,
656
654
  "requires_class": lambda clazz : cache.get(clazz, None) is not None # ? only works if the class is in the cache already?
657
655
  }
658
656
 
@@ -708,10 +706,13 @@ class Providers:
708
706
  if type is provider.get_type():
709
707
  raise ProviderCollisionException(f"type {type.__name__} already registered", existing_provider, provider)
710
708
 
711
- if isinstance(existing_provider, AmbiguousProvider):
712
- cast(AmbiguousProvider, existing_provider).add_provider(provider)
713
- else:
714
- cache[type] = AmbiguousProvider(type, existing_provider, provider)
709
+ if existing_provider.get_type() is not type:
710
+ # only overwrite if the existing provider is not specific
711
+
712
+ if isinstance(existing_provider, AmbiguousProvider):
713
+ cast(AmbiguousProvider, existing_provider).add_provider(provider)
714
+ else:
715
+ cache[type] = AmbiguousProvider(type, existing_provider, provider)
715
716
 
716
717
  # recursion
717
718
 
@@ -747,7 +748,6 @@ class Providers:
747
748
  provider_context = Providers.ResolveContext(providers)
748
749
  for provider in mapped.values():
749
750
  provider.resolve(provider_context)
750
- provider_context.next() # clear dependencies
751
751
 
752
752
  # done
753
753
 
@@ -763,8 +763,7 @@ def register_factories(cls: Type):
763
763
  if return_type is None:
764
764
  raise DIRegistrationException(f"{cls.__name__}.{method.method.__name__} expected to have a return type")
765
765
 
766
- Providers.register(FunctionInstanceProvider(cls, method.method, return_type, create_decorator.args[0],
767
- create_decorator.args[1]))
766
+ Providers.register(FunctionInstanceProvider(cls, method, create_decorator.args[0], create_decorator.args[1]))
768
767
  def order(prio = 0):
769
768
  def decorator(cls):
770
769
  Decorators.add(cls, order, prio)
@@ -939,12 +938,12 @@ class Environment:
939
938
  class Foo:
940
939
  def __init__(self):
941
940
 
942
- @environment()
943
- class SimpleEnvironment:
941
+ @module()
942
+ class Module:
944
943
  def __init__(self):
945
944
  pass
946
945
 
947
- environment = Environment(SimpleEnvironment)
946
+ environment = Environment(Module)
948
947
 
949
948
  foo = environment.get(Foo) # will create an instance of Foo
950
949
  ```
@@ -976,6 +975,11 @@ class Environment:
976
975
  parent (Optional[Environment]): Optional parent environment, whose objects are inherited.
977
976
  """
978
977
 
978
+ def add_provider(type: Type, provider: AbstractInstanceProvider):
979
+ Environment.logger.debug("\tadd provider %s for %s", provider, type)
980
+
981
+ self.providers[type] = provider
982
+
979
983
  Environment.logger.debug("create environment for class %s", env.__qualname__)
980
984
 
981
985
  # initialize
@@ -996,9 +1000,11 @@ class Environment:
996
1000
  for provider_type, inherited_provider in self.parent.providers.items():
997
1001
  if inherited_provider.get_scope() == "environment":
998
1002
  # replace with own environment instance provider
999
- self.providers[provider_type] = EnvironmentInstanceProvider(self, cast(EnvironmentInstanceProvider, inherited_provider).provider)
1003
+ provider = EnvironmentInstanceProvider(self, cast(EnvironmentInstanceProvider, inherited_provider).provider)
1004
+ provider.dependencies = [] # ??
1005
+ add_provider(provider_type, provider)
1000
1006
  else:
1001
- self.providers[provider_type] = inherited_provider
1007
+ add_provider(provider_type, inherited_provider)
1002
1008
 
1003
1009
  # inherit processors as is unless they have an environment scope
1004
1010
 
@@ -1019,16 +1025,35 @@ class Environment:
1019
1025
 
1020
1026
  loaded = set()
1021
1027
 
1022
- def add_provider(type: Type, provider: AbstractInstanceProvider):
1023
- Environment.logger.debug("\tadd provider %s for %s", provider, type)
1024
-
1025
- self.providers[type] = provider
1026
-
1027
1028
  def get_type_package(type: Type):
1028
1029
  module_name = type.__module__
1029
- module = sys.modules[module_name]
1030
+ module = sys.modules.get(module_name)
1031
+
1032
+ if not module:
1033
+ raise ImportError(f"Module {module_name} not found")
1034
+
1035
+ # Try to get the package
1036
+
1037
+ package = getattr(module, '__package__', None)
1038
+
1039
+ # Fallback: if module is __main__, try to infer from the module name if possible
1040
+
1041
+ if not package:
1042
+ if module_name == '__main__':
1043
+ # Try to resolve real name via __file__
1044
+ path = getattr(module, '__file__', None)
1045
+ if path:
1046
+ Environment.logger.warning(
1047
+ "Module is __main__; consider running via -m to preserve package context")
1048
+ return ''
1049
+
1050
+ # Try to infer package name from module name
1051
+
1052
+ parts = module_name.split('.')
1053
+ if len(parts) > 1:
1054
+ return '.'.join(parts[:-1])
1030
1055
 
1031
- return module.__package__
1056
+ return package or ''
1032
1057
 
1033
1058
  def import_package(name: str):
1034
1059
  """Import a package and all its submodules recursively."""
@@ -155,7 +155,7 @@ class ExceptionManager:
155
155
 
156
156
  # chain
157
157
 
158
- for i in range(0, len(chain) - 2):
158
+ for i in range(0, len(chain) - 1):
159
159
  chain[i].next = chain[i + 1]
160
160
 
161
161
  if len(chain) > 0:
aspyx/reflection/proxy.py CHANGED
@@ -1,7 +1,8 @@
1
1
  """
2
2
  Dynamic proxies for method interception and delegation.
3
3
  """
4
- from typing import Generic, TypeVar, Type
4
+ import inspect
5
+ from typing import Generic, TypeVar, Type, Callable
5
6
 
6
7
  T = TypeVar("T")
7
8
 
@@ -28,9 +29,18 @@ class DynamicProxy(Generic[T]):
28
29
  # inner class
29
30
 
30
31
  class Invocation:
31
- def __init__(self, type: Type[T], name: str, *args, **kwargs):
32
+ __slots__ = [
33
+ "type",
34
+ "method",
35
+ "args",
36
+ "kwargs",
37
+ ]
38
+
39
+ # constructor
40
+
41
+ def __init__(self, type: Type[T], method: Callable, *args, **kwargs):
32
42
  self.type = type
33
- self.name = name
43
+ self.method = method
34
44
  self.args = args
35
45
  self.kwargs = kwargs
36
46
 
@@ -38,12 +48,20 @@ class DynamicProxy(Generic[T]):
38
48
  def invoke(self, invocation: 'DynamicProxy.Invocation'):
39
49
  pass
40
50
 
51
+ async def invoke_async(self, invocation: 'DynamicProxy.Invocation'):
52
+ return self.invoke(invocation)
53
+
41
54
  # class methods
42
55
 
43
56
  @classmethod
44
57
  def create(cls, type: Type[T], invocation_handler: 'DynamicProxy.InvocationHandler') -> T:
45
58
  return DynamicProxy(type, invocation_handler)
46
59
 
60
+ __slots__ = [
61
+ "type",
62
+ "invocation_handler"
63
+ ]
64
+
47
65
  # constructor
48
66
 
49
67
  def __init__(self, type: Type[T], invocation_handler: 'DynamicProxy.InvocationHandler'):
@@ -53,7 +71,16 @@ class DynamicProxy(Generic[T]):
53
71
  # public
54
72
 
55
73
  def __getattr__(self, name):
56
- def wrapper(*args, **kwargs):
57
- return self.invocation_handler.invoke(DynamicProxy.Invocation(self.type, name, *args, **kwargs))
74
+ method = getattr(self.type, name)
75
+
76
+ if inspect.iscoroutinefunction(method):
77
+ async def async_wrapper(*args, **kwargs):
78
+ return await self.invocation_handler.invoke_async(DynamicProxy.Invocation(self.type, method, *args, **kwargs))
79
+
80
+ return async_wrapper
81
+
82
+ else:
83
+ def sync_wrapper(*args, **kwargs):
84
+ return self.invocation_handler.invoke(DynamicProxy.Invocation(self.type, method, *args, **kwargs))
58
85
 
59
- return wrapper
86
+ return sync_wrapper
@@ -5,8 +5,10 @@ including their methods, decorators, and type hints. It supports caching for per
5
5
  from __future__ import annotations
6
6
 
7
7
  import inspect
8
- from inspect import signature, getmembers
8
+ from inspect import signature
9
9
  import threading
10
+ from types import FunctionType
11
+
10
12
  from typing import Callable, get_type_hints, Type, Dict, Optional
11
13
  from weakref import WeakKeyDictionary
12
14
 
@@ -25,7 +27,7 @@ class DecoratorDescriptor:
25
27
  self.args = args
26
28
 
27
29
  def __str__(self):
28
- return f"@{self.decorator.__name__}({','.join(self.args)})"
30
+ return f"@{self.decorator.__name__}({', '.join(map(str, self.args))})"
29
31
 
30
32
  class Decorators:
31
33
  """
@@ -59,6 +61,14 @@ class Decorators:
59
61
  """
60
62
  return any(decorator.decorator is callable for decorator in Decorators.get(func_or_class))
61
63
 
64
+ @classmethod
65
+ def get_decorator(cls, func_or_class, callable: Callable) -> DecoratorDescriptor:
66
+ return next((decorator for decorator in Decorators.get_all(func_or_class) if decorator.decorator is callable), None)
67
+
68
+ @classmethod
69
+ def get_all(cls, func_or_class) -> list[DecoratorDescriptor]:
70
+ return getattr(func_or_class, '__decorators__', [])
71
+
62
72
  @classmethod
63
73
  def get(cls, func_or_class) -> list[DecoratorDescriptor]:
64
74
  """
@@ -67,9 +77,14 @@ class Decorators:
67
77
  func_or_class: the function or class
68
78
 
69
79
  Returns:
70
- list[DecoratorDescriptor]: ths list
80
+ list[DecoratorDescriptor]: the list
71
81
  """
72
- return getattr(func_or_class, '__decorators__', [])
82
+ if inspect.ismethod(func_or_class):
83
+ func_or_class = func_or_class.__func__ # unwrap bound method
84
+
85
+ #return getattr(func_or_class, '__decorators__', []) will return inherited as well
86
+ return func_or_class.__dict__.get('__decorators__', [])
87
+
73
88
 
74
89
  class TypeDescriptor:
75
90
  """
@@ -130,6 +145,9 @@ class TypeDescriptor:
130
145
  """
131
146
  return inspect.iscoroutinefunction(self.method)
132
147
 
148
+ def get_decorators(self) -> list[DecoratorDescriptor]:
149
+ return self.decorators
150
+
133
151
  def get_decorator(self, decorator: Callable) -> Optional[DecoratorDescriptor]:
134
152
  """
135
153
  return the DecoratorDescriptor - if any - associated with the passed Callable
@@ -212,10 +230,16 @@ class TypeDescriptor:
212
230
  # internal
213
231
 
214
232
  def _get_local_members(self, cls):
233
+ #return [
234
+ # (name, value)
235
+ # for name, value in getmembers(cls, predicate=inspect.isfunction)
236
+ # if name in cls.__dict__
237
+ #]
238
+
215
239
  return [
216
- (name, value)
217
- for name, value in getmembers(cls, predicate=inspect.isfunction)
218
- if name in cls.__dict__
240
+ (name, attr)
241
+ for name, attr in cls.__dict__.items()
242
+ if isinstance(attr, FunctionType)
219
243
  ]
220
244
 
221
245
  # public
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aspyx
3
- Version: 1.4.1
3
+ Version: 1.5.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
@@ -24,13 +24,11 @@ License: MIT License
24
24
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
-
27
+ License-File: LICENSE
28
28
  Requires-Python: >=3.9
29
+ Requires-Dist: python-dotenv~=1.1.0
30
+ Requires-Dist: pyyaml~=6.0.2
29
31
  Description-Content-Type: text/markdown
30
- License-File: LICENSE
31
- Provides-Extra: dev
32
- Requires-Dist: mkdocstrings-python; extra == "dev"
33
- Dynamic: license-file
34
32
 
35
33
  # aspyx
36
34
 
@@ -73,7 +71,7 @@ Dynamic: license-file
73
71
 
74
72
  While working on AI-related projects in Python, I was looking for a dependency injection (DI) framework. After evaluating existing options, my impression was that the most either lacked key features — such as integrated AOP — or had APIs that felt overly technical and complex, which made me develop a library on my own with the following goals
75
73
 
76
- - bring both di and AOP features together in a lightweight library ( still only about 2T loc),
74
+ - bring both di and AOP features together in a lightweight library,
77
75
  - be as minimal invasive as possible,
78
76
  - offering mechanisms to easily extend and customize features without touching the core,
79
77
  - while still offering a _simple_ and _readable_ api that doesnt overwhelm developers and only requires a minimum initial learning curve
@@ -85,7 +83,7 @@ The AOP integration, in particular, makes a lot of sense because:
85
83
 
86
84
  # Overview
87
85
 
88
- Aspyx is a lightweight - still only about 2T LOC- Python library that provides both Dependency Injection (DI) and Aspect-Oriented Programming (AOP) support.
86
+ Aspyx is a lightweight - still only about 2K LOC - Python library that provides both Dependency Injection (DI) and Aspect-Oriented Programming (AOP) support.
89
87
 
90
88
  The following DI features are supported
91
89
  - constructor and setter injection
@@ -100,7 +98,7 @@ The following DI features are supported
100
98
  - lifecycle events methods `on_init`, `on_destroy`, `on_running`
101
99
  - Automatic discovery and bundling of injectable objects based on their module location, including support for recursive imports
102
100
  - Instantiation of one or possible more isolated container instances — called environments — each managing the lifecycle of a related set of objects,
103
- - Support for hierarchical environments, enabling structured scoping and layered object management.
101
+ - Support for hierarchical environments, enabling structured scoping and layered object management.
104
102
 
105
103
  With respect to AOP:
106
104
  - support for before, around, after and error aspects
@@ -839,7 +837,3 @@ class ExceptionAdvice:
839
837
  **1.4.1**
840
838
 
841
839
  - mkdocs
842
-
843
-
844
-
845
-
@@ -1,6 +1,6 @@
1
- aspyx/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- aspyx/di/__init__.py,sha256=BDOloIhmCIUJWC7l4PLtuiWS1LlWyitIofLCLcyXYpQ,1097
3
- aspyx/di/di.py,sha256=ozZanDcrmluzghQBqqD_vbav3Civ-V4R8XXwlBAM3MI,44068
1
+ aspyx/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
2
+ aspyx/di/__init__.py,sha256=AGVU2VBWQyBxSssvbk_GOKrYWIYtcmSoIlupz-Oqxi4,1138
3
+ aspyx/di/di.py,sha256=O8FUZm68aimUbt7GnIc43XzRa47iFV8Qh696JGOwdEE,44703
4
4
  aspyx/di/aop/__init__.py,sha256=rn6LSpzFtUOlgaBATyhLRWBzFmZ6XoVKA9B8SgQzYEI,746
5
5
  aspyx/di/aop/aop.py,sha256=Cn-fqFW6PznVDM38fPX7mqlSpjGKMsgpJRBSYBv59xY,18403
6
6
  aspyx/di/configuration/__init__.py,sha256=flM9A79J2wfA5I8goQbxs4tTqYustR9tn_9s0YO2WJQ,484
@@ -10,16 +10,15 @@ aspyx/di/configuration/yaml_configuration_source.py,sha256=NDl3SeoLMNVlzHgfP-Ysv
10
10
  aspyx/di/threading/__init__.py,sha256=qrWdaq7MewQ2UmZy4J0Dn6BhY-ahfiG3xsv-EHqoqSE,191
11
11
  aspyx/di/threading/synchronized.py,sha256=BQ9PjMQUJsF5r-qWaDgvqg3AvFm_R9QZdKB49EkoelQ,1263
12
12
  aspyx/exception/__init__.py,sha256=OZwv-C3ZHD0Eg1rohCQMj575WLJ7lfYuk6PZD6sh1MA,211
13
- aspyx/exception/exception_manager.py,sha256=ihQ8Hs_EAUi-4xtVOn6kNZVblJjznpscLQ4vKXfWq7s,5228
13
+ aspyx/exception/exception_manager.py,sha256=tv0nb0b2CFPiYWK6wwH9yI8hSc--9Xz9_xQVBwU42wc,5228
14
14
  aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
15
- aspyx/reflection/proxy.py,sha256=kaVPeEGuerdYgcgchMe99c8xykDskRSYR-w4OF83Ofo,1868
16
- aspyx/reflection/reflection.py,sha256=AgChenUzK9elcqOc_BfMiszTQuXrsy0NHYkqy9Jsl0E,8066
15
+ aspyx/reflection/proxy.py,sha256=9zqzmK2HGGx7LxdiBw8MfKRNT8H03h_0I6Y972eKFH8,2582
16
+ aspyx/reflection/reflection.py,sha256=HYzbzExG7jzdHG_AggrRxy9yte6Cl192dPsJBRl785Y,8939
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.4.1.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
22
- aspyx-1.4.1.dist-info/METADATA,sha256=cEYH9P1Mng4RjRQID-bUp-5X6fbxUHhQWf3WDUwkPI0,26564
23
- aspyx-1.4.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- aspyx-1.4.1.dist-info/top_level.txt,sha256=A_ZwhBY_ybIgjZlztd44eaOrWqkJAndiqjGlbJ3tR_I,6
25
- aspyx-1.4.1.dist-info/RECORD,,
21
+ aspyx-1.5.1.dist-info/METADATA,sha256=U4sRH9owvFuCgQINKFlC84hL5BnQ-5kGavDNb-q8D88,26490
22
+ aspyx-1.5.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
23
+ aspyx-1.5.1.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
24
+ aspyx-1.5.1.dist-info/RECORD,,
@@ -1,5 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: hatchling 1.27.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
-
@@ -1 +0,0 @@
1
- aspyx