aspyx 1.1.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 +10 -5
- aspyx/di/aop/aop.py +86 -4
- aspyx/di/configuration/__init__.py +4 -1
- aspyx/di/configuration/configuration.py +1 -51
- aspyx/di/configuration/env_configuration_source.py +55 -0
- aspyx/di/configuration/yaml_configuration_source.py +26 -0
- aspyx/di/di.py +339 -207
- aspyx/di/threading/__init__.py +11 -0
- aspyx/di/threading/synchronized.py +46 -0
- aspyx/reflection/reflection.py +33 -1
- {aspyx-1.1.0.dist-info → aspyx-1.3.0.dist-info}/METADATA +150 -29
- aspyx-1.3.0.dist-info/RECORD +20 -0
- aspyx-1.1.0.dist-info/RECORD +0 -16
- {aspyx-1.1.0.dist-info → aspyx-1.3.0.dist-info}/WHEEL +0 -0
- {aspyx-1.1.0.dist-info → aspyx-1.3.0.dist-info}/licenses/LICENSE +0 -0
- {aspyx-1.1.0.dist-info → aspyx-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Synchronized decorator and advice
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import threading
|
|
7
|
+
from weakref import WeakKeyDictionary
|
|
8
|
+
|
|
9
|
+
from aspyx.reflection import Decorators
|
|
10
|
+
from aspyx.di.aop import advice, around, methods, Invocation
|
|
11
|
+
|
|
12
|
+
def synchronized():
|
|
13
|
+
"""
|
|
14
|
+
decorate methods to synchronize them based on an instance related `RLock`
|
|
15
|
+
"""
|
|
16
|
+
def decorator(func):
|
|
17
|
+
Decorators.add(func, synchronized)
|
|
18
|
+
return func #
|
|
19
|
+
|
|
20
|
+
return decorator
|
|
21
|
+
|
|
22
|
+
@advice
|
|
23
|
+
class SynchronizeAdvice():
|
|
24
|
+
__slots__ = ("locks")
|
|
25
|
+
|
|
26
|
+
# constructor
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.locks = WeakKeyDictionary()
|
|
30
|
+
|
|
31
|
+
# internal
|
|
32
|
+
|
|
33
|
+
def get_lock(self, instance) -> threading.RLock:
|
|
34
|
+
lock = self.locks.get(instance, None)
|
|
35
|
+
if lock is None:
|
|
36
|
+
lock = threading.RLock()
|
|
37
|
+
self.locks[instance] = lock
|
|
38
|
+
|
|
39
|
+
return lock
|
|
40
|
+
|
|
41
|
+
# around
|
|
42
|
+
|
|
43
|
+
@around(methods().decorated_with(synchronized))
|
|
44
|
+
def synchronize(self, invocation: Invocation):
|
|
45
|
+
with self.get_lock(invocation.args[0]):
|
|
46
|
+
return invocation.proceed()
|
aspyx/reflection/reflection.py
CHANGED
|
@@ -17,7 +17,7 @@ class DecoratorDescriptor:
|
|
|
17
17
|
"args"
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
-
def __init__(self, decorator, *args):
|
|
20
|
+
def __init__(self, decorator: Callable, *args):
|
|
21
21
|
self.decorator = decorator
|
|
22
22
|
self.args = args
|
|
23
23
|
|
|
@@ -25,6 +25,9 @@ class DecoratorDescriptor:
|
|
|
25
25
|
return f"@{self.decorator.__name__}({','.join(self.args)})"
|
|
26
26
|
|
|
27
27
|
class Decorators:
|
|
28
|
+
"""
|
|
29
|
+
Utility class that caches decorators ( Python does not have a feature for this )
|
|
30
|
+
"""
|
|
28
31
|
@classmethod
|
|
29
32
|
def add(cls, func, decorator, *args):
|
|
30
33
|
decorators = getattr(func, '__decorators__', None)
|
|
@@ -38,9 +41,17 @@ class Decorators:
|
|
|
38
41
|
return getattr(func, '__decorators__', [])
|
|
39
42
|
|
|
40
43
|
class TypeDescriptor:
|
|
44
|
+
"""
|
|
45
|
+
This class provides a way to introspect Python classes, their methods, decorators, and type hints.
|
|
46
|
+
"""
|
|
41
47
|
# inner class
|
|
42
48
|
|
|
43
49
|
class MethodDescriptor:
|
|
50
|
+
"""
|
|
51
|
+
This class represents a method of a class, including its decorators, parameter types, and return type.
|
|
52
|
+
"""
|
|
53
|
+
# constructor
|
|
54
|
+
|
|
44
55
|
def __init__(self, cls, method: Callable):
|
|
45
56
|
self.clazz = cls
|
|
46
57
|
self.method = method
|
|
@@ -56,6 +67,11 @@ class TypeDescriptor:
|
|
|
56
67
|
|
|
57
68
|
self.return_type = type_hints.get('return', None)
|
|
58
69
|
|
|
70
|
+
# public
|
|
71
|
+
|
|
72
|
+
def is_async(self) -> bool:
|
|
73
|
+
return inspect.iscoroutinefunction(self.method)
|
|
74
|
+
|
|
59
75
|
def get_decorator(self, decorator: Callable) -> Optional[DecoratorDescriptor]:
|
|
60
76
|
for dec in self.decorators:
|
|
61
77
|
if dec.decorator is decorator:
|
|
@@ -82,6 +98,9 @@ class TypeDescriptor:
|
|
|
82
98
|
|
|
83
99
|
@classmethod
|
|
84
100
|
def for_type(cls, clazz: Type) -> TypeDescriptor:
|
|
101
|
+
"""
|
|
102
|
+
Returns a TypeDescriptor for the given class, using a cache to avoid redundant introspection.
|
|
103
|
+
"""
|
|
85
104
|
descriptor = cls._cache.get(clazz)
|
|
86
105
|
if descriptor is None:
|
|
87
106
|
with cls._lock:
|
|
@@ -126,6 +145,9 @@ class TypeDescriptor:
|
|
|
126
145
|
# public
|
|
127
146
|
|
|
128
147
|
def get_decorator(self, decorator: Callable) -> Optional[DecoratorDescriptor]:
|
|
148
|
+
"""
|
|
149
|
+
Returns the first decorator of the given type, or None if not found.
|
|
150
|
+
"""
|
|
129
151
|
for dec in self.decorators:
|
|
130
152
|
if dec.decorator is decorator:
|
|
131
153
|
return dec
|
|
@@ -133,6 +155,8 @@ class TypeDescriptor:
|
|
|
133
155
|
return None
|
|
134
156
|
|
|
135
157
|
def has_decorator(self, decorator: Callable) -> bool:
|
|
158
|
+
"""
|
|
159
|
+
Checks if the class has a decorator of the given type."""
|
|
136
160
|
for dec in self.decorators:
|
|
137
161
|
if dec.decorator is decorator:
|
|
138
162
|
return True
|
|
@@ -140,12 +164,20 @@ class TypeDescriptor:
|
|
|
140
164
|
return False
|
|
141
165
|
|
|
142
166
|
def get_methods(self, local = False) -> list[TypeDescriptor.MethodDescriptor]:
|
|
167
|
+
"""
|
|
168
|
+
Returns a list of MethodDescriptor objects for the class.
|
|
169
|
+
If local is True, only returns methods defined in the class itself, otherwise includes inherited methods.
|
|
170
|
+
"""
|
|
143
171
|
if local:
|
|
144
172
|
return list(self.local_methods.values())
|
|
145
173
|
else:
|
|
146
174
|
return list(self.methods.values())
|
|
147
175
|
|
|
148
176
|
def get_method(self, name: str, local = False) -> Optional[TypeDescriptor.MethodDescriptor]:
|
|
177
|
+
"""
|
|
178
|
+
Returns a MethodDescriptor for the method with the given name.
|
|
179
|
+
If local is True, only searches for methods defined in the class itself, otherwise includes inherited methods.
|
|
180
|
+
"""
|
|
149
181
|
if local:
|
|
150
182
|
return self.local_methods.get(name, None)
|
|
151
183
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aspyx
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
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
|
|
@@ -37,15 +37,18 @@ Dynamic: license-file
|
|
|
37
37
|

|
|
38
38
|

|
|
39
39
|

|
|
40
|
+
[](https://pypi.org/project/aspyx/)
|
|
40
41
|
|
|
41
42
|
## Table of Contents
|
|
42
43
|
|
|
43
|
-
- [
|
|
44
|
+
- [Motivation](#motivation)
|
|
45
|
+
- [Introduction](#introduction)
|
|
44
46
|
- [Installation](#installation)
|
|
45
47
|
- [Registration](#registration)
|
|
46
48
|
- [Class](#class)
|
|
47
49
|
- [Class Factory](#class-factory)
|
|
48
50
|
- [Method](#method)
|
|
51
|
+
- [Conditional](#conditional)
|
|
49
52
|
- [Environment](#environment)
|
|
50
53
|
- [Definition](#definition)
|
|
51
54
|
- [Retrieval](#retrieval)
|
|
@@ -59,22 +62,38 @@ Dynamic: license-file
|
|
|
59
62
|
- [Reflection](#reflection)
|
|
60
63
|
- [Version History](#version-history)
|
|
61
64
|
|
|
65
|
+
# Motivation
|
|
66
|
+
|
|
67
|
+
While working on some AI related topics in Python, i required a simple DI framework.
|
|
68
|
+
Looking at the existing solutions - there are quite a number of - i was not quite happy. Either the solutions
|
|
69
|
+
lacked functionality - starting with that usually aop and di are separate libraries -, that i am accustomed to from other languages, or the API was in my mind too clumsy and "technical".
|
|
70
|
+
|
|
71
|
+
So, why not develop one on my own...Having done that in other languages previously, the task was not that hard, and last but not least...it is fun :-)
|
|
72
|
+
|
|
62
73
|
# Introduction
|
|
63
74
|
|
|
64
75
|
Aspyx is a small python libary, that adds support for both dependency injection and aop.
|
|
65
76
|
|
|
66
|
-
The following features are supported
|
|
77
|
+
The following di features are supported
|
|
67
78
|
- constructor and setter injection
|
|
79
|
+
- possibility to define custom injections
|
|
68
80
|
- post processors
|
|
69
|
-
- factory classes and methods
|
|
81
|
+
- support for factory classes and methods
|
|
70
82
|
- support for eager construction
|
|
71
|
-
- support for singleton and
|
|
83
|
+
- support for scopes singleton, request and thread
|
|
72
84
|
- possibilty to add custom scopes
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
85
|
+
- conditional registration of classes and factories
|
|
86
|
+
- lifecycle events methods `on_init`, `on_destroy`
|
|
87
|
+
- bundling of injectable objects according to their module location including recursive imports and inheritance
|
|
88
|
+
- instatiation of - possibly multiple - container instances - so called environments - that manage the lifecylce of related objects
|
|
89
|
+
- filtering of classes by associating "features" sets to environment ( similar to spring profiles )
|
|
76
90
|
- hierarchical environments
|
|
77
91
|
|
|
92
|
+
With respect to aop:
|
|
93
|
+
- support for before, around, after and error aspects
|
|
94
|
+
- sync and async method support
|
|
95
|
+
- `synchronized` decorator that adds locking to methods
|
|
96
|
+
|
|
78
97
|
The library is thread-safe!
|
|
79
98
|
|
|
80
99
|
Let's look at a simple example
|
|
@@ -155,11 +174,11 @@ Let's look at the details
|
|
|
155
174
|
|
|
156
175
|
# Installation
|
|
157
176
|
|
|
158
|
-
|
|
177
|
+
Just install from PyPI with
|
|
159
178
|
|
|
160
|
-
|
|
179
|
+
`pip install aspyx`
|
|
161
180
|
|
|
162
|
-
|
|
181
|
+
The library is tested with all Python version >= 3.9
|
|
163
182
|
|
|
164
183
|
# Registration
|
|
165
184
|
|
|
@@ -192,9 +211,9 @@ The decorator accepts the keyword arguments
|
|
|
192
211
|
- `singleton`
|
|
193
212
|
objects are created once inside an environment and cached. This is the default.
|
|
194
213
|
- `request`
|
|
195
|
-
|
|
214
|
+
objects are created on every injection request
|
|
196
215
|
- `thread`
|
|
197
|
-
objects are
|
|
216
|
+
objects are created and cached with respect to the current thread.
|
|
198
217
|
|
|
199
218
|
Other scopes - e.g. session related scopes - can be defined dynamically. Please check the corresponding chapter.
|
|
200
219
|
|
|
@@ -233,6 +252,22 @@ class Foo:
|
|
|
233
252
|
|
|
234
253
|
The same arguments as in `@injectable` are possible.
|
|
235
254
|
|
|
255
|
+
## Conditional
|
|
256
|
+
|
|
257
|
+
All `@injectable` declarations can be supplemented with
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
@conditional(<condition>, ..., <condition>)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
decorators that act as filters in the context of an environment.
|
|
264
|
+
|
|
265
|
+
Valid conditions are created by:
|
|
266
|
+
- `requires_class(clazz: Type)`
|
|
267
|
+
the injectable is valid, if the specified class is registered as well.
|
|
268
|
+
- `requires_feature(feature: str)`
|
|
269
|
+
the injectable is valid, if the environment defines the specified feature.
|
|
270
|
+
|
|
236
271
|
# Environment
|
|
237
272
|
|
|
238
273
|
## Definition
|
|
@@ -251,6 +286,26 @@ environment = Environment(SampleEnvironment)
|
|
|
251
286
|
|
|
252
287
|
The default is that all eligible classes, that are implemented in the containing module or in any submodule will be managed.
|
|
253
288
|
|
|
289
|
+
By adding the parameter `features: list[str]`, it is possible to filter injectables by evaluating the corresponding `@conditional` decorators.
|
|
290
|
+
|
|
291
|
+
**Example**:
|
|
292
|
+
```python
|
|
293
|
+
|
|
294
|
+
@injectable()
|
|
295
|
+
@conditional(requires_feature("dev"))
|
|
296
|
+
class DevOnly:
|
|
297
|
+
def __init__(self):
|
|
298
|
+
pass
|
|
299
|
+
|
|
300
|
+
@environment()
|
|
301
|
+
class SampleEnvironmen()):
|
|
302
|
+
def __init__(self):
|
|
303
|
+
pass
|
|
304
|
+
|
|
305
|
+
environment = Environment(SampleEnvironment, features=["dev"])
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
|
|
254
309
|
By adding an `imports: list[Type]` parameter, specifying other environment types, it will register the appropriate classes recursively.
|
|
255
310
|
|
|
256
311
|
**Example**:
|
|
@@ -428,18 +483,35 @@ Different aspects - with the appropriate decorator - are possible:
|
|
|
428
483
|
- `before`
|
|
429
484
|
methods that will be executed _prior_ to the original method
|
|
430
485
|
- `around`
|
|
431
|
-
methods that will be executed _around_ to the original method giving it the possibility add side effects or even change the parameters.
|
|
486
|
+
methods that will be executed _around_ to the original method giving it the possibility to add side effects or even change the parameters.
|
|
432
487
|
- `after`
|
|
433
|
-
|
|
488
|
+
methods that will be executed _after_ to the original method
|
|
434
489
|
- `error`
|
|
435
|
-
methods that will be executed in case of a caught exception
|
|
490
|
+
methods that will be executed in case of a caught exception
|
|
491
|
+
|
|
492
|
+
All methods are expected to have single `Invocation` parameter, that stores
|
|
436
493
|
|
|
437
|
-
|
|
494
|
+
- `func` the target function
|
|
495
|
+
- `args` the suppliued args
|
|
496
|
+
- `kwargs` the keywords args
|
|
497
|
+
- `result` the result ( initially `None`)
|
|
498
|
+
- `exception` a possible caught excpetion ( initially `None`)
|
|
438
499
|
|
|
439
500
|
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.
|
|
440
501
|
If the `proceed` is called with parameters, they will replace the original parameters!
|
|
441
502
|
|
|
442
|
-
|
|
503
|
+
**Example**: Parameter modifications
|
|
504
|
+
|
|
505
|
+
```python
|
|
506
|
+
@around(methods().named("say"))
|
|
507
|
+
def call_around(self, invocation: Invocation):
|
|
508
|
+
args = [invocation.args[0],invocation.args[1] + "!"] # 0 is self!
|
|
509
|
+
|
|
510
|
+
return invocation.proceed(*args)
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
The argument list to the corresponding decorators control which methods are targeted by the advice.
|
|
514
|
+
|
|
443
515
|
A fluent interface is used describe the mapping.
|
|
444
516
|
The parameters restrict either methods or classes and are constructed by a call to either `methods()` or `classes()`.
|
|
445
517
|
|
|
@@ -448,6 +520,8 @@ Both add the fluent methods:
|
|
|
448
520
|
defines the matching classes
|
|
449
521
|
- `named(name: str)`
|
|
450
522
|
defines method or class names
|
|
523
|
+
- `that_are_async()`
|
|
524
|
+
defines async methods
|
|
451
525
|
- `matches(re: str)`
|
|
452
526
|
defines regular expressions for methods or classes
|
|
453
527
|
- `decorated_with(type: Type)`
|
|
@@ -455,7 +529,7 @@ Both add the fluent methods:
|
|
|
455
529
|
|
|
456
530
|
The fluent methods `named`, `matches` and `of_type` can be called multiple times!
|
|
457
531
|
|
|
458
|
-
**Example**:
|
|
532
|
+
**Example**: react on both `transactional` decorators on methods or classes
|
|
459
533
|
|
|
460
534
|
```python
|
|
461
535
|
@injectable()
|
|
@@ -468,6 +542,10 @@ class TransactionAdvice:
|
|
|
468
542
|
...
|
|
469
543
|
```
|
|
470
544
|
|
|
545
|
+
With respect to async methods, you need to make sure, to replace a `proceeed()` with a `await proceed_async()` to have the overall chain async!
|
|
546
|
+
|
|
547
|
+
A handy decorator `@synchronized` is implemented that automatically synchronizes methods with a `RLock` associated with the instance.
|
|
548
|
+
|
|
471
549
|
# Configuration
|
|
472
550
|
|
|
473
551
|
It is possible to inject configuration values, by decorating methods with `@inject-value(<name>)` given a configuration key.
|
|
@@ -478,11 +556,13 @@ class Foo:
|
|
|
478
556
|
def __init__(self):
|
|
479
557
|
pass
|
|
480
558
|
|
|
481
|
-
@
|
|
482
|
-
def
|
|
559
|
+
@inject_value("HOME")
|
|
560
|
+
def inject_home(self, os: str):
|
|
483
561
|
...
|
|
484
562
|
```
|
|
485
563
|
|
|
564
|
+
If required a coercion will be executed.
|
|
565
|
+
|
|
486
566
|
This concept relies on a central object `ConfigurationManager` that stores the overall configuration values as provided by so called configuration sources that are defined as follows.
|
|
487
567
|
|
|
488
568
|
```python
|
|
@@ -494,14 +574,24 @@ class ConfigurationSource(ABC):
|
|
|
494
574
|
|
|
495
575
|
@abstractmethod
|
|
496
576
|
def load(self) -> dict:
|
|
497
|
-
pass
|
|
498
577
|
```
|
|
499
578
|
|
|
500
579
|
The `load` method is able to return a tree-like structure by returning a `dict`.
|
|
501
580
|
|
|
502
|
-
|
|
581
|
+
Configuration variables are retrieved with the method
|
|
582
|
+
|
|
583
|
+
```python
|
|
584
|
+
def get(self, path: str, type: Type[T], default : Optional[T]=None) -> T:
|
|
585
|
+
```
|
|
503
586
|
|
|
504
|
-
|
|
587
|
+
- `path`
|
|
588
|
+
a '.' separated path
|
|
589
|
+
- `type`
|
|
590
|
+
the desired type
|
|
591
|
+
- `default`
|
|
592
|
+
a default, if no value is registered
|
|
593
|
+
|
|
594
|
+
Sources can be added dynamically by registering them.
|
|
505
595
|
|
|
506
596
|
**Example**:
|
|
507
597
|
```python
|
|
@@ -521,6 +611,31 @@ class SampleConfigurationSource(ConfigurationSource):
|
|
|
521
611
|
}
|
|
522
612
|
```
|
|
523
613
|
|
|
614
|
+
Two specific source are already implemented:
|
|
615
|
+
- `EnvConfigurationSource`
|
|
616
|
+
reads the os environment variables
|
|
617
|
+
- `YamlConfigurationSource`
|
|
618
|
+
reads a specific yaml file
|
|
619
|
+
|
|
620
|
+
Typically you create the required configuration sources in an environment class, e.g.
|
|
621
|
+
|
|
622
|
+
```python
|
|
623
|
+
@environment()
|
|
624
|
+
class SampleEnvironment:
|
|
625
|
+
# constructor
|
|
626
|
+
|
|
627
|
+
def __init__(self):
|
|
628
|
+
pass
|
|
629
|
+
|
|
630
|
+
@create()
|
|
631
|
+
def create_env_source(self) -> EnvConfigurationSource:
|
|
632
|
+
return EnvConfigurationSource()
|
|
633
|
+
|
|
634
|
+
@create()
|
|
635
|
+
def create_yaml_source(self) -> YamlConfigurationSource:
|
|
636
|
+
return YamlConfigurationSource("config.yaml")
|
|
637
|
+
```
|
|
638
|
+
|
|
524
639
|
# Reflection
|
|
525
640
|
|
|
526
641
|
As the library heavily relies on type introspection of classes and methods, a utility class `TypeDescriptor` is available that covers type information on classes.
|
|
@@ -537,7 +652,7 @@ it offers the methods
|
|
|
537
652
|
- `get_method(name: str, local=False)`
|
|
538
653
|
return a single either local or overall method
|
|
539
654
|
- `has_decorator(decorator: Callable) -> bool`
|
|
540
|
-
return `True`, if the class is decorated with the specified
|
|
655
|
+
return `True`, if the class is decorated with the specified decorator
|
|
541
656
|
- `get_decorator(decorator) -> Optional[DecoratorDescriptor]`
|
|
542
657
|
return a descriptor covering the decorator. In addition to the callable, it also stores the supplied args in the `args` property
|
|
543
658
|
|
|
@@ -545,9 +660,9 @@ The returned method descriptors offer:
|
|
|
545
660
|
- `param_types`
|
|
546
661
|
list of arg types
|
|
547
662
|
- `return_type`
|
|
548
|
-
the
|
|
663
|
+
the return type
|
|
549
664
|
- `has_decorator(decorator: Callable) -> bool`
|
|
550
|
-
return `True`, if the method is decorated with the specified
|
|
665
|
+
return `True`, if the method is decorated with the specified decorator
|
|
551
666
|
- `get_decorator(decorator: Callable) -> Optional[DecoratorDescriptor]`
|
|
552
667
|
return a descriptor covering the decorator. In addition to the callable, it also stores the supplied args in the `args` property
|
|
553
668
|
|
|
@@ -557,9 +672,9 @@ Whenver you define a custom decorator, you will need to register it accordingly.
|
|
|
557
672
|
|
|
558
673
|
**Example**:
|
|
559
674
|
```python
|
|
560
|
-
def transactional():
|
|
675
|
+
def transactional(scope):
|
|
561
676
|
def decorator(func):
|
|
562
|
-
Decorators.add(func, transactional)
|
|
677
|
+
Decorators.add(func, transactional, scope) # also add _all_ parameters in order to cache them
|
|
563
678
|
return func
|
|
564
679
|
|
|
565
680
|
return decorator
|
|
@@ -577,6 +692,12 @@ def transactional():
|
|
|
577
692
|
- added `@on_running()` callback
|
|
578
693
|
- added `thread` scope
|
|
579
694
|
|
|
695
|
+
**1.2.0**
|
|
696
|
+
|
|
697
|
+
- added `YamlConfigurationSource`
|
|
698
|
+
|
|
699
|
+
**1.2.1**
|
|
700
|
+
|
|
580
701
|
|
|
581
702
|
|
|
582
703
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
aspyx/di/__init__.py,sha256=OfETLGaquTbFHhhDRzzGtnSu-KkMj68aDdEaVU19KoI,1107
|
|
2
|
+
aspyx/di/di.py,sha256=OmvcEd2Arsyo0n9Vh40LM_-jLFdOg-bKwahcEINJ7GA,38363
|
|
3
|
+
aspyx/di/aop/__init__.py,sha256=nOABex49zSyMZ2w1ezwX3Q3yrOcQRSDjDtSj0DwKVbQ,233
|
|
4
|
+
aspyx/di/aop/aop.py,sha256=GPkNE6CgQZUPeMlDdmE5TiksNLSgPb2A8D2IXRZmJ_M,16887
|
|
5
|
+
aspyx/di/configuration/__init__.py,sha256=mweJ3tZX1YJfY1d4ra-i0TWEcF3EwXBpGbHrKg1Kc6E,380
|
|
6
|
+
aspyx/di/configuration/configuration.py,sha256=KfPjrlUhhmEOUxdJiXePt5RGxKc8JczkWqlEBjpWQTg,4362
|
|
7
|
+
aspyx/di/configuration/env_configuration_source.py,sha256=FXPvREzq2ZER6_GG5xdpx154TQQDxZVf7LW7cvaylAk,1446
|
|
8
|
+
aspyx/di/configuration/yaml_configuration_source.py,sha256=NDl3SeoLMNVlzHgfP-Ysvhco1tRew_zFnBL5gGy2WRk,550
|
|
9
|
+
aspyx/di/threading/__init__.py,sha256=qrWdaq7MewQ2UmZy4J0Dn6BhY-ahfiG3xsv-EHqoqSE,191
|
|
10
|
+
aspyx/di/threading/synchronized.py,sha256=awFm8T5_lPw0An-H4-jdTUumPmX348WiYw1x8xyz4qs,1053
|
|
11
|
+
aspyx/di/util/__init__.py,sha256=8H2yKkXu3nkRGeTerb8ialzKGfvzUx44XUWFUYcYuQM,125
|
|
12
|
+
aspyx/di/util/stringbuilder.py,sha256=JywkLxZfaQUUWjSB5wvqA6a6Cfs3sW1jbaZ1z4U0-CQ,540
|
|
13
|
+
aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
|
|
14
|
+
aspyx/reflection/proxy.py,sha256=zJ6Psd6zWfFABdrKOf4cULt3gibyqCRdcR6z8WKIkzE,1982
|
|
15
|
+
aspyx/reflection/reflection.py,sha256=0kQXQcyOCj29vy5Ls4dbAlvOKsY7FRyHndiygZzg7HY,5826
|
|
16
|
+
aspyx-1.3.0.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
17
|
+
aspyx-1.3.0.dist-info/METADATA,sha256=lxqBe-dsW1oe6HgBOfoRIUk_q-qFObZSNpO4e8Xi40U,21691
|
|
18
|
+
aspyx-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
aspyx-1.3.0.dist-info/top_level.txt,sha256=A_ZwhBY_ybIgjZlztd44eaOrWqkJAndiqjGlbJ3tR_I,6
|
|
20
|
+
aspyx-1.3.0.dist-info/RECORD,,
|
aspyx-1.1.0.dist-info/RECORD
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
aspyx/di/__init__.py,sha256=U2-4JnCmSO_IXCnN1jeig9nuWfJN__z9yRzl1WexQJk,927
|
|
2
|
-
aspyx/di/di.py,sha256=VVLOXhTKJer2cV0rNEOqMLyMigsNMEQ7xoc-6M7a8wU,34296
|
|
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=jJJSHRG2wOIvle23Xc8CQ4WM7lp_xCL8-ENVZO603uQ,5682
|
|
7
|
-
aspyx/di/util/__init__.py,sha256=8H2yKkXu3nkRGeTerb8ialzKGfvzUx44XUWFUYcYuQM,125
|
|
8
|
-
aspyx/di/util/stringbuilder.py,sha256=JywkLxZfaQUUWjSB5wvqA6a6Cfs3sW1jbaZ1z4U0-CQ,540
|
|
9
|
-
aspyx/reflection/__init__.py,sha256=r2sNJrfHDpuqaIYu4fTYsoo046gpgn4VTd7bsS3mQJY,282
|
|
10
|
-
aspyx/reflection/proxy.py,sha256=zJ6Psd6zWfFABdrKOf4cULt3gibyqCRdcR6z8WKIkzE,1982
|
|
11
|
-
aspyx/reflection/reflection.py,sha256=HfPFd_FehTGmgF6NA-IJ_aHePE6GXM1ag7K3h8YIXjI,4600
|
|
12
|
-
aspyx-1.1.0.dist-info/licenses/LICENSE,sha256=n4jfx_MNj7cBtPhhI7MCoB_K35cj1icP9yJ4Rh4vlvY,1070
|
|
13
|
-
aspyx-1.1.0.dist-info/METADATA,sha256=rdeICMYyQSJmSZDJZrsdw6l5ZVrQrxvAPS_z47ObM-Y,17989
|
|
14
|
-
aspyx-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
aspyx-1.1.0.dist-info/top_level.txt,sha256=A_ZwhBY_ybIgjZlztd44eaOrWqkJAndiqjGlbJ3tR_I,6
|
|
16
|
-
aspyx-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|