aspyx 1.4.0__py3-none-any.whl → 1.5.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 +1 -0
- aspyx/di/__init__.py +3 -3
- aspyx/di/aop/__init__.py +16 -2
- aspyx/di/aop/aop.py +64 -22
- aspyx/di/configuration/__init__.py +3 -3
- aspyx/di/configuration/configuration.py +7 -5
- aspyx/di/di.py +188 -44
- aspyx/exception/__init__.py +1 -1
- aspyx/exception/exception_manager.py +35 -18
- aspyx/reflection/proxy.py +35 -10
- aspyx/reflection/reflection.py +84 -20
- aspyx/threading/__init__.py +1 -1
- aspyx/threading/thread_local.py +6 -2
- aspyx/util/stringbuilder.py +6 -2
- aspyx-1.5.0.dist-info/METADATA +33 -0
- aspyx-1.5.0.dist-info/RECORD +24 -0
- {aspyx-1.4.0.dist-info → aspyx-1.5.0.dist-info}/WHEEL +1 -2
- aspyx-1.4.0.dist-info/METADATA +0 -825
- aspyx-1.4.0.dist-info/RECORD +0 -25
- aspyx-1.4.0.dist-info/top_level.txt +0 -1
- {aspyx-1.4.0.dist-info → aspyx-1.5.0.dist-info}/licenses/LICENSE +0 -0
aspyx/reflection/reflection.py
CHANGED
|
@@ -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
|
|
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,27 +27,64 @@ 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
|
"""
|
|
32
34
|
Utility class that caches decorators ( Python does not have a feature for this )
|
|
33
35
|
"""
|
|
34
36
|
@classmethod
|
|
35
|
-
def add(cls,
|
|
36
|
-
|
|
37
|
+
def add(cls, func_or_class, decorator: Callable, *args):
|
|
38
|
+
"""
|
|
39
|
+
Remember the decorator
|
|
40
|
+
Args:
|
|
41
|
+
func_or_class: a function or class
|
|
42
|
+
decorator: the decorator
|
|
43
|
+
*args: any arguments supplied to the decorator
|
|
44
|
+
"""
|
|
45
|
+
decorators = getattr(func_or_class, '__decorators__', None)
|
|
37
46
|
if decorators is None:
|
|
38
|
-
setattr(
|
|
47
|
+
setattr(func_or_class, '__decorators__', [DecoratorDescriptor(decorator, *args)])
|
|
39
48
|
else:
|
|
40
49
|
decorators.append(DecoratorDescriptor(decorator, *args))
|
|
41
50
|
|
|
42
51
|
@classmethod
|
|
43
|
-
def has_decorator(cls,
|
|
44
|
-
|
|
52
|
+
def has_decorator(cls, func_or_class, callable: Callable) -> bool:
|
|
53
|
+
"""
|
|
54
|
+
Return True, if the function or class is decorated with the decorator
|
|
55
|
+
Args:
|
|
56
|
+
func_or_class: a function or class
|
|
57
|
+
callable: the decorator
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
bool: the result
|
|
61
|
+
"""
|
|
62
|
+
return any(decorator.decorator is callable for decorator in Decorators.get(func_or_class))
|
|
45
63
|
|
|
46
64
|
@classmethod
|
|
47
|
-
def
|
|
48
|
-
return
|
|
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
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def get(cls, func_or_class) -> list[DecoratorDescriptor]:
|
|
74
|
+
"""
|
|
75
|
+
return the list of decorators associated with the given function or class
|
|
76
|
+
Args:
|
|
77
|
+
func_or_class: the function or class
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
list[DecoratorDescriptor]: the list
|
|
81
|
+
"""
|
|
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
|
+
|
|
49
88
|
|
|
50
89
|
class TypeDescriptor:
|
|
51
90
|
"""
|
|
@@ -79,30 +118,45 @@ class TypeDescriptor:
|
|
|
79
118
|
def get_name(self) -> str:
|
|
80
119
|
"""
|
|
81
120
|
return the method name
|
|
82
|
-
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
str: the method name
|
|
83
124
|
"""
|
|
84
125
|
return self.method.__name__
|
|
85
126
|
|
|
86
127
|
def get_doc(self, default = "") -> str:
|
|
87
128
|
"""
|
|
88
129
|
return the method docstring
|
|
89
|
-
|
|
90
|
-
:
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
default: the default if no docstring is found
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
str: the docstring
|
|
91
136
|
"""
|
|
92
137
|
return self.method.__doc__ or default
|
|
93
138
|
|
|
94
139
|
def is_async(self) -> bool:
|
|
95
140
|
"""
|
|
96
141
|
return true if the method is asynchronous
|
|
97
|
-
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
bool: async flag
|
|
98
145
|
"""
|
|
99
146
|
return inspect.iscoroutinefunction(self.method)
|
|
100
147
|
|
|
148
|
+
def get_decorators(self) -> list[DecoratorDescriptor]:
|
|
149
|
+
return self.decorators
|
|
150
|
+
|
|
101
151
|
def get_decorator(self, decorator: Callable) -> Optional[DecoratorDescriptor]:
|
|
102
152
|
"""
|
|
103
153
|
return the DecoratorDescriptor - if any - associated with the passed Callable
|
|
104
|
-
|
|
105
|
-
:
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
decorator: the decorator
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
Optional[DecoratorDescriptor]: the DecoratorDescriptor or None
|
|
106
160
|
"""
|
|
107
161
|
for dec in self.decorators:
|
|
108
162
|
if dec.decorator is decorator:
|
|
@@ -113,8 +167,12 @@ class TypeDescriptor:
|
|
|
113
167
|
def has_decorator(self, decorator: Callable) -> bool:
|
|
114
168
|
"""
|
|
115
169
|
return True if the method is decorated with the decorator
|
|
116
|
-
|
|
117
|
-
:
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
decorator: the decorator callable
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
bool: True if the method is decorated with the decorator
|
|
118
176
|
"""
|
|
119
177
|
for dec in self.decorators:
|
|
120
178
|
if dec.decorator is decorator:
|
|
@@ -172,10 +230,16 @@ class TypeDescriptor:
|
|
|
172
230
|
# internal
|
|
173
231
|
|
|
174
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
|
+
|
|
175
239
|
return [
|
|
176
|
-
(name,
|
|
177
|
-
for name,
|
|
178
|
-
if
|
|
240
|
+
(name, attr)
|
|
241
|
+
for name, attr in cls.__dict__.items()
|
|
242
|
+
if isinstance(attr, FunctionType)
|
|
179
243
|
]
|
|
180
244
|
|
|
181
245
|
# public
|
aspyx/threading/__init__.py
CHANGED
aspyx/threading/thread_local.py
CHANGED
|
@@ -22,7 +22,9 @@ class ThreadLocal(Generic[T]):
|
|
|
22
22
|
def get(self) -> Optional[T]:
|
|
23
23
|
"""
|
|
24
24
|
return the current value or invoke the optional factory to compute one
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
Optional[T]: the value associated with the current thread
|
|
26
28
|
"""
|
|
27
29
|
if not hasattr(self.local, "value"):
|
|
28
30
|
if self.factory is not None:
|
|
@@ -35,7 +37,9 @@ class ThreadLocal(Generic[T]):
|
|
|
35
37
|
def set(self, value: T) -> None:
|
|
36
38
|
"""
|
|
37
39
|
set a value in the current thread
|
|
38
|
-
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
value: the value
|
|
39
43
|
"""
|
|
40
44
|
self.local.value = value
|
|
41
45
|
|
aspyx/util/stringbuilder.py
CHANGED
|
@@ -17,8 +17,12 @@ class StringBuilder:
|
|
|
17
17
|
def append(self, s: str) -> "StringBuilder":
|
|
18
18
|
"""
|
|
19
19
|
append a string to the end of the string builder
|
|
20
|
-
|
|
21
|
-
:
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
s (str): the string
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
StringBuilder: self
|
|
22
26
|
"""
|
|
23
27
|
self._parts.append(str(s))
|
|
24
28
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aspyx
|
|
3
|
+
Version: 1.5.0
|
|
4
|
+
Summary: A DI and AOP library for Python
|
|
5
|
+
Author-email: Andreas Ernst <andreas.ernst7@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2025 Andreas Ernst
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
|
+
Requires-Dist: python-dotenv~=1.1.0
|
|
30
|
+
Requires-Dist: pyyaml~=6.0.2
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
aspyx
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
aspyx/__init__.py,sha256=MsSFjiLMLJZ7QhUPpVBWKiyDnCzryquRyr329NoCACI,2
|
|
2
|
+
aspyx/di/__init__.py,sha256=AGVU2VBWQyBxSssvbk_GOKrYWIYtcmSoIlupz-Oqxi4,1138
|
|
3
|
+
aspyx/di/di.py,sha256=MN9I-KIAgBQYakctEZQH3W1_jjsc7pgGyeLt7eJr0yY,46539
|
|
4
|
+
aspyx/di/aop/__init__.py,sha256=rn6LSpzFtUOlgaBATyhLRWBzFmZ6XoVKA9B8SgQzYEI,746
|
|
5
|
+
aspyx/di/aop/aop.py,sha256=Cn-fqFW6PznVDM38fPX7mqlSpjGKMsgpJRBSYBv59xY,18403
|
|
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=BQ9PjMQUJsF5r-qWaDgvqg3AvFm_R9QZdKB49EkoelQ,1263
|
|
12
|
+
aspyx/exception/__init__.py,sha256=OZwv-C3ZHD0Eg1rohCQMj575WLJ7lfYuk6PZD6sh1MA,211
|
|
13
|
+
aspyx/exception/exception_manager.py,sha256=tv0nb0b2CFPiYWK6wwH9yI8hSc--9Xz9_xQVBwU42wc,5228
|
|
14
|
+
aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
|
|
15
|
+
aspyx/reflection/proxy.py,sha256=9zqzmK2HGGx7LxdiBw8MfKRNT8H03h_0I6Y972eKFH8,2582
|
|
16
|
+
aspyx/reflection/reflection.py,sha256=HYzbzExG7jzdHG_AggrRxy9yte6Cl192dPsJBRl785Y,8939
|
|
17
|
+
aspyx/threading/__init__.py,sha256=3clmbCDP37GPan3dWtxTQvpg0Ti4aFzruAbUClkHGi0,147
|
|
18
|
+
aspyx/threading/thread_local.py,sha256=86dNtbA4k2B-rNUUnZgn3_pU0DAojgLrRnh8RL6zf1E,1196
|
|
19
|
+
aspyx/util/__init__.py,sha256=8H2yKkXu3nkRGeTerb8ialzKGfvzUx44XUWFUYcYuQM,125
|
|
20
|
+
aspyx/util/stringbuilder.py,sha256=a-0T4YEXSJFUuQ3ztKN1ZPARkh8dIGMSkNEEJHRN7dc,856
|
|
21
|
+
aspyx-1.5.0.dist-info/METADATA,sha256=dm6A8H-_qsn1NDzfFFG2AYclm7Bj6BbLdgtbuVRR6HY,1540
|
|
22
|
+
aspyx-1.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
23
|
+
aspyx-1.5.0.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
24
|
+
aspyx-1.5.0.dist-info/RECORD,,
|