aspyx 1.0.1__tar.gz → 1.2.0__tar.gz
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-1.0.1/src/aspyx.egg-info → aspyx-1.2.0}/PKG-INFO +95 -27
- {aspyx-1.0.1 → aspyx-1.2.0}/README.md +94 -26
- {aspyx-1.0.1 → aspyx-1.2.0}/pyproject.toml +1 -1
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/di/__init__.py +7 -5
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/di/aop/aop.py +2 -2
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/di/configuration/__init__.py +4 -1
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/di/configuration/configuration.py +4 -54
- aspyx-1.2.0/src/aspyx/di/configuration/env_configuration_source.py +55 -0
- aspyx-1.2.0/src/aspyx/di/configuration/yaml_configuration_source.py +26 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/di/di.py +260 -94
- aspyx-1.2.0/src/aspyx/di/util/__init__.py +8 -0
- aspyx-1.2.0/src/aspyx/di/util/stringbuilder.py +31 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/reflection/reflection.py +40 -6
- {aspyx-1.0.1 → aspyx-1.2.0/src/aspyx.egg-info}/PKG-INFO +95 -27
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx.egg-info/SOURCES.txt +4 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/tests/test_aop.py +14 -12
- {aspyx-1.0.1 → aspyx-1.2.0}/tests/test_configuration.py +38 -7
- {aspyx-1.0.1 → aspyx-1.2.0}/tests/test_di.py +53 -33
- {aspyx-1.0.1 → aspyx-1.2.0}/tests/test_di_cycle.py +8 -6
- {aspyx-1.0.1 → aspyx-1.2.0}/tests/test_proxy.py +4 -1
- {aspyx-1.0.1 → aspyx-1.2.0}/tests/test_reflection.py +10 -14
- {aspyx-1.0.1 → aspyx-1.2.0}/LICENSE +0 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/setup.cfg +0 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/di/aop/__init__.py +0 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/reflection/__init__.py +0 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx/reflection/proxy.py +0 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx.egg-info/dependency_links.txt +0 -0
- {aspyx-1.0.1 → aspyx-1.2.0}/src/aspyx.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aspyx
|
|
3
|
-
Version: 1.0
|
|
3
|
+
Version: 1.2.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
|
|
@@ -34,8 +34,9 @@ Dynamic: license-file
|
|
|
34
34
|
|
|
35
35
|

|
|
36
36
|

|
|
37
|
-

|
|
38
38
|

|
|
39
|
+

|
|
39
40
|
|
|
40
41
|
## Table of Contents
|
|
41
42
|
|
|
@@ -56,18 +57,18 @@ Dynamic: license-file
|
|
|
56
57
|
- [AOP](#aop)
|
|
57
58
|
- [Configuration](#configuration)
|
|
58
59
|
- [Reflection](#reflection)
|
|
60
|
+
- [Version History](#version-history)
|
|
59
61
|
|
|
60
62
|
# Introduction
|
|
61
63
|
|
|
62
64
|
Aspyx is a small python libary, that adds support for both dependency injection and aop.
|
|
63
65
|
|
|
64
66
|
The following features are supported
|
|
65
|
-
- constructor injection
|
|
66
|
-
- method injection
|
|
67
|
+
- constructor and setter injection
|
|
67
68
|
- post processors
|
|
68
69
|
- factory classes and methods
|
|
69
70
|
- support for eager construction
|
|
70
|
-
- support for singleton and
|
|
71
|
+
- support for singleton and request scopes
|
|
71
72
|
- possibilty to add custom scopes
|
|
72
73
|
- lifecycle events methods
|
|
73
74
|
- bundling of injectable object sets by environment classes including recursive imports and inheritance
|
|
@@ -156,7 +157,7 @@ Let's look at the details
|
|
|
156
157
|
|
|
157
158
|
`pip install aspyx`
|
|
158
159
|
|
|
159
|
-
The library is tested with Python version > 3.
|
|
160
|
+
The library is tested with all Python version > 3.9
|
|
160
161
|
|
|
161
162
|
Ready to go...
|
|
162
163
|
|
|
@@ -185,8 +186,15 @@ The decorator accepts the keyword arguments
|
|
|
185
186
|
- `eager : boolean`
|
|
186
187
|
if `True`, the container will create the instances automatically while booting the environment. This is the default.
|
|
187
188
|
- `scope: str`
|
|
188
|
-
the name of a scope which will determine how often instances will be created.
|
|
189
|
-
|
|
189
|
+
the name of a - registered - scope which will determine how often instances will be created.
|
|
190
|
+
|
|
191
|
+
The following scopes are implemented out of the box:
|
|
192
|
+
- `singleton`
|
|
193
|
+
objects are created once inside an environment and cached. This is the default.
|
|
194
|
+
- `request`
|
|
195
|
+
objects are created on every injection request
|
|
196
|
+
- `thread`
|
|
197
|
+
objects are created and cached with respect to the current thread.
|
|
190
198
|
|
|
191
199
|
Other scopes - e.g. session related scopes - can be defined dynamically. Please check the corresponding chapter.
|
|
192
200
|
|
|
@@ -304,7 +312,7 @@ Different decorators are implemented, that call methods with computed values
|
|
|
304
312
|
the method is called with all resolved parameter types ( same as the constructor call)
|
|
305
313
|
- `@inject_environment`
|
|
306
314
|
the method is called with the creating environment as a single parameter
|
|
307
|
-
- `@
|
|
315
|
+
- `@inject_value()`
|
|
308
316
|
the method is called with a resolved configuration value. Check the corresponding chapter
|
|
309
317
|
|
|
310
318
|
**Example**:
|
|
@@ -325,9 +333,11 @@ class Foo:
|
|
|
325
333
|
|
|
326
334
|
## Lifecycle methods
|
|
327
335
|
|
|
328
|
-
It is possible to mark specific
|
|
336
|
+
It is possible to mark specific lifecyle methods.
|
|
329
337
|
- `@on_init()`
|
|
330
338
|
called after the constructor and all other injections.
|
|
339
|
+
- `@on_running()`
|
|
340
|
+
called an environment has initialized all eager objects.
|
|
331
341
|
- `@on_destroy()`
|
|
332
342
|
called during shutdown of the environment
|
|
333
343
|
|
|
@@ -445,7 +455,7 @@ Both add the fluent methods:
|
|
|
445
455
|
|
|
446
456
|
The fluent methods `named`, `matches` and `of_type` can be called multiple times!
|
|
447
457
|
|
|
448
|
-
**Example**:
|
|
458
|
+
**Example**: react on both `transactional` decorators on methods or classes
|
|
449
459
|
|
|
450
460
|
```python
|
|
451
461
|
@injectable()
|
|
@@ -460,7 +470,7 @@ class TransactionAdvice:
|
|
|
460
470
|
|
|
461
471
|
# Configuration
|
|
462
472
|
|
|
463
|
-
It is possible to inject configuration values, by decorating methods with `@value(<name>)` given a configuration key.
|
|
473
|
+
It is possible to inject configuration values, by decorating methods with `@inject-value(<name>)` given a configuration key.
|
|
464
474
|
|
|
465
475
|
```python
|
|
466
476
|
@injectable()
|
|
@@ -468,11 +478,13 @@ class Foo:
|
|
|
468
478
|
def __init__(self):
|
|
469
479
|
pass
|
|
470
480
|
|
|
471
|
-
@
|
|
472
|
-
def
|
|
481
|
+
@inject_value("HOME")
|
|
482
|
+
def inject_home(self, os: str):
|
|
473
483
|
...
|
|
474
484
|
```
|
|
475
485
|
|
|
486
|
+
If required a coercion will be executed.
|
|
487
|
+
|
|
476
488
|
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.
|
|
477
489
|
|
|
478
490
|
```python
|
|
@@ -484,14 +496,24 @@ class ConfigurationSource(ABC):
|
|
|
484
496
|
|
|
485
497
|
@abstractmethod
|
|
486
498
|
def load(self) -> dict:
|
|
487
|
-
pass
|
|
488
499
|
```
|
|
489
500
|
|
|
490
501
|
The `load` method is able to return a tree-like structure by returning a `dict`.
|
|
491
502
|
|
|
492
|
-
|
|
503
|
+
Configuration variables are retrieved with the method
|
|
504
|
+
|
|
505
|
+
```python
|
|
506
|
+
def get(self, path: str, type: Type[T], default : Optional[T]=None) -> T:
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
- `path`
|
|
510
|
+
a '.' separated path
|
|
511
|
+
- `type`
|
|
512
|
+
the desired type
|
|
513
|
+
- `default`
|
|
514
|
+
a default, if no value is registered
|
|
493
515
|
|
|
494
|
-
|
|
516
|
+
Sources can be added dynamically by registering them.
|
|
495
517
|
|
|
496
518
|
**Example**:
|
|
497
519
|
```python
|
|
@@ -511,6 +533,31 @@ class SampleConfigurationSource(ConfigurationSource):
|
|
|
511
533
|
}
|
|
512
534
|
```
|
|
513
535
|
|
|
536
|
+
Two specific source are already implemented:
|
|
537
|
+
- `EnvConfigurationSource`
|
|
538
|
+
reads the os environment variables
|
|
539
|
+
- `YamlConfigurationSource`
|
|
540
|
+
reads a specific yaml file
|
|
541
|
+
|
|
542
|
+
Typically you create the required configuration sources in an environment class, e.g.
|
|
543
|
+
|
|
544
|
+
```python
|
|
545
|
+
@environment()
|
|
546
|
+
class SampleEnvironment:
|
|
547
|
+
# constructor
|
|
548
|
+
|
|
549
|
+
def __init__(self):
|
|
550
|
+
pass
|
|
551
|
+
|
|
552
|
+
@create()
|
|
553
|
+
def create_env_source(self) -> EnvConfigurationSource:
|
|
554
|
+
return EnvConfigurationSource()
|
|
555
|
+
|
|
556
|
+
@create()
|
|
557
|
+
def create_yaml_source(self) -> YamlConfigurationSource:
|
|
558
|
+
return YamlConfigurationSource("config.yaml")
|
|
559
|
+
```
|
|
560
|
+
|
|
514
561
|
# Reflection
|
|
515
562
|
|
|
516
563
|
As the library heavily relies on type introspection of classes and methods, a utility class `TypeDescriptor` is available that covers type information on classes.
|
|
@@ -522,16 +569,24 @@ TypeDescriptor.for_type(<type>)
|
|
|
522
569
|
```
|
|
523
570
|
|
|
524
571
|
it offers the methods
|
|
525
|
-
- `get_methods(local=False)`
|
|
526
|
-
|
|
527
|
-
- `
|
|
528
|
-
|
|
572
|
+
- `get_methods(local=False)`
|
|
573
|
+
return a list of either local or overall methods
|
|
574
|
+
- `get_method(name: str, local=False)`
|
|
575
|
+
return a single either local or overall method
|
|
576
|
+
- `has_decorator(decorator: Callable) -> bool`
|
|
577
|
+
return `True`, if the class is decorated with the specified decorator
|
|
578
|
+
- `get_decorator(decorator) -> Optional[DecoratorDescriptor]`
|
|
579
|
+
return a descriptor covering the decorator. In addition to the callable, it also stores the supplied args in the `args` property
|
|
529
580
|
|
|
530
581
|
The returned method descriptors offer:
|
|
531
|
-
- `param_types`
|
|
532
|
-
|
|
533
|
-
- `
|
|
534
|
-
|
|
582
|
+
- `param_types`
|
|
583
|
+
list of arg types
|
|
584
|
+
- `return_type`
|
|
585
|
+
the return type
|
|
586
|
+
- `has_decorator(decorator: Callable) -> bool`
|
|
587
|
+
return `True`, if the method is decorated with the specified decorator
|
|
588
|
+
- `get_decorator(decorator: Callable) -> Optional[DecoratorDescriptor]`
|
|
589
|
+
return a descriptor covering the decorator. In addition to the callable, it also stores the supplied args in the `args` property
|
|
535
590
|
|
|
536
591
|
The management of decorators in turn relies on another utility class `Decorators` that caches decorators.
|
|
537
592
|
|
|
@@ -539,16 +594,29 @@ Whenver you define a custom decorator, you will need to register it accordingly.
|
|
|
539
594
|
|
|
540
595
|
**Example**:
|
|
541
596
|
```python
|
|
542
|
-
def transactional():
|
|
597
|
+
def transactional(scope):
|
|
543
598
|
def decorator(func):
|
|
544
|
-
Decorators.add(func, transactional)
|
|
599
|
+
Decorators.add(func, transactional, scope) # also add _all_ parameters in order to cache them
|
|
545
600
|
return func
|
|
546
601
|
|
|
547
602
|
return decorator
|
|
548
603
|
```
|
|
549
604
|
|
|
550
605
|
|
|
606
|
+
# Version History
|
|
607
|
+
|
|
608
|
+
**1.0.1**
|
|
609
|
+
|
|
610
|
+
- some internal refactorings
|
|
611
|
+
|
|
612
|
+
**1.1.0**
|
|
613
|
+
|
|
614
|
+
- added `@on_running()` callback
|
|
615
|
+
- added `thread` scope
|
|
616
|
+
|
|
617
|
+
**1.2.0**
|
|
551
618
|
|
|
619
|
+
- added `YamlConfigurationSource`
|
|
552
620
|
|
|
553
621
|
|
|
554
622
|
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|

|
|
5
|
-

|
|
6
6
|

|
|
7
|
+

|
|
7
8
|
|
|
8
9
|
## Table of Contents
|
|
9
10
|
|
|
@@ -24,18 +25,18 @@
|
|
|
24
25
|
- [AOP](#aop)
|
|
25
26
|
- [Configuration](#configuration)
|
|
26
27
|
- [Reflection](#reflection)
|
|
28
|
+
- [Version History](#version-history)
|
|
27
29
|
|
|
28
30
|
# Introduction
|
|
29
31
|
|
|
30
32
|
Aspyx is a small python libary, that adds support for both dependency injection and aop.
|
|
31
33
|
|
|
32
34
|
The following features are supported
|
|
33
|
-
- constructor injection
|
|
34
|
-
- method injection
|
|
35
|
+
- constructor and setter injection
|
|
35
36
|
- post processors
|
|
36
37
|
- factory classes and methods
|
|
37
38
|
- support for eager construction
|
|
38
|
-
- support for singleton and
|
|
39
|
+
- support for singleton and request scopes
|
|
39
40
|
- possibilty to add custom scopes
|
|
40
41
|
- lifecycle events methods
|
|
41
42
|
- bundling of injectable object sets by environment classes including recursive imports and inheritance
|
|
@@ -124,7 +125,7 @@ Let's look at the details
|
|
|
124
125
|
|
|
125
126
|
`pip install aspyx`
|
|
126
127
|
|
|
127
|
-
The library is tested with Python version > 3.
|
|
128
|
+
The library is tested with all Python version > 3.9
|
|
128
129
|
|
|
129
130
|
Ready to go...
|
|
130
131
|
|
|
@@ -153,8 +154,15 @@ The decorator accepts the keyword arguments
|
|
|
153
154
|
- `eager : boolean`
|
|
154
155
|
if `True`, the container will create the instances automatically while booting the environment. This is the default.
|
|
155
156
|
- `scope: str`
|
|
156
|
-
the name of a scope which will determine how often instances will be created.
|
|
157
|
-
|
|
157
|
+
the name of a - registered - scope which will determine how often instances will be created.
|
|
158
|
+
|
|
159
|
+
The following scopes are implemented out of the box:
|
|
160
|
+
- `singleton`
|
|
161
|
+
objects are created once inside an environment and cached. This is the default.
|
|
162
|
+
- `request`
|
|
163
|
+
objects are created on every injection request
|
|
164
|
+
- `thread`
|
|
165
|
+
objects are created and cached with respect to the current thread.
|
|
158
166
|
|
|
159
167
|
Other scopes - e.g. session related scopes - can be defined dynamically. Please check the corresponding chapter.
|
|
160
168
|
|
|
@@ -272,7 +280,7 @@ Different decorators are implemented, that call methods with computed values
|
|
|
272
280
|
the method is called with all resolved parameter types ( same as the constructor call)
|
|
273
281
|
- `@inject_environment`
|
|
274
282
|
the method is called with the creating environment as a single parameter
|
|
275
|
-
- `@
|
|
283
|
+
- `@inject_value()`
|
|
276
284
|
the method is called with a resolved configuration value. Check the corresponding chapter
|
|
277
285
|
|
|
278
286
|
**Example**:
|
|
@@ -293,9 +301,11 @@ class Foo:
|
|
|
293
301
|
|
|
294
302
|
## Lifecycle methods
|
|
295
303
|
|
|
296
|
-
It is possible to mark specific
|
|
304
|
+
It is possible to mark specific lifecyle methods.
|
|
297
305
|
- `@on_init()`
|
|
298
306
|
called after the constructor and all other injections.
|
|
307
|
+
- `@on_running()`
|
|
308
|
+
called an environment has initialized all eager objects.
|
|
299
309
|
- `@on_destroy()`
|
|
300
310
|
called during shutdown of the environment
|
|
301
311
|
|
|
@@ -413,7 +423,7 @@ Both add the fluent methods:
|
|
|
413
423
|
|
|
414
424
|
The fluent methods `named`, `matches` and `of_type` can be called multiple times!
|
|
415
425
|
|
|
416
|
-
**Example**:
|
|
426
|
+
**Example**: react on both `transactional` decorators on methods or classes
|
|
417
427
|
|
|
418
428
|
```python
|
|
419
429
|
@injectable()
|
|
@@ -428,7 +438,7 @@ class TransactionAdvice:
|
|
|
428
438
|
|
|
429
439
|
# Configuration
|
|
430
440
|
|
|
431
|
-
It is possible to inject configuration values, by decorating methods with `@value(<name>)` given a configuration key.
|
|
441
|
+
It is possible to inject configuration values, by decorating methods with `@inject-value(<name>)` given a configuration key.
|
|
432
442
|
|
|
433
443
|
```python
|
|
434
444
|
@injectable()
|
|
@@ -436,11 +446,13 @@ class Foo:
|
|
|
436
446
|
def __init__(self):
|
|
437
447
|
pass
|
|
438
448
|
|
|
439
|
-
@
|
|
440
|
-
def
|
|
449
|
+
@inject_value("HOME")
|
|
450
|
+
def inject_home(self, os: str):
|
|
441
451
|
...
|
|
442
452
|
```
|
|
443
453
|
|
|
454
|
+
If required a coercion will be executed.
|
|
455
|
+
|
|
444
456
|
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.
|
|
445
457
|
|
|
446
458
|
```python
|
|
@@ -452,14 +464,24 @@ class ConfigurationSource(ABC):
|
|
|
452
464
|
|
|
453
465
|
@abstractmethod
|
|
454
466
|
def load(self) -> dict:
|
|
455
|
-
pass
|
|
456
467
|
```
|
|
457
468
|
|
|
458
469
|
The `load` method is able to return a tree-like structure by returning a `dict`.
|
|
459
470
|
|
|
460
|
-
|
|
471
|
+
Configuration variables are retrieved with the method
|
|
472
|
+
|
|
473
|
+
```python
|
|
474
|
+
def get(self, path: str, type: Type[T], default : Optional[T]=None) -> T:
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
- `path`
|
|
478
|
+
a '.' separated path
|
|
479
|
+
- `type`
|
|
480
|
+
the desired type
|
|
481
|
+
- `default`
|
|
482
|
+
a default, if no value is registered
|
|
461
483
|
|
|
462
|
-
|
|
484
|
+
Sources can be added dynamically by registering them.
|
|
463
485
|
|
|
464
486
|
**Example**:
|
|
465
487
|
```python
|
|
@@ -479,6 +501,31 @@ class SampleConfigurationSource(ConfigurationSource):
|
|
|
479
501
|
}
|
|
480
502
|
```
|
|
481
503
|
|
|
504
|
+
Two specific source are already implemented:
|
|
505
|
+
- `EnvConfigurationSource`
|
|
506
|
+
reads the os environment variables
|
|
507
|
+
- `YamlConfigurationSource`
|
|
508
|
+
reads a specific yaml file
|
|
509
|
+
|
|
510
|
+
Typically you create the required configuration sources in an environment class, e.g.
|
|
511
|
+
|
|
512
|
+
```python
|
|
513
|
+
@environment()
|
|
514
|
+
class SampleEnvironment:
|
|
515
|
+
# constructor
|
|
516
|
+
|
|
517
|
+
def __init__(self):
|
|
518
|
+
pass
|
|
519
|
+
|
|
520
|
+
@create()
|
|
521
|
+
def create_env_source(self) -> EnvConfigurationSource:
|
|
522
|
+
return EnvConfigurationSource()
|
|
523
|
+
|
|
524
|
+
@create()
|
|
525
|
+
def create_yaml_source(self) -> YamlConfigurationSource:
|
|
526
|
+
return YamlConfigurationSource("config.yaml")
|
|
527
|
+
```
|
|
528
|
+
|
|
482
529
|
# Reflection
|
|
483
530
|
|
|
484
531
|
As the library heavily relies on type introspection of classes and methods, a utility class `TypeDescriptor` is available that covers type information on classes.
|
|
@@ -490,16 +537,24 @@ TypeDescriptor.for_type(<type>)
|
|
|
490
537
|
```
|
|
491
538
|
|
|
492
539
|
it offers the methods
|
|
493
|
-
- `get_methods(local=False)`
|
|
494
|
-
|
|
495
|
-
- `
|
|
496
|
-
|
|
540
|
+
- `get_methods(local=False)`
|
|
541
|
+
return a list of either local or overall methods
|
|
542
|
+
- `get_method(name: str, local=False)`
|
|
543
|
+
return a single either local or overall method
|
|
544
|
+
- `has_decorator(decorator: Callable) -> bool`
|
|
545
|
+
return `True`, if the class is decorated with the specified decorator
|
|
546
|
+
- `get_decorator(decorator) -> Optional[DecoratorDescriptor]`
|
|
547
|
+
return a descriptor covering the decorator. In addition to the callable, it also stores the supplied args in the `args` property
|
|
497
548
|
|
|
498
549
|
The returned method descriptors offer:
|
|
499
|
-
- `param_types`
|
|
500
|
-
|
|
501
|
-
- `
|
|
502
|
-
|
|
550
|
+
- `param_types`
|
|
551
|
+
list of arg types
|
|
552
|
+
- `return_type`
|
|
553
|
+
the return type
|
|
554
|
+
- `has_decorator(decorator: Callable) -> bool`
|
|
555
|
+
return `True`, if the method is decorated with the specified decorator
|
|
556
|
+
- `get_decorator(decorator: Callable) -> Optional[DecoratorDescriptor]`
|
|
557
|
+
return a descriptor covering the decorator. In addition to the callable, it also stores the supplied args in the `args` property
|
|
503
558
|
|
|
504
559
|
The management of decorators in turn relies on another utility class `Decorators` that caches decorators.
|
|
505
560
|
|
|
@@ -507,16 +562,29 @@ Whenver you define a custom decorator, you will need to register it accordingly.
|
|
|
507
562
|
|
|
508
563
|
**Example**:
|
|
509
564
|
```python
|
|
510
|
-
def transactional():
|
|
565
|
+
def transactional(scope):
|
|
511
566
|
def decorator(func):
|
|
512
|
-
Decorators.add(func, transactional)
|
|
567
|
+
Decorators.add(func, transactional, scope) # also add _all_ parameters in order to cache them
|
|
513
568
|
return func
|
|
514
569
|
|
|
515
570
|
return decorator
|
|
516
571
|
```
|
|
517
572
|
|
|
518
573
|
|
|
574
|
+
# Version History
|
|
575
|
+
|
|
576
|
+
**1.0.1**
|
|
577
|
+
|
|
578
|
+
- some internal refactorings
|
|
579
|
+
|
|
580
|
+
**1.1.0**
|
|
581
|
+
|
|
582
|
+
- added `@on_running()` callback
|
|
583
|
+
- added `thread` scope
|
|
584
|
+
|
|
585
|
+
**1.2.0**
|
|
519
586
|
|
|
587
|
+
- added `YamlConfigurationSource`
|
|
520
588
|
|
|
521
589
|
|
|
522
590
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""
|
|
2
2
|
This module provides dependency injection and aop capabilities for Python applications.
|
|
3
3
|
"""
|
|
4
|
-
from .di import
|
|
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
|
|
5
5
|
|
|
6
6
|
# import something from the subpackages, so that teh decorators are executed
|
|
7
7
|
|
|
8
|
-
from
|
|
9
|
-
from
|
|
8
|
+
from .configuration import ConfigurationManager
|
|
9
|
+
from .aop import before
|
|
10
10
|
|
|
11
11
|
imports = [ConfigurationManager, before]
|
|
12
12
|
|
|
@@ -19,14 +19,16 @@ __all__ = [
|
|
|
19
19
|
"environment",
|
|
20
20
|
"inject",
|
|
21
21
|
"create",
|
|
22
|
+
"order",
|
|
22
23
|
|
|
23
24
|
"on_init",
|
|
25
|
+
"on_running",
|
|
24
26
|
"on_destroy",
|
|
25
27
|
"inject_environment",
|
|
26
28
|
"Factory",
|
|
27
29
|
"PostProcessor",
|
|
28
|
-
"
|
|
30
|
+
"AbstractCallableProcessor",
|
|
29
31
|
"LifecycleCallable",
|
|
30
|
-
"
|
|
32
|
+
"DIException",
|
|
31
33
|
"Lifecycle"
|
|
32
34
|
]
|
|
@@ -533,10 +533,10 @@ class AdviceProcessor(PostProcessor):
|
|
|
533
533
|
def process(self, instance: object, environment: Environment):
|
|
534
534
|
join_point_dict = self.advice.join_points4(instance, environment)
|
|
535
535
|
|
|
536
|
-
for member,
|
|
536
|
+
for member, joinPoints in join_point_dict.items():
|
|
537
537
|
Environment.logger.debug("add aspects for %s:%s", type(instance), member.__name__)
|
|
538
538
|
|
|
539
539
|
def wrap(jp):
|
|
540
540
|
return lambda *args, **kwargs: Invocation(member, jp).call(*args, **kwargs)
|
|
541
541
|
|
|
542
|
-
setattr(instance, member.__name__, types.MethodType(wrap(
|
|
542
|
+
setattr(instance, member.__name__, types.MethodType(wrap(joinPoints), instance))
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Configuration value handling
|
|
3
3
|
"""
|
|
4
|
-
from .configuration import ConfigurationManager, ConfigurationSource,
|
|
4
|
+
from .configuration import ConfigurationManager, ConfigurationSource, value
|
|
5
|
+
from .env_configuration_source import EnvConfigurationSource
|
|
6
|
+
from .yaml_configuration_source import YamlConfigurationSource
|
|
5
7
|
|
|
6
8
|
__all__ = [
|
|
7
9
|
"ConfigurationManager",
|
|
8
10
|
"ConfigurationSource",
|
|
9
11
|
"EnvConfigurationSource",
|
|
12
|
+
"YamlConfigurationSource",
|
|
10
13
|
"value"
|
|
11
14
|
]
|
|
@@ -4,11 +4,9 @@ Configuration handling module.
|
|
|
4
4
|
from __future__ import annotations
|
|
5
5
|
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
-
import os
|
|
8
7
|
from typing import Optional, Type, TypeVar
|
|
9
|
-
from dotenv import load_dotenv
|
|
10
8
|
|
|
11
|
-
from aspyx.di import injectable, Environment,
|
|
9
|
+
from aspyx.di import injectable, Environment, LifecycleCallable, Lifecycle
|
|
12
10
|
from aspyx.di.di import order, inject
|
|
13
11
|
from aspyx.reflection import Decorators, DecoratorDescriptor, TypeDescriptor
|
|
14
12
|
|
|
@@ -67,7 +65,7 @@ class ConfigurationManager:
|
|
|
67
65
|
|
|
68
66
|
def get(self, path: str, type: Type[T], default : Optional[T]=None) -> T:
|
|
69
67
|
"""
|
|
70
|
-
|
|
68
|
+
Retrieve a configuration value by path and type, with optional coercion.
|
|
71
69
|
Arguments:
|
|
72
70
|
path (str): The path to the configuration value, e.g. "database.host".
|
|
73
71
|
type (Type[T]): The expected type.
|
|
@@ -119,54 +117,6 @@ class ConfigurationSource(ABC):
|
|
|
119
117
|
return the configuration values of this source as a dictionary.
|
|
120
118
|
"""
|
|
121
119
|
|
|
122
|
-
@injectable()
|
|
123
|
-
class EnvConfigurationSource(ConfigurationSource):
|
|
124
|
-
"""
|
|
125
|
-
EnvConfigurationSource loads all environment variables.
|
|
126
|
-
"""
|
|
127
|
-
|
|
128
|
-
__slots__ = []
|
|
129
|
-
|
|
130
|
-
# constructor
|
|
131
|
-
|
|
132
|
-
def __init__(self):
|
|
133
|
-
super().__init__()
|
|
134
|
-
|
|
135
|
-
load_dotenv()
|
|
136
|
-
|
|
137
|
-
# implement
|
|
138
|
-
|
|
139
|
-
def load(self) -> dict:
|
|
140
|
-
def merge_dicts(a, b):
|
|
141
|
-
"""Recursively merges b into a"""
|
|
142
|
-
for key, value in b.items():
|
|
143
|
-
if isinstance(value, dict) and key in a and isinstance(a[key], dict):
|
|
144
|
-
merge_dicts(a[key], value)
|
|
145
|
-
else:
|
|
146
|
-
a[key] = value
|
|
147
|
-
return a
|
|
148
|
-
|
|
149
|
-
def explode_key(key, value):
|
|
150
|
-
"""Explodes keys with '.' or '/' into nested dictionaries"""
|
|
151
|
-
parts = key.replace('/', '.').split('.')
|
|
152
|
-
d = current = {}
|
|
153
|
-
for part in parts[:-1]:
|
|
154
|
-
current[part] = {}
|
|
155
|
-
current = current[part]
|
|
156
|
-
current[parts[-1]] = value
|
|
157
|
-
return d
|
|
158
|
-
|
|
159
|
-
exploded = {}
|
|
160
|
-
|
|
161
|
-
for key, value in os.environ.items():
|
|
162
|
-
if '.' in key or '/' in key:
|
|
163
|
-
partial = explode_key(key, value)
|
|
164
|
-
merge_dicts(exploded, partial)
|
|
165
|
-
else:
|
|
166
|
-
exploded[key] = value
|
|
167
|
-
|
|
168
|
-
return exploded
|
|
169
|
-
|
|
170
120
|
# decorator
|
|
171
121
|
|
|
172
122
|
def value(key: str, default=None):
|
|
@@ -188,8 +138,8 @@ def value(key: str, default=None):
|
|
|
188
138
|
@injectable()
|
|
189
139
|
@order(9)
|
|
190
140
|
class ConfigurationLifecycleCallable(LifecycleCallable):
|
|
191
|
-
def __init__(self,
|
|
192
|
-
super().__init__(value,
|
|
141
|
+
def __init__(self, manager: ConfigurationManager):
|
|
142
|
+
super().__init__(value, Lifecycle.ON_INJECT)
|
|
193
143
|
|
|
194
144
|
self.manager = manager
|
|
195
145
|
|