aspyx 1.6.0__tar.gz → 1.7.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.6.0 → aspyx-1.7.0}/PKG-INFO +24 -63
- {aspyx-1.6.0 → aspyx-1.7.0}/README.md +23 -62
- {aspyx-1.6.0 → aspyx-1.7.0}/pyproject.toml +1 -1
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/configuration/configuration.py +0 -3
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/configuration/env_configuration_source.py +0 -5
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/configuration/yaml_configuration_source.py +0 -2
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/di.py +15 -7
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/reflection/reflection.py +8 -1
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/util/__init__.py +3 -0
- aspyx-1.7.0/src/aspyx/util/copy_on_write_cache.py +37 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/di_import.py +2 -4
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/sub_import.py +2 -4
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_aop.py +1 -5
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_configuration.py +0 -14
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_di.py +3 -2
- {aspyx-1.6.0 → aspyx-1.7.0}/.gitignore +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/LICENSE +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/aop/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/aop/aop.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/configuration/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/threading/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/di/threading/synchronized.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/exception/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/exception/exception_manager.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/reflection/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/reflection/proxy.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/threading/__init__.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/threading/context_local.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/threading/thread_local.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/util/logger.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/util/serialization.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/src/aspyx/util/stringbuilder.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/config.yaml +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/config1.yaml +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_cycle.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_decorator.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_exception_manager.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_proxy.py +0 -0
- {aspyx-1.6.0 → aspyx-1.7.0}/tests/test_reflection.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: aspyx
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.7.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
|
|
@@ -117,9 +117,6 @@ from aspyx.di import injectable, on_init, on_destroy, module, Environment
|
|
|
117
117
|
|
|
118
118
|
@injectable()
|
|
119
119
|
class Foo:
|
|
120
|
-
def __init__(self):
|
|
121
|
-
pass
|
|
122
|
-
|
|
123
120
|
def hello(self, msg: str):
|
|
124
121
|
print(f"hello {msg}")
|
|
125
122
|
|
|
@@ -138,9 +135,7 @@ class Bar:
|
|
|
138
135
|
|
|
139
136
|
@module()
|
|
140
137
|
class SampleModule:
|
|
141
|
-
|
|
142
|
-
pass
|
|
143
|
-
|
|
138
|
+
pass
|
|
144
139
|
|
|
145
140
|
# create environment
|
|
146
141
|
|
|
@@ -148,7 +143,7 @@ environment = Environment(SampleModule)
|
|
|
148
143
|
|
|
149
144
|
# fetch an instance
|
|
150
145
|
|
|
151
|
-
bar =
|
|
146
|
+
bar = environment.get(Bar)
|
|
152
147
|
|
|
153
148
|
bar.foo.hello("world")
|
|
154
149
|
```
|
|
@@ -206,10 +201,7 @@ class Foo:
|
|
|
206
201
|
def __init__(self):
|
|
207
202
|
pass
|
|
208
203
|
```
|
|
209
|
-
|
|
210
|
-
All referenced types will be injected by the environment.
|
|
211
|
-
|
|
212
|
-
Only eligible types are allowed, of course!
|
|
204
|
+
If the class defines a constructor, all parameters - which are expected to be registered as well - will be injected automatically.
|
|
213
205
|
|
|
214
206
|
The decorator accepts the keyword arguments
|
|
215
207
|
- `eager : boolean`
|
|
@@ -235,9 +227,6 @@ Classes that implement the `Factory` base class and are annotated with `@factory
|
|
|
235
227
|
```python
|
|
236
228
|
@factory()
|
|
237
229
|
class TestFactory(Factory[Foo]):
|
|
238
|
-
def __init__(self):
|
|
239
|
-
pass
|
|
240
|
-
|
|
241
230
|
def create(self) -> Foo:
|
|
242
231
|
return Foo()
|
|
243
232
|
```
|
|
@@ -252,9 +241,6 @@ Any `injectable` can define methods decorated with `@create()`, that will create
|
|
|
252
241
|
```python
|
|
253
242
|
@injectable()
|
|
254
243
|
class Foo:
|
|
255
|
-
def __init__(self):
|
|
256
|
-
pass
|
|
257
|
-
|
|
258
244
|
@create(scope="request")
|
|
259
245
|
def create(self) -> Baz:
|
|
260
246
|
return Baz()
|
|
@@ -290,8 +276,7 @@ constructor type argument called `module`.
|
|
|
290
276
|
```python
|
|
291
277
|
@module()
|
|
292
278
|
class SampleModule:
|
|
293
|
-
|
|
294
|
-
pass
|
|
279
|
+
pass
|
|
295
280
|
```
|
|
296
281
|
|
|
297
282
|
A module is a regular injectable class decorated with `@module` that controls the discovery of injectable classes, by filtering classes according to their module location relative to this class.
|
|
@@ -311,13 +296,11 @@ By adding the parameter `features: list[str]`, it is possible to filter injectab
|
|
|
311
296
|
@injectable()
|
|
312
297
|
@conditional(requires_feature("dev"))
|
|
313
298
|
class DevOnly:
|
|
314
|
-
|
|
315
|
-
pass
|
|
299
|
+
pass
|
|
316
300
|
|
|
317
301
|
@module()
|
|
318
302
|
class SampleModule():
|
|
319
|
-
|
|
320
|
-
pass
|
|
303
|
+
pass
|
|
321
304
|
|
|
322
305
|
environment = Environment(SampleModule, features=["dev"])
|
|
323
306
|
```
|
|
@@ -329,8 +312,7 @@ By adding an `imports: list[Type]` parameter, specifying other module types, it
|
|
|
329
312
|
```python
|
|
330
313
|
@module()
|
|
331
314
|
class SampleModule(imports=[OtherModule]):
|
|
332
|
-
|
|
333
|
-
pass
|
|
315
|
+
pass
|
|
334
316
|
```
|
|
335
317
|
|
|
336
318
|
Another possibility is to add a parent environment as an `Environment` constructor parameter
|
|
@@ -392,11 +374,8 @@ Different decorators are implemented, that call methods with computed values
|
|
|
392
374
|
```python
|
|
393
375
|
@injectable()
|
|
394
376
|
class Foo:
|
|
395
|
-
def __init__(self):
|
|
396
|
-
pass
|
|
397
|
-
|
|
398
377
|
@inject_environment()
|
|
399
|
-
def
|
|
378
|
+
def set_environment(self, env: Environment):
|
|
400
379
|
...
|
|
401
380
|
|
|
402
381
|
@inject()
|
|
@@ -586,9 +565,6 @@ A handy decorator `@synchronized` in combination with the respective advice is i
|
|
|
586
565
|
```python
|
|
587
566
|
@injectable()
|
|
588
567
|
class Foo:
|
|
589
|
-
def __init__(self):
|
|
590
|
-
pass
|
|
591
|
-
|
|
592
568
|
@synchronized()
|
|
593
569
|
def execute_synchronized(self):
|
|
594
570
|
...
|
|
@@ -601,9 +577,6 @@ It is possible to inject configuration values, by decorating methods with `@inje
|
|
|
601
577
|
```python
|
|
602
578
|
@injectable()
|
|
603
579
|
class Foo:
|
|
604
|
-
def __init__(self):
|
|
605
|
-
pass
|
|
606
|
-
|
|
607
580
|
@inject_value("HOME")
|
|
608
581
|
def inject_home(self, os: str):
|
|
609
582
|
...
|
|
@@ -615,13 +588,13 @@ Configuration values are managed centrally using a `ConfigurationManager`, which
|
|
|
615
588
|
|
|
616
589
|
```python
|
|
617
590
|
class ConfigurationSource(ABC):
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
...
|
|
591
|
+
@inject()
|
|
592
|
+
def set_manager(self, manager: ConfigurationManager):
|
|
593
|
+
manager._register(self)
|
|
622
594
|
|
|
623
595
|
@abstractmethod
|
|
624
596
|
def load(self) -> dict:
|
|
597
|
+
pass
|
|
625
598
|
```
|
|
626
599
|
|
|
627
600
|
The `load` method is able to return a tree-like structure by returning a `dict`.
|
|
@@ -670,11 +643,6 @@ Typically you create the required configuration sources in an environment class,
|
|
|
670
643
|
```python
|
|
671
644
|
@module()
|
|
672
645
|
class SampleModule:
|
|
673
|
-
# constructor
|
|
674
|
-
|
|
675
|
-
def __init__(self):
|
|
676
|
-
pass
|
|
677
|
-
|
|
678
646
|
@create()
|
|
679
647
|
def create_env_source(self) -> EnvConfigurationSource:
|
|
680
648
|
return EnvConfigurationSource()
|
|
@@ -733,7 +701,7 @@ def transactional(scope):
|
|
|
733
701
|
The class `ExceptionManager` is used to collect dynamic handlers for specific exceptions and is able to dispatch to the concrete functions
|
|
734
702
|
given a specific exception.
|
|
735
703
|
|
|
736
|
-
The handlers are declared by annoting a class with `@exception_handler` and decorating specific methods with `@
|
|
704
|
+
The handlers are declared by annoting a class with `@exception_handler` and decorating specific methods with `@catch`
|
|
737
705
|
|
|
738
706
|
**Example**:
|
|
739
707
|
|
|
@@ -745,11 +713,6 @@ class DerivedException(Exception):
|
|
|
745
713
|
|
|
746
714
|
@module()
|
|
747
715
|
class SampleModule:
|
|
748
|
-
# constructor
|
|
749
|
-
|
|
750
|
-
def __init__(self):
|
|
751
|
-
pass
|
|
752
|
-
|
|
753
716
|
@create()
|
|
754
717
|
def create_exception_manager(self) -> ExceptionManager:
|
|
755
718
|
return ExceptionManager()
|
|
@@ -758,19 +721,16 @@ class SampleModule:
|
|
|
758
721
|
@injectable()
|
|
759
722
|
@exception_handler()
|
|
760
723
|
class TestExceptionHandler:
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
@handle()
|
|
765
|
-
def handle_derived_exception(self, exception: DerivedException):
|
|
724
|
+
@catch()
|
|
725
|
+
def catch_derived_exception(self, exception: DerivedException):
|
|
766
726
|
ExceptionManager.proceed()
|
|
767
727
|
|
|
768
|
-
@
|
|
769
|
-
def
|
|
728
|
+
@catch()
|
|
729
|
+
def catch_exception(self, exception: Exception):
|
|
770
730
|
pass
|
|
771
731
|
|
|
772
|
-
@
|
|
773
|
-
def
|
|
732
|
+
@catch()
|
|
733
|
+
def catch_base_exception(self, exception: BaseException):
|
|
774
734
|
pass
|
|
775
735
|
|
|
776
736
|
|
|
@@ -798,9 +758,6 @@ Together with a simple around advice we can now add exception handling to any me
|
|
|
798
758
|
```python
|
|
799
759
|
@injectable()
|
|
800
760
|
class Service:
|
|
801
|
-
def __init__(self):
|
|
802
|
-
pass
|
|
803
|
-
|
|
804
761
|
def throw(self):
|
|
805
762
|
raise DerivedException()
|
|
806
763
|
|
|
@@ -843,3 +800,7 @@ class ExceptionAdvice:
|
|
|
843
800
|
**1.4.1**
|
|
844
801
|
|
|
845
802
|
- mkdocs
|
|
803
|
+
|
|
804
|
+
**1.6.1**
|
|
805
|
+
|
|
806
|
+
- default constructors not requires anymore
|
|
@@ -83,9 +83,6 @@ from aspyx.di import injectable, on_init, on_destroy, module, Environment
|
|
|
83
83
|
|
|
84
84
|
@injectable()
|
|
85
85
|
class Foo:
|
|
86
|
-
def __init__(self):
|
|
87
|
-
pass
|
|
88
|
-
|
|
89
86
|
def hello(self, msg: str):
|
|
90
87
|
print(f"hello {msg}")
|
|
91
88
|
|
|
@@ -104,9 +101,7 @@ class Bar:
|
|
|
104
101
|
|
|
105
102
|
@module()
|
|
106
103
|
class SampleModule:
|
|
107
|
-
|
|
108
|
-
pass
|
|
109
|
-
|
|
104
|
+
pass
|
|
110
105
|
|
|
111
106
|
# create environment
|
|
112
107
|
|
|
@@ -114,7 +109,7 @@ environment = Environment(SampleModule)
|
|
|
114
109
|
|
|
115
110
|
# fetch an instance
|
|
116
111
|
|
|
117
|
-
bar =
|
|
112
|
+
bar = environment.get(Bar)
|
|
118
113
|
|
|
119
114
|
bar.foo.hello("world")
|
|
120
115
|
```
|
|
@@ -172,10 +167,7 @@ class Foo:
|
|
|
172
167
|
def __init__(self):
|
|
173
168
|
pass
|
|
174
169
|
```
|
|
175
|
-
|
|
176
|
-
All referenced types will be injected by the environment.
|
|
177
|
-
|
|
178
|
-
Only eligible types are allowed, of course!
|
|
170
|
+
If the class defines a constructor, all parameters - which are expected to be registered as well - will be injected automatically.
|
|
179
171
|
|
|
180
172
|
The decorator accepts the keyword arguments
|
|
181
173
|
- `eager : boolean`
|
|
@@ -201,9 +193,6 @@ Classes that implement the `Factory` base class and are annotated with `@factory
|
|
|
201
193
|
```python
|
|
202
194
|
@factory()
|
|
203
195
|
class TestFactory(Factory[Foo]):
|
|
204
|
-
def __init__(self):
|
|
205
|
-
pass
|
|
206
|
-
|
|
207
196
|
def create(self) -> Foo:
|
|
208
197
|
return Foo()
|
|
209
198
|
```
|
|
@@ -218,9 +207,6 @@ Any `injectable` can define methods decorated with `@create()`, that will create
|
|
|
218
207
|
```python
|
|
219
208
|
@injectable()
|
|
220
209
|
class Foo:
|
|
221
|
-
def __init__(self):
|
|
222
|
-
pass
|
|
223
|
-
|
|
224
210
|
@create(scope="request")
|
|
225
211
|
def create(self) -> Baz:
|
|
226
212
|
return Baz()
|
|
@@ -256,8 +242,7 @@ constructor type argument called `module`.
|
|
|
256
242
|
```python
|
|
257
243
|
@module()
|
|
258
244
|
class SampleModule:
|
|
259
|
-
|
|
260
|
-
pass
|
|
245
|
+
pass
|
|
261
246
|
```
|
|
262
247
|
|
|
263
248
|
A module is a regular injectable class decorated with `@module` that controls the discovery of injectable classes, by filtering classes according to their module location relative to this class.
|
|
@@ -277,13 +262,11 @@ By adding the parameter `features: list[str]`, it is possible to filter injectab
|
|
|
277
262
|
@injectable()
|
|
278
263
|
@conditional(requires_feature("dev"))
|
|
279
264
|
class DevOnly:
|
|
280
|
-
|
|
281
|
-
pass
|
|
265
|
+
pass
|
|
282
266
|
|
|
283
267
|
@module()
|
|
284
268
|
class SampleModule():
|
|
285
|
-
|
|
286
|
-
pass
|
|
269
|
+
pass
|
|
287
270
|
|
|
288
271
|
environment = Environment(SampleModule, features=["dev"])
|
|
289
272
|
```
|
|
@@ -295,8 +278,7 @@ By adding an `imports: list[Type]` parameter, specifying other module types, it
|
|
|
295
278
|
```python
|
|
296
279
|
@module()
|
|
297
280
|
class SampleModule(imports=[OtherModule]):
|
|
298
|
-
|
|
299
|
-
pass
|
|
281
|
+
pass
|
|
300
282
|
```
|
|
301
283
|
|
|
302
284
|
Another possibility is to add a parent environment as an `Environment` constructor parameter
|
|
@@ -358,11 +340,8 @@ Different decorators are implemented, that call methods with computed values
|
|
|
358
340
|
```python
|
|
359
341
|
@injectable()
|
|
360
342
|
class Foo:
|
|
361
|
-
def __init__(self):
|
|
362
|
-
pass
|
|
363
|
-
|
|
364
343
|
@inject_environment()
|
|
365
|
-
def
|
|
344
|
+
def set_environment(self, env: Environment):
|
|
366
345
|
...
|
|
367
346
|
|
|
368
347
|
@inject()
|
|
@@ -552,9 +531,6 @@ A handy decorator `@synchronized` in combination with the respective advice is i
|
|
|
552
531
|
```python
|
|
553
532
|
@injectable()
|
|
554
533
|
class Foo:
|
|
555
|
-
def __init__(self):
|
|
556
|
-
pass
|
|
557
|
-
|
|
558
534
|
@synchronized()
|
|
559
535
|
def execute_synchronized(self):
|
|
560
536
|
...
|
|
@@ -567,9 +543,6 @@ It is possible to inject configuration values, by decorating methods with `@inje
|
|
|
567
543
|
```python
|
|
568
544
|
@injectable()
|
|
569
545
|
class Foo:
|
|
570
|
-
def __init__(self):
|
|
571
|
-
pass
|
|
572
|
-
|
|
573
546
|
@inject_value("HOME")
|
|
574
547
|
def inject_home(self, os: str):
|
|
575
548
|
...
|
|
@@ -581,13 +554,13 @@ Configuration values are managed centrally using a `ConfigurationManager`, which
|
|
|
581
554
|
|
|
582
555
|
```python
|
|
583
556
|
class ConfigurationSource(ABC):
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
...
|
|
557
|
+
@inject()
|
|
558
|
+
def set_manager(self, manager: ConfigurationManager):
|
|
559
|
+
manager._register(self)
|
|
588
560
|
|
|
589
561
|
@abstractmethod
|
|
590
562
|
def load(self) -> dict:
|
|
563
|
+
pass
|
|
591
564
|
```
|
|
592
565
|
|
|
593
566
|
The `load` method is able to return a tree-like structure by returning a `dict`.
|
|
@@ -636,11 +609,6 @@ Typically you create the required configuration sources in an environment class,
|
|
|
636
609
|
```python
|
|
637
610
|
@module()
|
|
638
611
|
class SampleModule:
|
|
639
|
-
# constructor
|
|
640
|
-
|
|
641
|
-
def __init__(self):
|
|
642
|
-
pass
|
|
643
|
-
|
|
644
612
|
@create()
|
|
645
613
|
def create_env_source(self) -> EnvConfigurationSource:
|
|
646
614
|
return EnvConfigurationSource()
|
|
@@ -699,7 +667,7 @@ def transactional(scope):
|
|
|
699
667
|
The class `ExceptionManager` is used to collect dynamic handlers for specific exceptions and is able to dispatch to the concrete functions
|
|
700
668
|
given a specific exception.
|
|
701
669
|
|
|
702
|
-
The handlers are declared by annoting a class with `@exception_handler` and decorating specific methods with `@
|
|
670
|
+
The handlers are declared by annoting a class with `@exception_handler` and decorating specific methods with `@catch`
|
|
703
671
|
|
|
704
672
|
**Example**:
|
|
705
673
|
|
|
@@ -711,11 +679,6 @@ class DerivedException(Exception):
|
|
|
711
679
|
|
|
712
680
|
@module()
|
|
713
681
|
class SampleModule:
|
|
714
|
-
# constructor
|
|
715
|
-
|
|
716
|
-
def __init__(self):
|
|
717
|
-
pass
|
|
718
|
-
|
|
719
682
|
@create()
|
|
720
683
|
def create_exception_manager(self) -> ExceptionManager:
|
|
721
684
|
return ExceptionManager()
|
|
@@ -724,19 +687,16 @@ class SampleModule:
|
|
|
724
687
|
@injectable()
|
|
725
688
|
@exception_handler()
|
|
726
689
|
class TestExceptionHandler:
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
@handle()
|
|
731
|
-
def handle_derived_exception(self, exception: DerivedException):
|
|
690
|
+
@catch()
|
|
691
|
+
def catch_derived_exception(self, exception: DerivedException):
|
|
732
692
|
ExceptionManager.proceed()
|
|
733
693
|
|
|
734
|
-
@
|
|
735
|
-
def
|
|
694
|
+
@catch()
|
|
695
|
+
def catch_exception(self, exception: Exception):
|
|
736
696
|
pass
|
|
737
697
|
|
|
738
|
-
@
|
|
739
|
-
def
|
|
698
|
+
@catch()
|
|
699
|
+
def catch_base_exception(self, exception: BaseException):
|
|
740
700
|
pass
|
|
741
701
|
|
|
742
702
|
|
|
@@ -764,9 +724,6 @@ Together with a simple around advice we can now add exception handling to any me
|
|
|
764
724
|
```python
|
|
765
725
|
@injectable()
|
|
766
726
|
class Service:
|
|
767
|
-
def __init__(self):
|
|
768
|
-
pass
|
|
769
|
-
|
|
770
727
|
def throw(self):
|
|
771
728
|
raise DerivedException()
|
|
772
729
|
|
|
@@ -809,3 +766,7 @@ class ExceptionAdvice:
|
|
|
809
766
|
**1.4.1**
|
|
810
767
|
|
|
811
768
|
- mkdocs
|
|
769
|
+
|
|
770
|
+
**1.6.1**
|
|
771
|
+
|
|
772
|
+
- default constructors not requires anymore
|
|
@@ -8,6 +8,7 @@ import logging
|
|
|
8
8
|
import importlib
|
|
9
9
|
import pkgutil
|
|
10
10
|
import sys
|
|
11
|
+
import time
|
|
11
12
|
|
|
12
13
|
from abc import abstractmethod, ABC
|
|
13
14
|
from enum import Enum
|
|
@@ -405,12 +406,10 @@ class ClassInstanceProvider(InstanceProvider):
|
|
|
405
406
|
# check constructor
|
|
406
407
|
|
|
407
408
|
init = TypeDescriptor.for_type(self.type).get_method("__init__")
|
|
408
|
-
if init is None:
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
for param in init.param_types:
|
|
413
|
-
types.append(param)
|
|
409
|
+
if init is not None:
|
|
410
|
+
self.params = len(init.param_types)
|
|
411
|
+
for param in init.param_types:
|
|
412
|
+
types.append(param)
|
|
414
413
|
|
|
415
414
|
# check @inject
|
|
416
415
|
|
|
@@ -976,7 +975,7 @@ class Environment:
|
|
|
976
975
|
"""
|
|
977
976
|
|
|
978
977
|
def add_provider(type: Type, provider: AbstractInstanceProvider):
|
|
979
|
-
Environment.logger.
|
|
978
|
+
Environment.logger.info("\tadd provider %s for %s", provider, type)
|
|
980
979
|
|
|
981
980
|
self.providers[type] = provider
|
|
982
981
|
|
|
@@ -989,6 +988,8 @@ class Environment:
|
|
|
989
988
|
if self.parent is None and env is not Boot:
|
|
990
989
|
self.parent = Boot.get_environment() # inherit environment including its manged instances!
|
|
991
990
|
|
|
991
|
+
start = time.perf_counter()
|
|
992
|
+
|
|
992
993
|
self.features = features
|
|
993
994
|
self.providers: Dict[Type, AbstractInstanceProvider] = {}
|
|
994
995
|
self.instances = []
|
|
@@ -1138,6 +1139,13 @@ class Environment:
|
|
|
1138
1139
|
for instance in self.instances:
|
|
1139
1140
|
self.execute_processors(Lifecycle.ON_RUNNING, instance)
|
|
1140
1141
|
|
|
1142
|
+
# done
|
|
1143
|
+
|
|
1144
|
+
end = time.perf_counter()
|
|
1145
|
+
|
|
1146
|
+
Environment.logger.info("created environment for class %s in %s ms, created %s instances", env.__qualname__, 1000 * (end - start), len(self.instances))
|
|
1147
|
+
|
|
1148
|
+
|
|
1141
1149
|
def is_registered_type(self, type: Type) -> bool:
|
|
1142
1150
|
provider = self.providers.get(type, None)
|
|
1143
1151
|
return provider is not None and not isinstance(provider, AmbiguousProvider)
|
|
@@ -94,7 +94,12 @@ class TypeDescriptor:
|
|
|
94
94
|
"""
|
|
95
95
|
This class provides a way to introspect Python classes, their methods, decorators, and type hints.
|
|
96
96
|
"""
|
|
97
|
-
# inner
|
|
97
|
+
# inner classes
|
|
98
|
+
|
|
99
|
+
class ParameterDescriptor:
|
|
100
|
+
def __init__(self, name: str, type: Type):
|
|
101
|
+
self.name = name
|
|
102
|
+
self.type = type
|
|
98
103
|
|
|
99
104
|
class MethodDescriptor:
|
|
100
105
|
"""
|
|
@@ -107,12 +112,14 @@ class TypeDescriptor:
|
|
|
107
112
|
self.method = method
|
|
108
113
|
self.decorators: list[DecoratorDescriptor] = Decorators.get(method)
|
|
109
114
|
self.param_types : list[Type] = []
|
|
115
|
+
self.params: list[TypeDescriptor.ParameterDescriptor] = []
|
|
110
116
|
|
|
111
117
|
type_hints = get_type_hints(method)
|
|
112
118
|
sig = signature(method)
|
|
113
119
|
|
|
114
120
|
for name, _ in sig.parameters.items():
|
|
115
121
|
if name != 'self':
|
|
122
|
+
self.params.append(TypeDescriptor.ParameterDescriptor(name, type_hints.get(name)))
|
|
116
123
|
self.param_types.append(type_hints.get(name, object))
|
|
117
124
|
|
|
118
125
|
self.return_type = type_hints.get('return', None)
|
|
@@ -4,11 +4,14 @@ This module provides utility functions.
|
|
|
4
4
|
from .stringbuilder import StringBuilder
|
|
5
5
|
from .logger import Logger
|
|
6
6
|
from .serialization import TypeSerializer, TypeDeserializer, get_serializer, get_deserializer
|
|
7
|
+
from .copy_on_write_cache import CopyOnWriteCache
|
|
7
8
|
|
|
8
9
|
__all__ = [
|
|
9
10
|
"StringBuilder",
|
|
10
11
|
"Logger",
|
|
11
12
|
|
|
13
|
+
"CopyOnWriteCache",
|
|
14
|
+
|
|
12
15
|
"TypeSerializer",
|
|
13
16
|
"TypeDeserializer",
|
|
14
17
|
"get_serializer",
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import TypeVar, Generic, Optional, Callable
|
|
2
|
+
|
|
3
|
+
K = TypeVar("K")
|
|
4
|
+
V = TypeVar("V")
|
|
5
|
+
|
|
6
|
+
class CopyOnWriteCache(Generic[K, V]):
|
|
7
|
+
# constructor
|
|
8
|
+
|
|
9
|
+
def __init__(self, factory: Optional[Callable[[K], V]] = None) -> None:
|
|
10
|
+
self._cache: dict[K, V] = {}
|
|
11
|
+
self._factory = factory
|
|
12
|
+
|
|
13
|
+
# public
|
|
14
|
+
|
|
15
|
+
def get(self, key: K, factory: Optional[Callable[[K], V]] = None) -> Optional[V]:
|
|
16
|
+
value = self._cache.get(key, None)
|
|
17
|
+
if value is None:
|
|
18
|
+
if factory is not None:
|
|
19
|
+
value = factory(key)
|
|
20
|
+
self.put(key, value)
|
|
21
|
+
|
|
22
|
+
elif self._factory is not None:
|
|
23
|
+
value = self._factory(key)
|
|
24
|
+
self.put(key, value)
|
|
25
|
+
|
|
26
|
+
return value
|
|
27
|
+
|
|
28
|
+
def put(self, key: K, value: V) -> None:
|
|
29
|
+
new_cache = self._cache.copy()
|
|
30
|
+
new_cache[key] = value
|
|
31
|
+
self._cache = new_cache
|
|
32
|
+
|
|
33
|
+
def contains(self, key: K) -> bool:
|
|
34
|
+
return key in self._cache
|
|
35
|
+
|
|
36
|
+
def clear(self) -> None:
|
|
37
|
+
self._cache = {}
|
|
@@ -34,16 +34,12 @@ def transactional():
|
|
|
34
34
|
|
|
35
35
|
@module()
|
|
36
36
|
class SampleModule:
|
|
37
|
-
|
|
38
|
-
pass
|
|
37
|
+
pass
|
|
39
38
|
|
|
40
39
|
|
|
41
40
|
@injectable()
|
|
42
41
|
@transactional()
|
|
43
42
|
class Bar:
|
|
44
|
-
def __init__(self):
|
|
45
|
-
pass
|
|
46
|
-
|
|
47
43
|
async def say_async(self, message: str):
|
|
48
44
|
await asyncio.sleep(0.01)
|
|
49
45
|
|
|
@@ -14,11 +14,6 @@ from aspyx.di import injectable, Environment, module, create
|
|
|
14
14
|
|
|
15
15
|
@module()
|
|
16
16
|
class SampleModule:
|
|
17
|
-
# constructor
|
|
18
|
-
|
|
19
|
-
def __init__(self):
|
|
20
|
-
pass
|
|
21
|
-
|
|
22
17
|
@create()
|
|
23
18
|
def create_env_source(self) -> EnvConfigurationSource:
|
|
24
19
|
return EnvConfigurationSource()
|
|
@@ -30,10 +25,6 @@ class SampleModule:
|
|
|
30
25
|
|
|
31
26
|
@injectable()
|
|
32
27
|
class SampleConfigurationSource1(ConfigurationSource):
|
|
33
|
-
# constructor
|
|
34
|
-
|
|
35
|
-
def __init__(self):
|
|
36
|
-
super().__init__()
|
|
37
28
|
|
|
38
29
|
def load(self) -> dict:
|
|
39
30
|
return {
|
|
@@ -43,11 +34,6 @@ class SampleConfigurationSource1(ConfigurationSource):
|
|
|
43
34
|
|
|
44
35
|
@injectable()
|
|
45
36
|
class SampleConfigurationSource2(ConfigurationSource):
|
|
46
|
-
# constructor
|
|
47
|
-
|
|
48
|
-
def __init__(self):
|
|
49
|
-
super().__init__()
|
|
50
|
-
|
|
51
37
|
def load(self) -> dict:
|
|
52
38
|
return {
|
|
53
39
|
"b": {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|