orionis 0.313.0__py3-none-any.whl → 0.314.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.
- orionis/container/container.py +433 -56
- orionis/metadata/framework.py +1 -1
- orionis/services/introspection/instances/reflection_instance.py +73 -5
- orionis/services/introspection/reflection.py +373 -23
- orionis/test/suite/test_unit.py +15 -10
- {orionis-0.313.0.dist-info → orionis-0.314.0.dist-info}/METADATA +1 -1
- {orionis-0.313.0.dist-info → orionis-0.314.0.dist-info}/RECORD +13 -12
- tests/services/inspection/test_reflection.py +462 -0
- tests/testing/test_testing_unit.py +1 -1
- {orionis-0.313.0.dist-info → orionis-0.314.0.dist-info}/WHEEL +0 -0
- {orionis-0.313.0.dist-info → orionis-0.314.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.313.0.dist-info → orionis-0.314.0.dist-info}/top_level.txt +0 -0
- {orionis-0.313.0.dist-info → orionis-0.314.0.dist-info}/zip-safe +0 -0
orionis/container/container.py
CHANGED
@@ -4,120 +4,497 @@ from orionis.container.exceptions.container_exception import OrionisContainerExc
|
|
4
4
|
from orionis.container.exceptions.type_error_exception import OrionisContainerTypeError
|
5
5
|
from orionis.services.introspection.abstract.reflection_abstract import ReflectionAbstract
|
6
6
|
from orionis.services.introspection.concretes.reflection_concrete import ReflectionConcrete
|
7
|
-
|
7
|
+
from orionis.services.introspection.instances.reflection_instance import ReflectionInstance
|
8
|
+
from orionis.services.introspection.reflection import Reflection
|
8
9
|
|
9
10
|
class Container:
|
10
11
|
|
11
|
-
def
|
12
|
+
def __init__(self):
|
13
|
+
self.__transient = {}
|
14
|
+
self.__singleton = {}
|
15
|
+
self.__scoped = {}
|
16
|
+
self.__instance = {}
|
17
|
+
|
18
|
+
def __ensureAliasType(self, value: Any) -> None:
|
12
19
|
"""
|
13
|
-
|
20
|
+
Ensures that the provided value is a valid alias of type str and does not contain invalid characters.
|
14
21
|
|
15
22
|
Parameters
|
16
23
|
----------
|
17
|
-
|
18
|
-
The
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
24
|
+
value : Any
|
25
|
+
The value to check.
|
26
|
+
|
27
|
+
Raises
|
28
|
+
------
|
29
|
+
OrionisContainerTypeError
|
30
|
+
If the value is not of type str or contains invalid characters.
|
31
|
+
|
32
|
+
Notes
|
33
|
+
-----
|
34
|
+
This method validates that a given value is a string and does not contain characters
|
35
|
+
that could cause errors when resolving dependencies (e.g., whitespace, special symbols).
|
23
36
|
"""
|
24
|
-
pass
|
25
37
|
|
26
|
-
|
38
|
+
# Check if the value is a string
|
39
|
+
if not isinstance(value, str):
|
40
|
+
raise OrionisContainerTypeError(
|
41
|
+
f"Expected a string type for alias, but got {type(value).__name__} instead."
|
42
|
+
)
|
43
|
+
|
44
|
+
# Define a set of invalid characters for aliases
|
45
|
+
invalid_chars = set(' \t\n\r\x0b\x0c!@#$%^&*()[]{};:,/<>?\\|`~"\'')
|
46
|
+
if any(char in invalid_chars for char in value):
|
47
|
+
raise OrionisContainerTypeError(
|
48
|
+
f"Alias '{value}' contains invalid characters. "
|
49
|
+
"Aliases must not contain whitespace or special symbols."
|
50
|
+
)
|
51
|
+
|
52
|
+
def __ensureAbstractClass(self, abstract: Callable[..., Any], lifetime: str) -> None:
|
27
53
|
"""
|
28
|
-
|
54
|
+
Ensures that the provided abstract is an abstract class.
|
29
55
|
|
30
56
|
Parameters
|
31
57
|
----------
|
32
58
|
abstract : Callable[..., Any]
|
33
|
-
The
|
34
|
-
|
35
|
-
The
|
59
|
+
The class intended to represent the abstract type.
|
60
|
+
lifetime : str
|
61
|
+
The service lifetime descriptor, used for error messages.
|
62
|
+
|
63
|
+
Raises
|
64
|
+
------
|
65
|
+
OrionisContainerTypeError
|
66
|
+
If the abstract class check fails.
|
36
67
|
"""
|
37
|
-
# Ensure that abstract is an abstract class and concrete is a concrete class
|
38
68
|
try:
|
39
69
|
ReflectionAbstract.ensureIsAbstractClass(abstract)
|
70
|
+
except Exception as e:
|
71
|
+
raise OrionisContainerTypeError(
|
72
|
+
f"Unexpected error registering {lifetime} service: {e}"
|
73
|
+
) from e
|
74
|
+
|
75
|
+
def __ensureConcreteClass(self, concrete: Callable[..., Any], lifetime: str) -> None:
|
76
|
+
"""
|
77
|
+
Ensures that the provided concrete is a concrete (non-abstract) class.
|
78
|
+
|
79
|
+
Parameters
|
80
|
+
----------
|
81
|
+
concrete : Callable[..., Any]
|
82
|
+
The class intended to represent the concrete implementation.
|
83
|
+
lifetime : str
|
84
|
+
The service lifetime descriptor, used for error messages.
|
85
|
+
|
86
|
+
Raises
|
87
|
+
------
|
88
|
+
OrionisContainerTypeError
|
89
|
+
If the concrete class check fails.
|
90
|
+
"""
|
91
|
+
try:
|
40
92
|
ReflectionConcrete.ensureIsConcreteClass(concrete)
|
41
93
|
except Exception as e:
|
42
94
|
raise OrionisContainerTypeError(
|
43
|
-
f"Unexpected error registering
|
95
|
+
f"Unexpected error registering {lifetime} service: {e}"
|
44
96
|
) from e
|
45
97
|
|
46
|
-
|
98
|
+
def __ensureIsSubclass(self, abstract: Callable[..., Any], concrete: Callable[..., Any]) -> None:
|
99
|
+
"""
|
100
|
+
Validates that the concrete class is a subclass of the provided abstract class.
|
101
|
+
|
102
|
+
Parameters
|
103
|
+
----------
|
104
|
+
abstract : Callable[..., Any]
|
105
|
+
The abstract base class or interface.
|
106
|
+
concrete : Callable[..., Any]
|
107
|
+
The concrete implementation class to check.
|
108
|
+
|
109
|
+
Raises
|
110
|
+
------
|
111
|
+
OrionisContainerException
|
112
|
+
If the concrete class is NOT a subclass of the abstract class.
|
113
|
+
|
114
|
+
Notes
|
115
|
+
-----
|
116
|
+
This method ensures that the concrete implementation inherits from the abstract class,
|
117
|
+
which is required for proper dependency injection and interface enforcement.
|
118
|
+
"""
|
119
|
+
if not issubclass(concrete, abstract):
|
120
|
+
raise OrionisContainerException(
|
121
|
+
"The concrete class must inherit from the provided abstract class. "
|
122
|
+
"Please ensure that the concrete class is a subclass of the specified abstract class."
|
123
|
+
)
|
124
|
+
|
125
|
+
def __ensureIsNotSubclass(self, abstract: Callable[..., Any], concrete: Callable[..., Any]) -> None:
|
126
|
+
"""
|
127
|
+
Validates that the concrete class is NOT a subclass of the provided abstract class.
|
128
|
+
|
129
|
+
Parameters
|
130
|
+
----------
|
131
|
+
abstract : Callable[..., Any]
|
132
|
+
The abstract base class or interface.
|
133
|
+
concrete : Callable[..., Any]
|
134
|
+
The concrete implementation class to check.
|
135
|
+
|
136
|
+
Raises
|
137
|
+
------
|
138
|
+
OrionisContainerException
|
139
|
+
If the concrete class IS a subclass of the abstract class.
|
140
|
+
|
141
|
+
Notes
|
142
|
+
-----
|
143
|
+
This method ensures that the concrete implementation does NOT inherit from the abstract class.
|
144
|
+
"""
|
47
145
|
if issubclass(concrete, abstract):
|
48
146
|
raise OrionisContainerException(
|
49
|
-
"
|
50
|
-
"Please ensure that the concrete class
|
147
|
+
"The concrete class must NOT inherit from the provided abstract class. "
|
148
|
+
"Please ensure that the concrete class is not a subclass of the specified abstract class."
|
149
|
+
)
|
150
|
+
|
151
|
+
def __ensureInstance(self, instance: Any) -> None:
|
152
|
+
"""
|
153
|
+
Ensures that the provided object is a valid instance.
|
154
|
+
|
155
|
+
Parameters
|
156
|
+
----------
|
157
|
+
instance : Any
|
158
|
+
The object to be validated as an instance.
|
159
|
+
|
160
|
+
Raises
|
161
|
+
------
|
162
|
+
OrionisContainerTypeError
|
163
|
+
If the provided object is not a valid instance.
|
164
|
+
|
165
|
+
Notes
|
166
|
+
-----
|
167
|
+
This method uses ReflectionInstance to verify that the given object
|
168
|
+
is a proper instance (not a class or abstract type). If the check fails,
|
169
|
+
an OrionisContainerTypeError is raised with a descriptive message.
|
170
|
+
"""
|
171
|
+
try:
|
172
|
+
ReflectionInstance.ensureIsInstance(instance)
|
173
|
+
except Exception as e:
|
174
|
+
raise OrionisContainerTypeError(
|
175
|
+
f"Error registering instance: {e}"
|
176
|
+
) from e
|
177
|
+
|
178
|
+
def __ensureImplementation(self, *, abstract: Callable[..., Any] = None, concrete: Callable[..., Any] = None, instance: Any = None) -> None:
|
179
|
+
"""
|
180
|
+
Ensures that a concrete class or instance implements all abstract methods defined in an abstract class.
|
181
|
+
|
182
|
+
Parameters
|
183
|
+
----------
|
184
|
+
abstract : Callable[..., Any]
|
185
|
+
The abstract class containing abstract methods.
|
186
|
+
concrete : Callable[..., Any], optional
|
187
|
+
The concrete class that should implement the abstract methods.
|
188
|
+
instance : Any, optional
|
189
|
+
The instance that should implement the abstract methods.
|
190
|
+
|
191
|
+
Raises
|
192
|
+
------
|
193
|
+
OrionisContainerException
|
194
|
+
If the concrete class or instance does not implement all abstract methods defined in the abstract class.
|
195
|
+
|
196
|
+
Notes
|
197
|
+
-----
|
198
|
+
This method checks that all abstract methods in the given abstract class are implemented
|
199
|
+
in the provided concrete class or instance. If any methods are missing, an exception is raised with
|
200
|
+
details about the missing implementations.
|
201
|
+
"""
|
202
|
+
if abstract is None:
|
203
|
+
raise OrionisContainerException("Abstract class must be provided for implementation check.")
|
204
|
+
|
205
|
+
abstract_methods = getattr(abstract, '__abstractmethods__', set())
|
206
|
+
if not abstract_methods:
|
207
|
+
raise OrionisContainerException(
|
208
|
+
f"The abstract class '{abstract.__name__}' does not define any abstract methods. "
|
209
|
+
"An abstract class must have at least one abstract method."
|
51
210
|
)
|
52
211
|
|
212
|
+
target = concrete if concrete is not None else instance
|
213
|
+
if target is None:
|
214
|
+
raise OrionisContainerException("Either concrete class or instance must be provided for implementation check.")
|
215
|
+
|
216
|
+
target_class = target if Reflection.isClass(target) else target.__class__
|
217
|
+
target_name = target_class.__name__
|
218
|
+
abstract_name = abstract.__name__
|
219
|
+
|
220
|
+
not_implemented = []
|
221
|
+
for method in abstract_methods:
|
222
|
+
if not hasattr(target, str(method).replace(f"_{abstract_name}", f"_{target_name}")):
|
223
|
+
not_implemented.append(method)
|
224
|
+
|
225
|
+
if not_implemented:
|
226
|
+
formatted_methods = "\n • " + "\n • ".join(not_implemented)
|
227
|
+
raise OrionisContainerException(
|
228
|
+
f"'{target_name}' does not implement the following abstract methods defined in '{abstract_name}':{formatted_methods}\n"
|
229
|
+
"Please ensure that all abstract methods are implemented."
|
230
|
+
)
|
231
|
+
|
232
|
+
def transient(self, abstract: Callable[..., Any], concrete: Callable[..., Any], alias: str = None, enforce_decoupling: bool = False) -> bool:
|
233
|
+
"""
|
234
|
+
Registers a service with a transient lifetime.
|
235
|
+
|
236
|
+
Parameters
|
237
|
+
----------
|
238
|
+
abstract : Callable[..., Any]
|
239
|
+
The abstract base type or interface to be bound.
|
240
|
+
concrete : Callable[..., Any]
|
241
|
+
The concrete implementation to associate with the abstract type.
|
242
|
+
alias : str, optional
|
243
|
+
An alternative name to register the service under. If not provided, the abstract's class name is used.
|
244
|
+
|
245
|
+
Returns
|
246
|
+
-------
|
247
|
+
bool
|
248
|
+
True if the service was registered successfully.
|
249
|
+
|
250
|
+
Raises
|
251
|
+
------
|
252
|
+
OrionisContainerTypeError
|
253
|
+
If the abstract or concrete class checks fail.
|
254
|
+
OrionisContainerException
|
255
|
+
If the concrete class inherits from the abstract class.
|
256
|
+
|
257
|
+
Notes
|
258
|
+
-----
|
259
|
+
Registers the given concrete implementation to the abstract type with a transient lifetime,
|
260
|
+
meaning a new instance will be created each time the service is requested. Optionally, an alias
|
261
|
+
can be provided for registration.
|
262
|
+
"""
|
263
|
+
|
264
|
+
# Ensure that abstract is an abstract class
|
265
|
+
self.__ensureAbstractClass(abstract, Lifetime.TRANSIENT)
|
266
|
+
|
267
|
+
# Ensure that concrete is a concrete class
|
268
|
+
self.__ensureConcreteClass(concrete, Lifetime.TRANSIENT)
|
269
|
+
|
270
|
+
if enforce_decoupling:
|
271
|
+
# Ensure that concrete is NOT a subclass of abstract
|
272
|
+
self.__ensureIsNotSubclass(abstract, concrete)
|
273
|
+
else:
|
274
|
+
# Validate that concrete is a subclass of abstract
|
275
|
+
self.__ensureIsSubclass(abstract, concrete)
|
276
|
+
|
277
|
+
# Ensure implementation
|
278
|
+
self.__ensureImplementation(
|
279
|
+
abstract=abstract,
|
280
|
+
concrete=concrete
|
281
|
+
)
|
282
|
+
|
283
|
+
# Ensure that the alias is a valid string if provided
|
284
|
+
if alias:
|
285
|
+
self.__ensureAliasType(alias)
|
286
|
+
|
53
287
|
# Register the service with transient lifetime
|
54
|
-
self.
|
288
|
+
self.__transient[abstract] = concrete
|
289
|
+
|
290
|
+
# If an alias is provided, register it as well
|
291
|
+
if alias:
|
292
|
+
self.__transient[alias] = concrete
|
293
|
+
elif hasattr(abstract, '__name__'):
|
294
|
+
alias = abstract.__name__
|
295
|
+
self.__transient[alias] = concrete
|
55
296
|
|
56
297
|
# Return True to indicate successful registration
|
57
298
|
return True
|
58
299
|
|
59
|
-
def singleton(self, abstract: Callable[..., Any], concrete: Callable[..., Any]) -> bool:
|
300
|
+
def singleton(self, abstract: Callable[..., Any], concrete: Callable[..., Any], alias: str = None, enforce_decoupling: bool = False) -> bool:
|
60
301
|
"""
|
61
302
|
Registers a service with a singleton lifetime.
|
62
303
|
|
63
304
|
Parameters
|
64
305
|
----------
|
65
306
|
abstract : Callable[..., Any]
|
66
|
-
The abstract base type or
|
307
|
+
The abstract base type or interface to be bound.
|
67
308
|
concrete : Callable[..., Any]
|
68
309
|
The concrete implementation to associate with the abstract type.
|
310
|
+
alias : str, optional
|
311
|
+
An alternative name to register the service under. If not provided, the abstract's class name is used.
|
312
|
+
|
313
|
+
Returns
|
314
|
+
-------
|
315
|
+
bool
|
316
|
+
True if the service was registered successfully.
|
317
|
+
|
318
|
+
Raises
|
319
|
+
------
|
320
|
+
OrionisContainerTypeError
|
321
|
+
If the abstract or concrete class checks fail.
|
322
|
+
OrionisContainerException
|
323
|
+
If the concrete class inherits from the abstract class.
|
324
|
+
|
325
|
+
Notes
|
326
|
+
-----
|
327
|
+
Registers the given concrete implementation to the abstract type with a singleton lifetime,
|
328
|
+
meaning a single instance will be created and shared. Optionally, an alias can be provided for registration.
|
69
329
|
"""
|
70
|
-
# Ensure that abstract is an abstract class and concrete is a concrete class
|
71
|
-
try:
|
72
|
-
ReflectionAbstract.ensureIsAbstractClass(abstract)
|
73
|
-
ReflectionConcrete.ensureIsConcreteClass(concrete)
|
74
|
-
except Exception as e:
|
75
|
-
raise OrionisContainerTypeError(
|
76
|
-
f"Unexpected error registering singleton service: {e}"
|
77
|
-
) from e
|
78
330
|
|
79
|
-
# Ensure that
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
331
|
+
# Ensure that abstract is an abstract class
|
332
|
+
self.__ensureAbstractClass(abstract, Lifetime.SINGLETON)
|
333
|
+
|
334
|
+
# Ensure that concrete is a concrete class
|
335
|
+
self.__ensureConcreteClass(concrete, Lifetime.SINGLETON)
|
336
|
+
|
337
|
+
if enforce_decoupling:
|
338
|
+
# Ensure that concrete is NOT a subclass of abstract
|
339
|
+
self.__ensureIsNotSubclass(abstract, concrete)
|
340
|
+
else:
|
341
|
+
# Validate that concrete is a subclass of abstract
|
342
|
+
self.__ensureIsSubclass(abstract, concrete)
|
343
|
+
|
344
|
+
# Ensure implementation
|
345
|
+
self.__ensureImplementation(
|
346
|
+
abstract=abstract,
|
347
|
+
concrete=concrete
|
348
|
+
)
|
349
|
+
|
350
|
+
# Ensure that the alias is a valid string if provided
|
351
|
+
if alias:
|
352
|
+
self.__ensureAliasType(alias)
|
85
353
|
|
86
354
|
# Register the service with singleton lifetime
|
87
|
-
self.
|
355
|
+
self.__singleton[abstract] = concrete
|
356
|
+
|
357
|
+
# If an alias is provided, register it as well
|
358
|
+
if alias:
|
359
|
+
self.__singleton[alias] = concrete
|
360
|
+
elif hasattr(abstract, '__name__'):
|
361
|
+
alias = abstract.__name__
|
362
|
+
self.__singleton[alias] = concrete
|
88
363
|
|
89
364
|
# Return True to indicate successful registration
|
90
365
|
return True
|
91
366
|
|
92
|
-
def scoped(self, abstract: Callable[..., Any], concrete: Callable[..., Any]) -> bool:
|
367
|
+
def scoped(self, abstract: Callable[..., Any], concrete: Callable[..., Any], alias: str = None, enforce_decoupling: bool = False) -> bool:
|
93
368
|
"""
|
94
369
|
Registers a service with a scoped lifetime.
|
95
370
|
|
96
371
|
Parameters
|
97
372
|
----------
|
98
373
|
abstract : Callable[..., Any]
|
99
|
-
The abstract base type or
|
374
|
+
The abstract base type or interface to be bound.
|
100
375
|
concrete : Callable[..., Any]
|
101
376
|
The concrete implementation to associate with the abstract type.
|
377
|
+
alias : str, optional
|
378
|
+
An alternative name to register the service under. If not provided, the abstract's class name is used.
|
379
|
+
|
380
|
+
Returns
|
381
|
+
-------
|
382
|
+
bool
|
383
|
+
True if the service was registered successfully.
|
384
|
+
|
385
|
+
Raises
|
386
|
+
------
|
387
|
+
OrionisContainerTypeError
|
388
|
+
If the abstract or concrete class checks fail.
|
389
|
+
OrionisContainerException
|
390
|
+
If the concrete class inherits from the abstract class.
|
391
|
+
|
392
|
+
Notes
|
393
|
+
-----
|
394
|
+
Registers the given concrete implementation to the abstract type with a scoped lifetime,
|
395
|
+
meaning a new instance will be created per scope. Optionally, an alias can be provided for registration.
|
102
396
|
"""
|
103
|
-
# Ensure that abstract is an abstract class and concrete is a concrete class
|
104
|
-
try:
|
105
|
-
ReflectionAbstract.ensureIsAbstractClass(abstract)
|
106
|
-
ReflectionConcrete.ensureIsConcreteClass(concrete)
|
107
|
-
except Exception as e:
|
108
|
-
raise OrionisContainerTypeError(
|
109
|
-
f"Unexpected error registering scoped service: {e}"
|
110
|
-
) from e
|
111
397
|
|
112
|
-
# Ensure that
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
398
|
+
# Ensure that abstract is an abstract class
|
399
|
+
self.__ensureAbstractClass(abstract, Lifetime.SCOPED)
|
400
|
+
|
401
|
+
# Ensure that concrete is a concrete class
|
402
|
+
self.__ensureConcreteClass(concrete, Lifetime.SCOPED)
|
403
|
+
|
404
|
+
if enforce_decoupling:
|
405
|
+
# Ensure that concrete is NOT a subclass of abstract
|
406
|
+
self.__ensureIsNotSubclass(abstract, concrete)
|
407
|
+
else:
|
408
|
+
# Validate that concrete is a subclass of abstract
|
409
|
+
self.__ensureIsSubclass(abstract, concrete)
|
410
|
+
|
411
|
+
# Ensure implementation
|
412
|
+
self.__ensureImplementation(
|
413
|
+
abstract=abstract,
|
414
|
+
concrete=concrete
|
415
|
+
)
|
416
|
+
|
417
|
+
# Ensure that the alias is a valid string if provided
|
418
|
+
if alias:
|
419
|
+
self.__ensureAliasType(alias)
|
118
420
|
|
119
421
|
# Register the service with scoped lifetime
|
120
|
-
self.
|
422
|
+
self.__scoped[abstract] = concrete
|
423
|
+
|
424
|
+
# If an alias is provided, register it as well
|
425
|
+
if alias:
|
426
|
+
self.__scoped[alias] = concrete
|
427
|
+
elif hasattr(abstract, '__name__'):
|
428
|
+
alias = abstract.__name__
|
429
|
+
self.__scoped[alias] = concrete
|
121
430
|
|
122
431
|
# Return True to indicate successful registration
|
123
|
-
return True
|
432
|
+
return True
|
433
|
+
|
434
|
+
def instance(self, abstract: Callable[..., Any], instance: Any, alias: str = None, enforce_decoupling: bool = False) -> bool:
|
435
|
+
"""
|
436
|
+
Registers an instance of a class or interface in the container.
|
437
|
+
Parameters
|
438
|
+
----------
|
439
|
+
abstract : Callable[..., Any]
|
440
|
+
The abstract class or interface to associate with the instance.
|
441
|
+
instance : Any
|
442
|
+
The concrete instance to register.
|
443
|
+
alias : str, optional
|
444
|
+
An optional alias to register the instance under. If not provided,
|
445
|
+
the abstract's `__name__` attribute will be used as the alias if available.
|
446
|
+
Returns
|
447
|
+
-------
|
448
|
+
bool
|
449
|
+
True if the instance was successfully registered.
|
450
|
+
Raises
|
451
|
+
------
|
452
|
+
TypeError
|
453
|
+
If `abstract` is not an abstract class or if `alias` is not a valid string.
|
454
|
+
ValueError
|
455
|
+
If `instance` is not a valid instance of `abstract`.
|
456
|
+
Notes
|
457
|
+
-----
|
458
|
+
This method ensures that the abstract is a valid abstract class, the instance
|
459
|
+
is valid, and the alias (if provided) is a valid string. The instance is then
|
460
|
+
registered in the container under both the abstract and the alias.
|
461
|
+
"""
|
462
|
+
|
463
|
+
# Ensure that the abstract is an abstract class
|
464
|
+
self.__ensureAbstractClass(abstract, f"Instance {Lifetime.SINGLETON}")
|
465
|
+
|
466
|
+
# Ensure that the instance is a valid instance
|
467
|
+
self.__ensureInstance(instance)
|
468
|
+
|
469
|
+
if enforce_decoupling:
|
470
|
+
# Ensure that instance is NOT a subclass of abstract
|
471
|
+
self.__ensureIsNotSubclass(abstract, instance.__class__)
|
472
|
+
else:
|
473
|
+
# Validate that instance is a subclass of abstract
|
474
|
+
self.__ensureIsSubclass(abstract, instance.__class__)
|
475
|
+
|
476
|
+
# Ensure implementation
|
477
|
+
self.__ensureImplementation(
|
478
|
+
abstract=abstract,
|
479
|
+
instance=instance
|
480
|
+
)
|
481
|
+
|
482
|
+
# Ensure that the alias is a valid string if provided
|
483
|
+
if alias:
|
484
|
+
self.__ensureAliasType(alias)
|
485
|
+
|
486
|
+
# Register the instance with the abstract type
|
487
|
+
self.__instance[abstract] = instance
|
488
|
+
|
489
|
+
# If an alias is provided, register it as well
|
490
|
+
if alias:
|
491
|
+
self.__instance[alias] = instance
|
492
|
+
elif hasattr(abstract, '__name__'):
|
493
|
+
alias = abstract.__name__
|
494
|
+
self.__instance[alias] = instance
|
495
|
+
|
496
|
+
# Return True to indicate successful registration
|
497
|
+
return True
|
498
|
+
|
499
|
+
def bind(self, concrete_instance_or_function, lifetime: str = Lifetime.TRANSIENT, alias: str = None) -> None:
|
500
|
+
pass
|
orionis/metadata/framework.py
CHANGED
@@ -12,35 +12,103 @@ from orionis.services.introspection.contracts.reflection_instance import IReflec
|
|
12
12
|
|
13
13
|
class ReflectionInstance(IReflectionInstance):
|
14
14
|
|
15
|
-
|
15
|
+
@staticmethod
|
16
|
+
def isInstance(instance: Any) -> bool:
|
16
17
|
"""
|
17
|
-
|
18
|
+
Check if the given object is a valid instance according to ReflectionInstance rules.
|
18
19
|
|
19
20
|
Parameters
|
20
21
|
----------
|
21
22
|
instance : Any
|
22
|
-
The object
|
23
|
+
The object to check.
|
24
|
+
|
25
|
+
Returns
|
26
|
+
-------
|
27
|
+
bool
|
28
|
+
True if the object is a valid instance, False otherwise.
|
29
|
+
|
30
|
+
Notes
|
31
|
+
-----
|
32
|
+
This method catches and handles exceptions internally; it does not raise.
|
33
|
+
"""
|
34
|
+
try:
|
35
|
+
return ReflectionInstance.ensureIsInstance(instance)
|
36
|
+
except (ReflectionTypeError, ReflectionValueError):
|
37
|
+
return False
|
38
|
+
|
39
|
+
@staticmethod
|
40
|
+
def ensureIsInstance(instance: Any) -> bool:
|
41
|
+
"""
|
42
|
+
Validate that the provided object is a proper instance of a user-defined class.
|
43
|
+
|
44
|
+
Parameters
|
45
|
+
----------
|
46
|
+
instance : Any
|
47
|
+
The object to validate.
|
48
|
+
|
49
|
+
Returns
|
50
|
+
-------
|
51
|
+
bool
|
52
|
+
True if the instance passes all checks.
|
23
53
|
|
24
54
|
Raises
|
25
55
|
------
|
26
56
|
ReflectionTypeError
|
27
|
-
If the
|
57
|
+
If the input is not a valid object instance.
|
28
58
|
ReflectionValueError
|
29
|
-
If the instance belongs to a
|
59
|
+
If the instance belongs to a disallowed module ('builtins', 'abc') or originates from '__main__'.
|
60
|
+
|
61
|
+
Notes
|
62
|
+
-----
|
63
|
+
This method performs the following checks:
|
64
|
+
1. Ensures the input is an object instance (not a class/type).
|
65
|
+
2. Disallows instances of types defined in the 'builtins' or 'abc' modules.
|
66
|
+
3. Disallows instances originating from the '__main__' module, requiring importable module origins.
|
30
67
|
"""
|
68
|
+
|
69
|
+
# Ensure the provided instance is a valid object instance
|
31
70
|
if not (isinstance(instance, object) and not isinstance(instance, type)):
|
32
71
|
raise ReflectionTypeError(
|
33
72
|
f"Expected an object instance, got {type(instance).__name__!r}: {instance!r}"
|
34
73
|
)
|
74
|
+
|
75
|
+
# Check if the instance belongs to a built-in or abstract base class
|
35
76
|
module = type(instance).__module__
|
36
77
|
if module in {'builtins', 'abc'}:
|
37
78
|
raise ReflectionValueError(
|
38
79
|
f"Instance of type '{type(instance).__name__}' belongs to disallowed module '{module}'."
|
39
80
|
)
|
81
|
+
|
82
|
+
# Check if the instance originates from '__main__'
|
40
83
|
if module == '__main__':
|
41
84
|
raise ReflectionValueError(
|
42
85
|
"Instance originates from '__main__'; please provide an instance from an importable module."
|
43
86
|
)
|
87
|
+
|
88
|
+
# If all checks pass, return True
|
89
|
+
return True
|
90
|
+
|
91
|
+
def __init__(self, instance: Any) -> None:
|
92
|
+
"""
|
93
|
+
Initialize the ReflectionInstance with a given object instance.
|
94
|
+
|
95
|
+
Parameters
|
96
|
+
----------
|
97
|
+
instance : Any
|
98
|
+
The object instance to be reflected upon.
|
99
|
+
|
100
|
+
Raises
|
101
|
+
------
|
102
|
+
ReflectionTypeError
|
103
|
+
If the provided instance is not a valid object instance.
|
104
|
+
ReflectionValueError
|
105
|
+
If the instance belongs to a built-in, abstract base class, or '__main__' module.
|
106
|
+
"""
|
107
|
+
|
108
|
+
# Ensure the instance is valid
|
109
|
+
ReflectionInstance.ensureIsInstance(instance)
|
110
|
+
|
111
|
+
# Store the instance for reflection
|
44
112
|
self._instance = instance
|
45
113
|
|
46
114
|
def getInstance(self) -> Any:
|