aspyx 0.1.0__py3-none-any.whl → 1.0.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/di/__init__.py +4 -1
- aspyx/di/aop/__init__.py +4 -1
- aspyx/di/aop/aop.py +98 -88
- aspyx/di/configuration/__init__.py +4 -1
- aspyx/di/configuration/configuration.py +21 -14
- aspyx/di/di.py +137 -109
- aspyx/reflection/__init__.py +4 -1
- aspyx/reflection/proxy.py +10 -7
- aspyx/reflection/reflection.py +35 -22
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/METADATA +147 -43
- aspyx-1.0.1.dist-info/RECORD +14 -0
- aspyx-0.1.0.dist-info/RECORD +0 -14
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/WHEEL +0 -0
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/licenses/LICENSE +0 -0
- {aspyx-0.1.0.dist-info → aspyx-1.0.1.dist-info}/top_level.txt +0 -0
aspyx/reflection/proxy.py
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dynamic proxies for method interception and delegation.
|
|
3
|
+
"""
|
|
1
4
|
from typing import Generic, TypeVar, Type
|
|
2
5
|
|
|
3
6
|
T = TypeVar("T")
|
|
@@ -22,7 +25,7 @@ class DynamicProxy(Generic[T]):
|
|
|
22
25
|
|
|
23
26
|
Attributes:
|
|
24
27
|
type: The proxied class type.
|
|
25
|
-
|
|
28
|
+
invocation_handler: The handler that processes intercepted method calls.
|
|
26
29
|
"""
|
|
27
30
|
# inner class
|
|
28
31
|
|
|
@@ -40,19 +43,19 @@ class DynamicProxy(Generic[T]):
|
|
|
40
43
|
# class methods
|
|
41
44
|
|
|
42
45
|
@classmethod
|
|
43
|
-
def create(cls, type: Type[T],
|
|
44
|
-
return DynamicProxy(type,
|
|
46
|
+
def create(cls, type: Type[T], invocation_handler: 'DynamicProxy.InvocationHandler') -> T:
|
|
47
|
+
return DynamicProxy(type, invocation_handler)
|
|
45
48
|
|
|
46
49
|
# constructor
|
|
47
50
|
|
|
48
|
-
def __init__(self, type: Type[T],
|
|
51
|
+
def __init__(self, type: Type[T], invocation_handler: 'DynamicProxy.InvocationHandler'):
|
|
49
52
|
self.type = type
|
|
50
|
-
self.
|
|
53
|
+
self.invocation_handler = invocation_handler
|
|
51
54
|
|
|
52
55
|
# public
|
|
53
56
|
|
|
54
57
|
def __getattr__(self, name):
|
|
55
58
|
def wrapper(*args, **kwargs):
|
|
56
|
-
return self.
|
|
59
|
+
return self.invocation_handler.invoke(DynamicProxy.Invocation(self.type, name, *args, **kwargs))
|
|
57
60
|
|
|
58
|
-
return wrapper
|
|
61
|
+
return wrapper
|
aspyx/reflection/reflection.py
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module provides a TypeDescriptor class that allows introspection of Python classes,
|
|
3
|
+
including their methods, decorators, and type hints. It supports caching for performance
|
|
4
|
+
"""
|
|
1
5
|
from __future__ import annotations
|
|
2
6
|
|
|
3
7
|
import inspect
|
|
@@ -5,7 +9,13 @@ from inspect import signature, getmembers
|
|
|
5
9
|
from typing import Callable, get_type_hints, Type, Dict, Optional
|
|
6
10
|
from weakref import WeakKeyDictionary
|
|
7
11
|
|
|
12
|
+
|
|
8
13
|
class DecoratorDescriptor:
|
|
14
|
+
__slots__ = [
|
|
15
|
+
"decorator",
|
|
16
|
+
"args"
|
|
17
|
+
]
|
|
18
|
+
|
|
9
19
|
def __init__(self, decorator, *args):
|
|
10
20
|
self.decorator = decorator
|
|
11
21
|
self.args = args
|
|
@@ -34,27 +44,27 @@ class TypeDescriptor:
|
|
|
34
44
|
self.clazz = cls
|
|
35
45
|
self.method = method
|
|
36
46
|
self.decorators: list[DecoratorDescriptor] = Decorators.get(method)
|
|
37
|
-
self.
|
|
47
|
+
self.param_types : list[Type] = []
|
|
38
48
|
|
|
39
49
|
type_hints = get_type_hints(method)
|
|
40
50
|
sig = signature(method)
|
|
41
51
|
|
|
42
|
-
for name,
|
|
52
|
+
for name, _ in sig.parameters.items():
|
|
43
53
|
if name != 'self':
|
|
44
|
-
self.
|
|
54
|
+
self.param_types.append(type_hints.get(name, object))
|
|
45
55
|
|
|
46
|
-
self.
|
|
56
|
+
self.return_type = type_hints.get('return', None)
|
|
47
57
|
|
|
48
|
-
def get_decorator(self, decorator):
|
|
58
|
+
def get_decorator(self, decorator) -> Optional[DecoratorDescriptor]:
|
|
49
59
|
for dec in self.decorators:
|
|
50
|
-
if dec.decorator
|
|
60
|
+
if dec.decorator is decorator:
|
|
51
61
|
return dec
|
|
52
62
|
|
|
53
63
|
return None
|
|
54
64
|
|
|
55
|
-
def has_decorator(self, decorator):
|
|
65
|
+
def has_decorator(self, decorator) -> bool:
|
|
56
66
|
for dec in self.decorators:
|
|
57
|
-
if dec.decorator
|
|
67
|
+
if dec.decorator is decorator:
|
|
58
68
|
return True
|
|
59
69
|
|
|
60
70
|
return False
|
|
@@ -62,8 +72,6 @@ class TypeDescriptor:
|
|
|
62
72
|
def __str__(self):
|
|
63
73
|
return f"Method({self.method.__name__})"
|
|
64
74
|
|
|
65
|
-
# class methods
|
|
66
|
-
|
|
67
75
|
# class properties
|
|
68
76
|
|
|
69
77
|
_cache = WeakKeyDictionary()
|
|
@@ -76,6 +84,7 @@ class TypeDescriptor:
|
|
|
76
84
|
if descriptor is None:
|
|
77
85
|
descriptor = TypeDescriptor(clazz)
|
|
78
86
|
cls._cache[clazz] = descriptor
|
|
87
|
+
|
|
79
88
|
return descriptor
|
|
80
89
|
|
|
81
90
|
# constructor
|
|
@@ -83,27 +92,25 @@ class TypeDescriptor:
|
|
|
83
92
|
def __init__(self, cls):
|
|
84
93
|
self.cls = cls
|
|
85
94
|
self.decorators = Decorators.get(cls)
|
|
86
|
-
self.methods: Dict[str, TypeDescriptor.MethodDescriptor] =
|
|
87
|
-
self.
|
|
95
|
+
self.methods: Dict[str, TypeDescriptor.MethodDescriptor] = {}
|
|
96
|
+
self.local_methods: Dict[str, TypeDescriptor.MethodDescriptor] = {}
|
|
88
97
|
|
|
89
98
|
# check superclasses
|
|
90
99
|
|
|
91
|
-
self.
|
|
100
|
+
self.super_types = [TypeDescriptor.for_type(x) for x in cls.__bases__ if not x is object]
|
|
92
101
|
|
|
93
|
-
for
|
|
94
|
-
self.methods = self.methods |
|
|
102
|
+
for super_type in self.super_types:
|
|
103
|
+
self.methods = self.methods | super_type.methods
|
|
95
104
|
|
|
96
105
|
# methods
|
|
97
106
|
|
|
98
107
|
for name, member in self._get_local_members(cls):
|
|
99
108
|
method = TypeDescriptor.MethodDescriptor(cls, member)
|
|
100
|
-
self.
|
|
109
|
+
self.local_methods[name] = method
|
|
101
110
|
self.methods[name] = method
|
|
102
111
|
|
|
103
112
|
# internal
|
|
104
113
|
|
|
105
|
-
#isinstance(attr, classmethod)
|
|
106
|
-
|
|
107
114
|
def _get_local_members(self, cls):
|
|
108
115
|
return [
|
|
109
116
|
(name, value)
|
|
@@ -127,8 +134,14 @@ class TypeDescriptor:
|
|
|
127
134
|
|
|
128
135
|
return False
|
|
129
136
|
|
|
130
|
-
def
|
|
131
|
-
|
|
137
|
+
def get_methods(self, local = False) -> list[TypeDescriptor.MethodDescriptor]:
|
|
138
|
+
if local:
|
|
139
|
+
return list(self.local_methods.values())
|
|
140
|
+
else:
|
|
141
|
+
return list(self.methods.values())
|
|
132
142
|
|
|
133
|
-
def get_method(self, name) -> Optional[MethodDescriptor]:
|
|
134
|
-
|
|
143
|
+
def get_method(self, name: str, local = False) -> Optional[TypeDescriptor.MethodDescriptor]:
|
|
144
|
+
if local:
|
|
145
|
+
return self.local_methods.get(name, None)
|
|
146
|
+
else:
|
|
147
|
+
return self.methods.get(name, None)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aspyx
|
|
3
|
-
Version: 0.1
|
|
3
|
+
Version: 1.0.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
|
|
@@ -25,20 +25,22 @@ License: MIT License
|
|
|
25
25
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
26
|
SOFTWARE.
|
|
27
27
|
|
|
28
|
-
Requires-Python: >=3.
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
29
|
Description-Content-Type: text/markdown
|
|
30
30
|
License-File: LICENSE
|
|
31
31
|
Dynamic: license-file
|
|
32
32
|
|
|
33
33
|
# aspyx
|
|
34
34
|
|
|
35
|
-

|
|
36
|
+

|
|
37
|
+

|
|
38
|
+

|
|
38
39
|
|
|
39
40
|
## Table of Contents
|
|
40
41
|
|
|
41
42
|
- [Introduction](#aspyx)
|
|
43
|
+
- [Installation](#installation)
|
|
42
44
|
- [Registration](#registration)
|
|
43
45
|
- [Class](#class)
|
|
44
46
|
- [Class Factory](#class-factory)
|
|
@@ -46,11 +48,14 @@ Dynamic: license-file
|
|
|
46
48
|
- [Environment](#environment)
|
|
47
49
|
- [Definition](#definition)
|
|
48
50
|
- [Retrieval](#retrieval)
|
|
49
|
-
- [
|
|
50
|
-
- [
|
|
51
|
+
- [Instantiation logic](#instantiation-logic)
|
|
52
|
+
- [Injection Methods](#injection-methods)
|
|
53
|
+
- [Lifecycle Methods](#lifecycle-methods)
|
|
54
|
+
- [Post Processors](#post-processors)
|
|
51
55
|
- [Custom scopes](#custom-scopes)
|
|
52
56
|
- [AOP](#aop)
|
|
53
57
|
- [Configuration](#configuration)
|
|
58
|
+
- [Reflection](#reflection)
|
|
54
59
|
|
|
55
60
|
# Introduction
|
|
56
61
|
|
|
@@ -69,12 +74,13 @@ The following features are supported
|
|
|
69
74
|
- container instances that relate to environment classes and manage the lifecylce of related objects
|
|
70
75
|
- hierarchical environments
|
|
71
76
|
|
|
77
|
+
The library is thread-safe!
|
|
78
|
+
|
|
72
79
|
Let's look at a simple example
|
|
73
80
|
|
|
74
81
|
```python
|
|
75
82
|
from aspyx.di import injectable, on_init, on_destroy, environment, Environment
|
|
76
83
|
|
|
77
|
-
|
|
78
84
|
@injectable()
|
|
79
85
|
class Foo:
|
|
80
86
|
def __init__(self):
|
|
@@ -83,13 +89,12 @@ class Foo:
|
|
|
83
89
|
def hello(msg: str):
|
|
84
90
|
print(f"hello {msg}")
|
|
85
91
|
|
|
86
|
-
|
|
87
92
|
@injectable() # eager and singleton by default
|
|
88
93
|
class Bar:
|
|
89
94
|
def __init__(self, foo: Foo): # will inject the Foo dependency
|
|
90
95
|
self.foo = foo
|
|
91
96
|
|
|
92
|
-
@on_init() # a lifecycle callback called
|
|
97
|
+
@on_init() # a lifecycle callback called after the constructor and all possible injections
|
|
93
98
|
def init(self):
|
|
94
99
|
...
|
|
95
100
|
|
|
@@ -99,41 +104,40 @@ class Bar:
|
|
|
99
104
|
|
|
100
105
|
@environment()
|
|
101
106
|
class SampleEnvironment:
|
|
102
|
-
# constructor
|
|
103
|
-
|
|
104
107
|
def __init__(self):
|
|
105
108
|
pass
|
|
106
109
|
|
|
107
|
-
|
|
108
110
|
# go, forrest
|
|
109
111
|
|
|
110
|
-
environment = SampleEnvironment
|
|
112
|
+
environment = Environment(SampleEnvironment)
|
|
111
113
|
|
|
112
114
|
bar = env.get(Bar)
|
|
113
|
-
|
|
115
|
+
|
|
116
|
+
bar.foo.hello("world")
|
|
114
117
|
```
|
|
115
118
|
|
|
116
|
-
The concepts should be pretty familiar
|
|
119
|
+
The concepts should be pretty familiar as well as the names which are a combination of Spring and Angular names :-)
|
|
117
120
|
|
|
118
121
|
Let's add some aspects...
|
|
119
122
|
|
|
120
123
|
```python
|
|
124
|
+
|
|
121
125
|
@advice
|
|
122
126
|
class SampleAdvice:
|
|
123
|
-
def __init__(self):
|
|
127
|
+
def __init__(self): # could inject additional stuff
|
|
124
128
|
pass
|
|
125
129
|
|
|
126
130
|
@before(methods().named("hello").of_type(Foo))
|
|
127
|
-
def
|
|
131
|
+
def call_before(self, invocation: Invocation):
|
|
128
132
|
print("before Foo.hello(...)")
|
|
129
133
|
|
|
130
134
|
@error(methods().named("hello").of_type(Foo))
|
|
131
|
-
def
|
|
135
|
+
def call_error(self, invocation: Invocation):
|
|
132
136
|
print("error Foo.hello(...)")
|
|
133
137
|
print(invocation.exception)
|
|
134
138
|
|
|
135
139
|
@around(methods().named("hello"))
|
|
136
|
-
def
|
|
140
|
+
def call_around(self, invocation: Invocation):
|
|
137
141
|
print("around Foo.hello()")
|
|
138
142
|
|
|
139
143
|
return invocation.proceed()
|
|
@@ -148,6 +152,14 @@ The invocation parameter stores the complete context of the current execution, w
|
|
|
148
152
|
|
|
149
153
|
Let's look at the details
|
|
150
154
|
|
|
155
|
+
# Installation
|
|
156
|
+
|
|
157
|
+
`pip install aspyx`
|
|
158
|
+
|
|
159
|
+
The library is tested with Python version > 3.8
|
|
160
|
+
|
|
161
|
+
Ready to go...
|
|
162
|
+
|
|
151
163
|
# Registration
|
|
152
164
|
|
|
153
165
|
Different mechanisms are available that make classes eligible for injection
|
|
@@ -164,16 +176,19 @@ class Foo:
|
|
|
164
176
|
def __init__(self):
|
|
165
177
|
pass
|
|
166
178
|
```
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
The constructor can only define parameter types that are known as well to the container!
|
|
179
|
+
Please make sure, that the class defines a local constructor, as this is required to determine injected instances.
|
|
180
|
+
All referenced types will be injected by the environemnt.
|
|
170
181
|
|
|
182
|
+
Only eligible types are allowed, of course!
|
|
171
183
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
184
|
+
The decorator accepts the keyword arguments
|
|
185
|
+
- `eager : boolean`
|
|
186
|
+
if `True`, the container will create the instances automatically while booting the environment. This is the default.
|
|
187
|
+
- `scope: str`
|
|
188
|
+
the name of a scope which will determine how often instances will be created.
|
|
189
|
+
`singleton` will create it only once - per environment -, while `request` will recreate it on every injection request. The default is `singleton`
|
|
175
190
|
|
|
176
|
-
Other scopes can be defined. Please check the corresponding chapter.
|
|
191
|
+
Other scopes - e.g. session related scopes - can be defined dynamically. Please check the corresponding chapter.
|
|
177
192
|
|
|
178
193
|
## Class Factory
|
|
179
194
|
|
|
@@ -268,15 +283,55 @@ In case of ambiguities, it will throw an exception.
|
|
|
268
283
|
|
|
269
284
|
Please be aware, that a base class are not _required_ to be annotated with `@injectable`, as this would mean, that it could be created on its own as well. ( Which is possible as well, btw. )
|
|
270
285
|
|
|
271
|
-
#
|
|
286
|
+
# Instantiation logic
|
|
287
|
+
|
|
288
|
+
Constructing a new instance involves a number of steps executed in this order
|
|
289
|
+
- Constructor call
|
|
290
|
+
the constructor is called with the resolved parameters
|
|
291
|
+
- Advice injection
|
|
292
|
+
All methods involving aspects are updated
|
|
293
|
+
- Lifecycle methods
|
|
294
|
+
different decorators can mark methods that should be called during the lifecycle ( here the construction ) of an instance.
|
|
295
|
+
These are various injection possibilities as well as an optional final `on_init` call
|
|
296
|
+
- PostProcessors
|
|
297
|
+
Any custom post processors, that can add isde effects or modify the instances
|
|
298
|
+
|
|
299
|
+
## Injection methods
|
|
300
|
+
|
|
301
|
+
Different decorators are implemented, that call methods with computed values
|
|
302
|
+
|
|
303
|
+
- `@inject`
|
|
304
|
+
the method is called with all resolved parameter types ( same as the constructor call)
|
|
305
|
+
- `@inject_environment`
|
|
306
|
+
the method is called with the creating environment as a single parameter
|
|
307
|
+
- `@value()`
|
|
308
|
+
the method is called with a resolved configuration value. Check the corresponding chapter
|
|
309
|
+
|
|
310
|
+
**Example**:
|
|
311
|
+
```python
|
|
312
|
+
@injectable()
|
|
313
|
+
class Foo:
|
|
314
|
+
def __init__(self):
|
|
315
|
+
pass
|
|
316
|
+
|
|
317
|
+
@inject_environment()
|
|
318
|
+
def initEnvironment(self, env: Environment):
|
|
319
|
+
...
|
|
320
|
+
|
|
321
|
+
@inject()
|
|
322
|
+
def set(self, baz: Baz) -> None:
|
|
323
|
+
...
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Lifecycle methods
|
|
272
327
|
|
|
273
|
-
It is possible to
|
|
328
|
+
It is possible to mark specific lifecle methods.
|
|
274
329
|
- `@on_init()`
|
|
275
330
|
called after the constructor and all other injections.
|
|
276
331
|
- `@on_destroy()`
|
|
277
|
-
called
|
|
332
|
+
called during shutdown of the environment
|
|
278
333
|
|
|
279
|
-
|
|
334
|
+
## Post Processors
|
|
280
335
|
|
|
281
336
|
As part of the instantiation logic it is possible to define post processors that execute any side effect on newly created instances.
|
|
282
337
|
|
|
@@ -343,17 +398,17 @@ class SampleAdvice:
|
|
|
343
398
|
pass
|
|
344
399
|
|
|
345
400
|
@before(methods().named("hello").of_type(Foo))
|
|
346
|
-
def
|
|
401
|
+
def call_before(self, invocation: Invocation):
|
|
347
402
|
# arguments: invocation.args
|
|
348
403
|
print("before Foo.hello(...)")
|
|
349
404
|
|
|
350
405
|
@error(methods().named("hello").of_type(Foo))
|
|
351
|
-
def
|
|
406
|
+
def call_error(self, invocation: Invocation):
|
|
352
407
|
print("error Foo.hello(...)")
|
|
353
408
|
print(invocation.exception)
|
|
354
409
|
|
|
355
410
|
@around(methods().named("hello"))
|
|
356
|
-
def
|
|
411
|
+
def call_around(self, invocation: Invocation):
|
|
357
412
|
print("around Foo.hello()")
|
|
358
413
|
|
|
359
414
|
return invocation.proceed() # will leave a result in invocation.result or invocation.exception in case of an exception
|
|
@@ -374,7 +429,7 @@ All methods are expected to hava single `Invocation` parameter, that stores, the
|
|
|
374
429
|
It is essential for `around` methods to call `proceed()` on the invocation, which will call the next around method in the chain and finally the original method.
|
|
375
430
|
If the `proceed` is called with parameters, they will replace the original parameters!
|
|
376
431
|
|
|
377
|
-
The
|
|
432
|
+
The argument list to the corresponding decorators control, how aspects are associated with which methods.
|
|
378
433
|
A fluent interface is used describe the mapping.
|
|
379
434
|
The parameters restrict either methods or classes and are constructed by a call to either `methods()` or `classes()`.
|
|
380
435
|
|
|
@@ -388,7 +443,20 @@ Both add the fluent methods:
|
|
|
388
443
|
- `decorated_with(type: Type)`
|
|
389
444
|
defines decorators on methods or classes
|
|
390
445
|
|
|
391
|
-
The fluent methods `named`, `matches` and `of_type` can be called multiple
|
|
446
|
+
The fluent methods `named`, `matches` and `of_type` can be called multiple times!
|
|
447
|
+
|
|
448
|
+
**Example**:
|
|
449
|
+
|
|
450
|
+
```python
|
|
451
|
+
@injectable()
|
|
452
|
+
class TransactionAdvice:
|
|
453
|
+
def __init__(self):
|
|
454
|
+
pass
|
|
455
|
+
|
|
456
|
+
@around(methods().decorated_with(transactional), classes().decorated_with(transactional))
|
|
457
|
+
def establish_transaction(self, invocation: Invocation):
|
|
458
|
+
...
|
|
459
|
+
```
|
|
392
460
|
|
|
393
461
|
# Configuration
|
|
394
462
|
|
|
@@ -409,10 +477,11 @@ This concept relies on a central object `ConfigurationManager` that stores the o
|
|
|
409
477
|
|
|
410
478
|
```python
|
|
411
479
|
class ConfigurationSource(ABC):
|
|
412
|
-
def __init__(self
|
|
413
|
-
manager._register(self)
|
|
480
|
+
def __init__(self):
|
|
414
481
|
pass
|
|
415
482
|
|
|
483
|
+
...
|
|
484
|
+
|
|
416
485
|
@abstractmethod
|
|
417
486
|
def load(self) -> dict:
|
|
418
487
|
pass
|
|
@@ -424,14 +493,12 @@ As a default environment variables are already supported.
|
|
|
424
493
|
|
|
425
494
|
Other sources can be added dynamically by just registering them.
|
|
426
495
|
|
|
496
|
+
**Example**:
|
|
427
497
|
```python
|
|
428
498
|
@injectable()
|
|
429
499
|
class SampleConfigurationSource(ConfigurationSource):
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
def __init__(self, manager: ConfigurationManager):
|
|
433
|
-
super().__init__(manager)
|
|
434
|
-
|
|
500
|
+
def __init__(self):
|
|
501
|
+
super().__init__()
|
|
435
502
|
|
|
436
503
|
def load(self) -> dict:
|
|
437
504
|
return {
|
|
@@ -444,6 +511,43 @@ class SampleConfigurationSource(ConfigurationSource):
|
|
|
444
511
|
}
|
|
445
512
|
```
|
|
446
513
|
|
|
514
|
+
# Reflection
|
|
515
|
+
|
|
516
|
+
As the library heavily relies on type introspection of classes and methods, a utility class `TypeDescriptor` is available that covers type information on classes.
|
|
517
|
+
|
|
518
|
+
After beeing instatiated with
|
|
519
|
+
|
|
520
|
+
```python
|
|
521
|
+
TypeDescriptor.for_type(<type>)
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
it offers the methods
|
|
525
|
+
- `get_methods(local=False)`
|
|
526
|
+
- `get_method(name: str, local=False)`
|
|
527
|
+
- `has_decorator(decorator) -> bool`
|
|
528
|
+
- `get_decorator(decorator)`
|
|
529
|
+
|
|
530
|
+
The returned method descriptors offer:
|
|
531
|
+
- `param_types`
|
|
532
|
+
- `return_type`
|
|
533
|
+
- `has_decorator(decorator)`
|
|
534
|
+
- `get_decorator(decorator)`
|
|
535
|
+
|
|
536
|
+
The management of decorators in turn relies on another utility class `Decorators` that caches decorators.
|
|
537
|
+
|
|
538
|
+
Whenver you define a custom decorator, you will need to register it accordingly.
|
|
539
|
+
|
|
540
|
+
**Example**:
|
|
541
|
+
```python
|
|
542
|
+
def transactional():
|
|
543
|
+
def decorator(func):
|
|
544
|
+
Decorators.add(func, transactional)
|
|
545
|
+
return func
|
|
546
|
+
|
|
547
|
+
return decorator
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
|
|
447
551
|
|
|
448
552
|
|
|
449
553
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
aspyx/di/__init__.py,sha256=G13Cz1MMElBKNWsrhgTEIvhmIKj5MX4uqK_Apke4w8I,897
|
|
2
|
+
aspyx/di/di.py,sha256=elT3XJokxYxPJ9mx528MAU-pmm9lIZF8_AXKGkjyhBo,31014
|
|
3
|
+
aspyx/di/aop/__init__.py,sha256=nOABex49zSyMZ2w1ezwX3Q3yrOcQRSDjDtSj0DwKVbQ,233
|
|
4
|
+
aspyx/di/aop/aop.py,sha256=1RUgijk8RsiXWTizfNQX1IfHF7b96kCPdUgahX_J4Io,14457
|
|
5
|
+
aspyx/di/configuration/__init__.py,sha256=Zw7h-OlbJD7LyJvzkgyF0EmVqra6oN_Pt0HuUdjTPTA,249
|
|
6
|
+
aspyx/di/configuration/configuration.py,sha256=dHYoM3jC43QeDVlT6-mGDj05Sz6-Nm91LNE2TfQT1UU,5740
|
|
7
|
+
aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
|
|
8
|
+
aspyx/reflection/proxy.py,sha256=zJ6Psd6zWfFABdrKOf4cULt3gibyqCRdcR6z8WKIkzE,1982
|
|
9
|
+
aspyx/reflection/reflection.py,sha256=NkBK94kjJ7rQpPsK4mzHzX5TLSlWRM3mbNVzkwOZo4o,4379
|
|
10
|
+
aspyx-1.0.1.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
11
|
+
aspyx-1.0.1.dist-info/METADATA,sha256=69OakKuzM1UK0YTgH-y6-473YWhylxQvt8v993WsqiU,16798
|
|
12
|
+
aspyx-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
+
aspyx-1.0.1.dist-info/top_level.txt,sha256=A_ZwhBY_ybIgjZlztd44eaOrWqkJAndiqjGlbJ3tR_I,6
|
|
14
|
+
aspyx-1.0.1.dist-info/RECORD,,
|
aspyx-0.1.0.dist-info/RECORD
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
aspyx/di/__init__.py,sha256=US6i6s94TIpk4eiRFKYm9JB0NnU9F_3RAoIoXa-OnTI,800
|
|
2
|
-
aspyx/di/di.py,sha256=6A_-x6NrPArcKddlntpDj41yNOF0Gvbte1wSosv9UCI,30035
|
|
3
|
-
aspyx/di/aop/__init__.py,sha256=2mv6o38HHoBzF_nx8rsrvuOlUlNgYITmNRcQJVT2Bk8,213
|
|
4
|
-
aspyx/di/aop/aop.py,sha256=y5kwSzWWzKsPQNP3RUhxiNA0q5o_T_eGDE5vScOiPTM,13798
|
|
5
|
-
aspyx/di/configuration/__init__.py,sha256=YgUk1bHVYq1EJ9W4D_CJ9ZM-3kje-vzPNMxBdUm0Vlg,211
|
|
6
|
-
aspyx/di/configuration/configuration.py,sha256=-9QgOnC9HyWk9d3N8UQCRYHNtk4qDXjxUBaHC3RvAYc,5685
|
|
7
|
-
aspyx/reflection/__init__.py,sha256=jZAjw5NDrtXENmlBsQ0u9MobcNkfEuT0Oey8IWNYh34,204
|
|
8
|
-
aspyx/reflection/proxy.py,sha256=Re643BJAgbQjmDXO5JAvj4mu3RTpe_HF3TcFQ1euArg,1910
|
|
9
|
-
aspyx/reflection/reflection.py,sha256=DS2J3CfxZ0BpdzGBOBU9c1H3R0XCTE34Zx5k2OWn8Iw,3925
|
|
10
|
-
aspyx-0.1.0.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
11
|
-
aspyx-0.1.0.dist-info/METADATA,sha256=Ch6Qb4hxI_vFRJ09nZgRKSiAoIhH8xERLT9a8qDUgqA,13906
|
|
12
|
-
aspyx-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
13
|
-
aspyx-0.1.0.dist-info/top_level.txt,sha256=A_ZwhBY_ybIgjZlztd44eaOrWqkJAndiqjGlbJ3tR_I,6
|
|
14
|
-
aspyx-0.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|