dependency-injection-pattern 0.0.0__tar.gz → 0.0.2__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.
Files changed (46) hide show
  1. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/LICENSE +1 -1
  2. {dependency_injection_pattern-0.0.0/src/dependency_injection_pattern.egg-info → dependency_injection_pattern-0.0.2}/PKG-INFO +5 -4
  3. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/README.md +2 -2
  4. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/pyproject.toml +2 -1
  5. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2/src/dependency_injection_pattern.egg-info}/PKG-INFO +5 -4
  6. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/dependency_injection_pattern.egg-info/SOURCES.txt +0 -6
  7. dependency_injection_pattern-0.0.2/src/dependency_injection_pattern.egg-info/requires.txt +6 -0
  8. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/container.py +15 -7
  9. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/dependency.py +1 -1
  10. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/factory.py +1 -1
  11. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/provider.py +1 -1
  12. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_container.py +66 -2
  13. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_examples.py +49 -1
  14. dependency_injection_pattern-0.0.0/src/dependency_injection_pattern.egg-info/requires.txt +0 -5
  15. dependency_injection_pattern-0.0.0/src/di/core/reflection/__init__.py +0 -126
  16. dependency_injection_pattern-0.0.0/src/di/core/reflection/parameter.py +0 -66
  17. dependency_injection_pattern-0.0.0/src/di/core/reflection/parameter_kind.py +0 -9
  18. dependency_injection_pattern-0.0.0/src/di/core/reflection/parameter_mapper.py +0 -55
  19. dependency_injection_pattern-0.0.0/src/di/core/reflection/signature.py +0 -45
  20. dependency_injection_pattern-0.0.0/src/di/core/reflection/undefined.py +0 -24
  21. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/setup.cfg +0 -0
  22. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/dependency_injection_pattern.egg-info/dependency_links.txt +0 -0
  23. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/dependency_injection_pattern.egg-info/top_level.txt +0 -0
  24. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/__init__.py +0 -0
  25. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/default_scope.py +0 -0
  26. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/add_exception.py +0 -0
  27. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/container_sealed_error.py +0 -0
  28. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/factory_provide_exception.py +0 -0
  29. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/implementation_exception.py +0 -0
  30. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/provide_exception.py +0 -0
  31. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/exceptions/seal_exception.py +0 -0
  32. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/scoped_factory.py +0 -0
  33. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/singleton_factory.py +0 -0
  34. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factories/transient_factory.py +0 -0
  35. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/factory.py +0 -0
  36. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/helpers.py +0 -0
  37. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/log.py +0 -0
  38. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/named_service.py +0 -0
  39. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/protocols/container.py +0 -0
  40. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/protocols/factory.py +0 -0
  41. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/protocols/provider.py +0 -0
  42. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/scope.py +0 -0
  43. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/core/service_request.py +0 -0
  44. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/src/di/exceptions.py +0 -0
  45. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_factory.py +0 -0
  46. {dependency_injection_pattern-0.0.0 → dependency_injection_pattern-0.0.2}/tests/test_provider.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2020 Dagrofa BI
3
+ Copyright (c) 2025 Anders Madsen
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dependency-injection-pattern
3
- Version: 0.0.0
3
+ Version: 0.0.2
4
4
  Summary: Python implementation of the Dependency Injection pattern.
5
5
  Author-email: Anders Madsen <anders.madsen@alphavue.com>
6
6
  License: MIT
@@ -25,7 +25,8 @@ Classifier: Typing :: Typed
25
25
  Requires-Python: >=3.10
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
- Requires-Dist: typing-utilities==0.0.2
28
+ Requires-Dist: typing-utilities<0.1,>=0.0.3
29
+ Requires-Dist: runtime-reflection-lite<0.1,>=0.0.0
29
30
  Provides-Extra: test
30
31
  Requires-Dist: pytest>=8.3; extra == "test"
31
32
  Requires-Dist: pytest-cov>=6.1; extra == "test"
@@ -99,6 +100,6 @@ app = provider.provide(Application)
99
100
  value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
100
101
  ```
101
102
 
102
- ## API
103
+ ## Full documentation
103
104
 
104
- [Go to API documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/api.md)
105
+ [Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
@@ -66,6 +66,6 @@ app = provider.provide(Application)
66
66
  value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
67
67
  ```
68
68
 
69
- ## API
69
+ ## Full documentation
70
70
 
71
- [Go to API documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/api.md)
71
+ [Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
@@ -27,7 +27,8 @@ classifiers = [
27
27
  "Typing :: Typed"
28
28
  ]
29
29
  dependencies = [
30
- "typing-utilities==0.0.2"
30
+ "typing-utilities >= 0.0.3, < 0.1",
31
+ "runtime-reflection-lite >= 0.0.0, < 0.1"
31
32
  ]
32
33
  requires-python = ">=3.10"
33
34
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dependency-injection-pattern
3
- Version: 0.0.0
3
+ Version: 0.0.2
4
4
  Summary: Python implementation of the Dependency Injection pattern.
5
5
  Author-email: Anders Madsen <anders.madsen@alphavue.com>
6
6
  License: MIT
@@ -25,7 +25,8 @@ Classifier: Typing :: Typed
25
25
  Requires-Python: >=3.10
26
26
  Description-Content-Type: text/markdown
27
27
  License-File: LICENSE
28
- Requires-Dist: typing-utilities==0.0.2
28
+ Requires-Dist: typing-utilities<0.1,>=0.0.3
29
+ Requires-Dist: runtime-reflection-lite<0.1,>=0.0.0
29
30
  Provides-Extra: test
30
31
  Requires-Dist: pytest>=8.3; extra == "test"
31
32
  Requires-Dist: pytest-cov>=6.1; extra == "test"
@@ -99,6 +100,6 @@ app = provider.provide(Application)
99
100
  value = app.get_value() # => "Service2 returned: Service1 returned: Some value"
100
101
  ```
101
102
 
102
- ## API
103
+ ## Full documentation
103
104
 
104
- [Go to API documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/api.md)
105
+ [Go to documentation](https://github.com/apmadsen/dependency-injection-pattern/blob/main/docs/documentation.md)
@@ -31,12 +31,6 @@ src/di/core/factories/transient_factory.py
31
31
  src/di/core/protocols/container.py
32
32
  src/di/core/protocols/factory.py
33
33
  src/di/core/protocols/provider.py
34
- src/di/core/reflection/__init__.py
35
- src/di/core/reflection/parameter.py
36
- src/di/core/reflection/parameter_kind.py
37
- src/di/core/reflection/parameter_mapper.py
38
- src/di/core/reflection/signature.py
39
- src/di/core/reflection/undefined.py
40
34
  tests/test_container.py
41
35
  tests/test_examples.py
42
36
  tests/test_factory.py
@@ -0,0 +1,6 @@
1
+ typing-utilities<0.1,>=0.0.3
2
+ runtime-reflection-lite<0.1,>=0.0.0
3
+
4
+ [test]
5
+ pytest>=8.3
6
+ pytest-cov>=6.1
@@ -2,9 +2,10 @@ from __future__ import annotations
2
2
  from typing import TypeVar, Callable, Any, cast, overload, TYPE_CHECKING
3
3
  from typingutils import is_type, isinstance_typing, get_optional_type, AnyFunction
4
4
  from types import FunctionType
5
+ from weakref import ref
6
+ from runtime.reflection.lite import reflect_function
5
7
 
6
8
  from di.core.log import LOG
7
- from di.core.reflection import reflect_function
8
9
  from di.core.scope import Scope
9
10
  from di.core.service_request import ServiceRequest
10
11
  from di.core.factories.singleton_factory import SingletonFactory
@@ -34,12 +35,12 @@ class Container:
34
35
  provider = container.provider() # container is sealed, and no more services can be added beyound this point
35
36
  provider.provide(Service) # => Service
36
37
  """
37
- __slots__ = ["__factories", "__named_factories", "__list_factories", "__default_scope", "__provider"]
38
+ __slots__ = ["__factories", "__named_factories", "__list_factories", "__default_scope", "__provider", "__weakref__"]
38
39
 
39
40
  def __init__(self):
40
41
  self.__factories: dict[type[Any], Factory[Any]] = {}
41
42
  self.__named_factories: dict[str, tuple[type[Any], Factory[Any]]] = {}
42
- self.__provider: Provider | None = None
43
+ self.__provider: ref[Provider] | None = None
43
44
  self.__default_scope = DefaultScope()
44
45
 
45
46
  @property
@@ -185,10 +186,11 @@ class Container:
185
186
  Returns:
186
187
  Provider -- The Provider instance
187
188
  """
189
+ from di.core.provider import Provider
188
190
  if self.__provider is None:
189
- from di.core.provider import Provider
190
- self.__provider = Provider(self)
191
- self.__add_factory(SingletonFactory, Provider, self.__provider)
191
+ provider = Provider(self)
192
+ self.__provider = ref(provider)
193
+ self.__add_factory(SingletonFactory, Provider, self.provider)
192
194
 
193
195
  factories = { **self.__factories, **{ service: factory for service, factory in self.__named_factories.values() } }
194
196
  for service, factory in factories.items():
@@ -204,7 +206,13 @@ class Container:
204
206
  elif isinstance(factories[dep.service], TransientFactory):
205
207
  raise SealException(f"Singleton service '{get_service_name(service)}' depends on transient service {get_service_name(dep.service)}' which is not permitted.")
206
208
 
207
- return self.__provider
209
+ return provider
210
+ elif provider :=self.__provider():
211
+ return provider
212
+ else:
213
+ provider = Provider(self)
214
+ self.__provider = ref(provider)
215
+ return provider
208
216
 
209
217
  def __add_factory_pre(self, factory_type: type[Factory[T]], *args: Any) -> tuple[str, type[Any]]:
210
218
  if self.__provider != None:
@@ -1,8 +1,8 @@
1
1
  from typing import Any, cast
2
2
  from typingutils import get_optional_type
3
+ from runtime.reflection.lite import Undefined
3
4
 
4
5
  from di.core.helpers import get_provided_service_from_annotation
5
- from di.core.reflection.undefined import Undefined
6
6
 
7
7
  class Dependency:
8
8
  __slots__ = [ "__name", "__service", "__factory", "__optional", "__default" ]
@@ -1,7 +1,7 @@
1
1
  from typing import TypeVar, Callable, Generic, Any, cast
2
2
  from typingutils import get_optional_type, issubclass_typing, isinstance_typing
3
+ from runtime.reflection.lite import Undefined, ParameterKind, reflect_function, get_constructor
3
4
 
4
- from di.core.reflection import Undefined, ParameterKind, reflect_function, get_constructor
5
5
  from di.core.scope import Scope
6
6
  from di.core.named_service import NamedService
7
7
  from di.core.service_request import ServiceRequest
@@ -14,7 +14,7 @@ class Provider:
14
14
  """
15
15
  The Provider is connected to the Container and provides the services defined.
16
16
  """
17
- __slots__ = ["__container"]
17
+ __slots__ = ["__container", "__weakref__"]
18
18
 
19
19
  def __init__(self, container: Container):
20
20
  self.__container = container
@@ -3,11 +3,13 @@
3
3
  from typing import cast, Sequence
4
4
  from pytest import raises as assert_raises
5
5
  from threading import Thread
6
+ from weakref import ref
7
+ from gc import get_referrers, collect
6
8
 
7
- from di import Container, Provider
9
+ from di import Container, Provider, Factory
8
10
  from di.exceptions import ImplementationException, AddException, SealException, ProvideException, ContainerSealedError
9
11
 
10
- from tests.classes import Service, Options1, DependentService, DependentServiceFail1, IService, Service2
12
+ from tests.classes import Service, Options1, DependentService, DependentServiceFail1, IService, Service2, Service5, DependentService5, Application
11
13
 
12
14
 
13
15
 
@@ -93,3 +95,65 @@ def test_collections():
93
95
  with assert_raises(ImplementationException):
94
96
  container.add_singleton(Sequence[IService], [ Service, Service2 ])
95
97
 
98
+ def test_lifetime():
99
+
100
+ container = Container()
101
+ container.add_transient(Service5)
102
+
103
+ provider = container.provider()
104
+ provider_id = id(provider)
105
+ svc = provider.provide(Service5)
106
+ provider = ref(provider)
107
+
108
+ collect()
109
+
110
+ assert not provider() # provider should have been collected
111
+
112
+ provider = container.provider()
113
+ assert id(provider) != provider_id # this provider be a new instance
114
+
115
+ provider = ref(provider)
116
+ container = ref(container)
117
+
118
+ collect()
119
+
120
+ # provider and container should have been collected
121
+ assert not provider()
122
+ assert not container()
123
+
124
+ ### cleanup
125
+ del container
126
+ del provider
127
+ del svc
128
+ collect()
129
+ ###
130
+
131
+ container = Container()
132
+ container.add_transient(Application)
133
+ container.add_transient(Service5)
134
+ container.add_transient(DependentService5)
135
+ provider = container.provider()
136
+
137
+ app = provider.provide(Application)
138
+ svc = app.service_factory()
139
+
140
+ container = ref(container)
141
+ provider = ref(provider)
142
+
143
+ collect()
144
+
145
+ # app has a live reference to Factory[Service5],
146
+ # so neither container or provider should be collected
147
+ assert container()
148
+ assert provider()
149
+
150
+ app = ref(app)
151
+
152
+ collect()
153
+
154
+ # app has been collected thus any references to container and provider
155
+ # has been removed and container and provider has therefore
156
+ # also been collected
157
+ assert not app()
158
+ assert not provider()
159
+ assert not container()
@@ -175,4 +175,52 @@ def test_example_7():
175
175
  service1, service2 = app.services # => Service1, Service2
176
176
 
177
177
  assert isinstance(service1, Service1)
178
- assert isinstance(service2, Service2)
178
+ assert isinstance(service2, Service2)
179
+
180
+
181
+ def test_example_8():
182
+ from di import Container, DefaultScope, Scope, Provider
183
+ from random import Random
184
+ from threading import Thread
185
+
186
+ class RandGen:
187
+ rnd: Random | None
188
+
189
+ def generate(self) -> int:
190
+ if self.rnd:
191
+ return self.rnd.randint(0, 100)
192
+ else:
193
+ return -1
194
+
195
+ def __enter__(self):
196
+ self.rnd = Random()
197
+ return self
198
+
199
+ def __exit__(self):
200
+ del self.rnd
201
+
202
+ container = Container()
203
+ container.add_scoped(RandGen)
204
+
205
+ scope = DefaultScope()
206
+ provider = container.provider()
207
+
208
+ generators: set[int] = set()
209
+ ints: list[int] = []
210
+
211
+ def fn(provider: Provider, scope: Scope):
212
+ for i in range(3):
213
+ gen = provider.provide(RandGen, scope)
214
+ generators.add(id(gen))
215
+ ints.append(gen.generate())
216
+
217
+ thread1 = Thread(target = fn, args = (provider, scope))
218
+ thread1.start()
219
+ thread2 = Thread(target = fn, args = (provider, scope))
220
+ thread2.start()
221
+ thread1.join()
222
+ thread2.join()
223
+
224
+ assert len(generators) == 2
225
+ assert len(ints) == 6
226
+
@@ -1,5 +0,0 @@
1
- typing-utilities==0.0.2
2
-
3
- [test]
4
- pytest>=8.3
5
- pytest-cov>=6.1
@@ -1,126 +0,0 @@
1
- from typing import Any, cast
2
- from typingutils import AnyFunction
3
- from types import FrameType, MethodType
4
- from sys import modules
5
- from inspect import Parameter as InspectParameter, FrameInfo, signature as get_signature, stack, unwrap
6
-
7
- from di.core.reflection.undefined import Undefined
8
- from di.core.reflection.signature import Signature
9
- from di.core.reflection.parameter import Parameter
10
- from di.core.reflection.parameter_kind import ParameterKind
11
- from di.core.reflection.parameter_mapper import ParameterMapper
12
-
13
-
14
- def resolve(
15
- annotation: str,
16
- globals: dict[str, Any] | None = None,
17
- builtins: dict[str, Any] | None = None,
18
- locals: dict[str, Any] | None = None
19
- ) -> Any:
20
- if globals: # pragma: no cover
21
- globals = { **globals , **(builtins or __builtins__)}
22
-
23
- try:
24
- result = eval(annotation, globals, locals)
25
- return result
26
- except:
27
- pass
28
-
29
- # fallback
30
- for frame in stack()[1:]: # pragma: no cover
31
- if frame.filename == __file__:
32
- continue
33
-
34
- globals = { **frame.frame.f_globals , **frame.frame.f_builtins}
35
- locals = frame.frame.f_locals
36
-
37
- try:
38
- return eval(annotation, globals, locals)
39
- except:
40
- pass
41
-
42
- raise Exception(f"Unable to resolve {annotation}") # pragma: no cover
43
-
44
-
45
-
46
-
47
- def get_frame(fn: Any, stack: list[FrameInfo], cls: Any | None) -> FrameType | None: # pragma: no cover
48
- module = None
49
-
50
- if hasattr(fn, "__module__"):
51
- module = modules[getattr(fn, "__module__")]
52
- elif cls and hasattr(cls, "__module__"):
53
- module = modules[getattr(cls, "__module__")]
54
-
55
- if module and hasattr(module, "__file__"):
56
- for frame in stack:
57
- if frame.filename == module.__file__:
58
- frame_locals = frame.frame.f_locals.values()
59
- if cls is not None and cls in frame_locals:
60
- return frame.frame
61
- elif fn in frame_locals:
62
- return frame.frame
63
-
64
-
65
- def reflect_function(fn: AnyFunction, cls: object | None = None) -> Signature:
66
- fn = unwrap(fn)
67
- sig = get_signature(fn)
68
-
69
- if hasattr(fn, "__self__") and ( self := getattr(fn, "__self__") ):
70
- cls = self
71
-
72
- parameters = list(sig.parameters.values())
73
- globals: dict[str, Any] | None = getattr(fn, "__globals__") if hasattr(fn, "__globals__") else None
74
- builtins: dict[str, Any] | None = getattr(fn, "__builtins__") if hasattr(fn, "__builtins__") else None
75
- locals: dict[str, Any] | None = None
76
-
77
- if frame := get_frame(fn, stack()[1:], cls): # pragma: no cover
78
- locals = frame.f_locals
79
-
80
- if parameters:
81
- first_param = parameters[0].name.lower()
82
-
83
- if isinstance(fn, MethodType) and first_param in ("self", "cls"):
84
- parameters = parameters[1:] # pragma: no cover
85
- elif first_param in ("self", "cls") and hasattr(fn, "__self__"):
86
- parameters = parameters[1:] # pragma: no cover
87
- elif first_param == "self":
88
- if hasattr(fn, "__call__") and ( call := getattr(fn, "__call__") ): # pragma: no cover
89
- if hasattr(call, "__text_signature__") and ( text_sig := getattr(call, "__text_signature__") ):
90
- if cast(str, text_sig).startswith("($self"):
91
- parameters = parameters[1:]
92
-
93
- for index, parameter in enumerate(parameters):
94
- changed = False
95
-
96
- if isinstance(parameter.annotation, str):
97
- parameter_type = resolve(parameter.annotation, globals, builtins, locals)
98
- parameter = parameter.replace(annotation = parameter_type)
99
- changed = True
100
-
101
- if changed:
102
- parameters[index] = parameter
103
-
104
- if sig.return_annotation is InspectParameter.empty:
105
- return_type = Undefined
106
- elif isinstance(sig.return_annotation, str):
107
- return_type = resolve(sig.return_annotation, globals, builtins, locals)
108
- else:
109
- return_type = sig.return_annotation
110
-
111
- return Signature(
112
- ParameterMapper(
113
- [
114
- Parameter(
115
- p.name,
116
- ParameterKind(p.kind),
117
- Undefined if p.annotation is InspectParameter.empty else p.annotation,
118
- Undefined if p.default is InspectParameter.empty else p.default
119
- ) for p in parameters
120
- ]
121
- ), return_type
122
- )
123
-
124
-
125
- def get_constructor(cls: type[Any]) -> Signature:
126
- return reflect_function(getattr(cls, "__init__"), cls)
@@ -1,66 +0,0 @@
1
- from typing import Any, cast
2
- from typingutils import get_type_name
3
- from inspect import Parameter as InspectParameter
4
-
5
- from di.core.reflection.undefined import Undefined
6
- from di.core.reflection.parameter_kind import ParameterKind
7
-
8
- class Parameter:
9
- __slots__ = ["__name", "__kind", "__doc", "__type", "__default"]
10
-
11
- def __init__(
12
- self,
13
- name: str,
14
- kind: ParameterKind,
15
- parameter_type: type[Any],
16
- default: Any
17
- ):
18
- self.__name = name
19
- self.__kind = kind
20
- self.__type = parameter_type
21
- self.__default = default
22
-
23
- @property
24
- def name(self) -> str:
25
- """The parameter name
26
- """
27
- return self.__name
28
-
29
- @property
30
- def kind(self) -> ParameterKind:
31
- """The parameter kind
32
- """
33
- return self.__kind
34
-
35
- @property
36
- def parameter_type(self) -> type[Any]:
37
- """The parameter type
38
- """
39
- return self.__type
40
-
41
- @property
42
- def default(self) -> Any:
43
- """The parameter default
44
- """
45
- return self.__default
46
-
47
- def __repr__(self) -> str:
48
- str_type = f": {get_type_name(cast(type, self.__type))}" if self.__type and self.__type != Undefined else ""
49
- str_default = f"={self.__default}" if self.__default else ""
50
- return f"{self.__name}{str_type}{str_default}"
51
-
52
- def __eq__(self, o: object) -> bool: # pragma: no cover
53
- if isinstance(o, Parameter):
54
- return (
55
- self.name == o.name
56
- and self.kind == o.kind
57
- and self.parameter_type == o.parameter_type
58
- )
59
- elif isinstance(o, InspectParameter):
60
- return (
61
- self.name == o.name
62
- and cast(int, self.kind) == o.kind
63
- and self.parameter_type == o.annotation
64
- )
65
-
66
- return False
@@ -1,9 +0,0 @@
1
- from enum import Enum
2
- from inspect import Parameter
3
-
4
- class ParameterKind(Enum):
5
- POSITIONAL = Parameter.POSITIONAL_ONLY
6
- KEYWORD = Parameter.KEYWORD_ONLY
7
- POSITIONAL_OR_KEYWORD = Parameter.POSITIONAL_OR_KEYWORD
8
- ARGS = Parameter.VAR_POSITIONAL
9
- KWARGS = Parameter.VAR_KEYWORD
@@ -1,55 +0,0 @@
1
- from typing import Iterator, Mapping, Iterable, Sequence
2
-
3
- from di.core.reflection.parameter import Parameter
4
-
5
- class ParameterMapper(Iterable[Parameter]):
6
- __slots__ = ["__parameters", "__index"]
7
-
8
- def __init__(self, parameters: Sequence[Parameter]):
9
- self.__parameters = list(parameters)
10
- self.__index: Mapping[str, int] | None = None
11
-
12
- def __get_index(self) -> Mapping[str, int]:
13
- if not self.__index:
14
- self.__index = {
15
- parameter.name: self.__parameters.index(parameter)
16
- for parameter in self.__parameters
17
- }
18
- return self.__index # pragma: no cover
19
-
20
- def __getitem__(self, index: int | str) -> Parameter: # pragma: no cover
21
- if isinstance(index, int):
22
- return self.__parameters[index]
23
- else:
24
- p_index = self.__get_index()
25
-
26
- if index in p_index:
27
- return self.__parameters[p_index[index]]
28
- else:
29
- raise KeyError(index)
30
-
31
- def __iter__(self) -> Iterator[Parameter]:
32
- return iter(self.__parameters)
33
-
34
- def __len__(self) -> int: # pragma: no cover
35
- return len(self.__parameters)
36
-
37
- def __delitem__(self, index: int | str) -> None: # pragma: no cover
38
- if isinstance(index, int):
39
- del self.__parameters[index]
40
- else:
41
- p_index = self.__get_index()
42
-
43
- if index in p_index:
44
- del self.__parameters[p_index[index]]
45
- else:
46
- raise KeyError(index)
47
-
48
- def __contains__(self, name: str) -> bool: # pragma: no cover
49
- p_index = self.__get_index()
50
- return name in p_index
51
-
52
- def __eq__(self, o: object) -> bool: # pragma: no cover
53
- if isinstance(o, ParameterMapper):
54
- return self.__parameters == o.__parameters
55
- return False
@@ -1,45 +0,0 @@
1
- from typing import Any
2
- from typingutils import get_type_name
3
-
4
- from di.core.reflection.parameter_mapper import ParameterMapper
5
-
6
- class Signature:
7
- __slots__ = ["__parameters", "__return_type"]
8
-
9
- def __init__(
10
- self,
11
- parameters: ParameterMapper,
12
- return_type: type[Any]
13
- ):
14
- self.__parameters = parameters
15
- self.__return_type = return_type
16
-
17
- @property
18
- def parameters(self) -> ParameterMapper:
19
- """The signature parameters
20
- """
21
- return self.__parameters
22
-
23
- @property
24
- def return_type(self) -> type[Any]:
25
- """The signature return type
26
- """
27
- return self.__return_type
28
-
29
-
30
-
31
- def __repr__(self) -> str:
32
- str_parameters = ", ".join([ str(p) for p in self.__parameters ]) if self.__parameters else ""
33
- str_return = f" -> {get_type_name(self.__return_type)}" if self.__return_type else ""
34
- return f"({str_parameters}){str_return}"
35
-
36
- def __eq__(self, o: object) -> bool: # pragma: no cover
37
- if self is o:
38
- return True
39
- elif isinstance(o, Signature):
40
- return (
41
- self.parameters == o.parameters
42
- and self.return_type == o.return_type
43
- )
44
- return False
45
-
@@ -1,24 +0,0 @@
1
- from inspect import Signature, Parameter
2
-
3
- class Meta(type): # pragma: no cover
4
-
5
- def __bool__(self):
6
- return False
7
-
8
- def __eq__(self, other: object) -> bool:
9
- return other is Undefined or other in [ Signature.empty, Parameter.empty, None ]
10
-
11
- def __repr__(cls):
12
- return "Undefined"
13
-
14
- def __str__(self) -> str:
15
- return "Undefined"
16
-
17
- def __hash__(self) -> int:
18
- return type.__hash__(self)
19
-
20
-
21
- class Undefined(metaclass=Meta):
22
- def __new__(cls):
23
- raise Exception("Undefined type is not supposed to be instantiated") # pragma: no cover
24
-