orionis 0.318.0__py3-none-any.whl → 0.319.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 +397 -14
- orionis/container/contracts/container.py +202 -0
- orionis/metadata/framework.py +1 -1
- orionis/services/introspection/callables/reflection_callable.py +14 -0
- orionis/services/introspection/concretes/reflection_concrete.py +11 -0
- {orionis-0.318.0.dist-info → orionis-0.319.0.dist-info}/METADATA +1 -1
- {orionis-0.318.0.dist-info → orionis-0.319.0.dist-info}/RECORD +11 -10
- {orionis-0.318.0.dist-info → orionis-0.319.0.dist-info}/WHEEL +0 -0
- {orionis-0.318.0.dist-info → orionis-0.319.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.318.0.dist-info → orionis-0.319.0.dist-info}/top_level.txt +0 -0
- {orionis-0.318.0.dist-info → orionis-0.319.0.dist-info}/zip-safe +0 -0
orionis/container/container.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
+
import threading
|
1
2
|
from typing import Any, Callable
|
3
|
+
from orionis.container.contracts.container import IContainer
|
2
4
|
from orionis.container.entities.binding import Binding
|
3
5
|
from orionis.container.enums.lifetimes import Lifetime
|
4
6
|
from orionis.container.exceptions.container_exception import OrionisContainerException
|
@@ -6,11 +8,11 @@ from orionis.container.exceptions.type_error_exception import OrionisContainerTy
|
|
6
8
|
from orionis.services.introspection.abstract.reflection_abstract import ReflectionAbstract
|
7
9
|
from orionis.services.introspection.callables.reflection_callable import ReflectionCallable
|
8
10
|
from orionis.services.introspection.concretes.reflection_concrete import ReflectionConcrete
|
11
|
+
from orionis.services.introspection.dependencies.entities.resolved_dependencies import ResolvedDependency
|
9
12
|
from orionis.services.introspection.instances.reflection_instance import ReflectionInstance
|
10
13
|
from orionis.services.introspection.reflection import Reflection
|
11
|
-
import threading
|
12
14
|
|
13
|
-
class Container:
|
15
|
+
class Container(IContainer):
|
14
16
|
|
15
17
|
# Singleton instance of the container.
|
16
18
|
# This is a class variable that holds the single instance of the Container class.
|
@@ -26,7 +28,11 @@ class Container:
|
|
26
28
|
# regardless of how many times the class is instantiated.
|
27
29
|
_initialized = False
|
28
30
|
|
29
|
-
def __new__(
|
31
|
+
def __new__(
|
32
|
+
cls,
|
33
|
+
*args,
|
34
|
+
**kwargs
|
35
|
+
) -> 'Container':
|
30
36
|
"""
|
31
37
|
Creates and returns a singleton instance of the class.
|
32
38
|
|
@@ -51,24 +57,29 @@ class Container:
|
|
51
57
|
cls._instance = super(Container, cls).__new__(cls)
|
52
58
|
return cls._instance
|
53
59
|
|
54
|
-
def __init__(
|
60
|
+
def __init__(
|
61
|
+
self
|
62
|
+
) -> None:
|
55
63
|
"""
|
56
64
|
Initializes a new instance of the container.
|
57
65
|
|
58
66
|
This constructor sets up the internal dictionaries for bindings and aliases,
|
59
67
|
ensuring that these are only initialized once per class. The initialization
|
60
68
|
is guarded by the `_initialized` class attribute to prevent redundant setup.
|
69
|
+
The container also registers itself as a service to allow for injection.
|
61
70
|
|
62
71
|
Notes
|
63
72
|
-----
|
64
73
|
- The `__bindings` dictionary is used to store service bindings.
|
65
74
|
- The `__aliasses` dictionary is used to store service aliases.
|
66
75
|
- Initialization occurs only once per class, regardless of the number of instances.
|
76
|
+
- The container registers itself under the IContainer interface to allow for dependency injection.
|
67
77
|
"""
|
68
78
|
if not self.__class__._initialized:
|
69
79
|
self.__bindings = {}
|
70
80
|
self.__aliasses = {}
|
71
81
|
self.__class__._initialized = True
|
82
|
+
self.instance(IContainer, self)
|
72
83
|
|
73
84
|
def __dropService(
|
74
85
|
self,
|
@@ -379,6 +390,48 @@ class Container:
|
|
379
390
|
"Please ensure that all abstract methods are implemented."
|
380
391
|
)
|
381
392
|
|
393
|
+
def __getService(
|
394
|
+
self,
|
395
|
+
abstract_or_alias: Any
|
396
|
+
) -> Binding:
|
397
|
+
"""
|
398
|
+
Retrieves the binding for the requested abstract type or alias.
|
399
|
+
|
400
|
+
Parameters
|
401
|
+
----------
|
402
|
+
abstract_or_alias : Any
|
403
|
+
The abstract class, interface, or alias (str) to retrieve.
|
404
|
+
|
405
|
+
Returns
|
406
|
+
-------
|
407
|
+
Binding
|
408
|
+
The binding associated with the requested abstract type or alias.
|
409
|
+
"""
|
410
|
+
return self.__bindings.get(abstract_or_alias) or self.__aliasses.get(abstract_or_alias)
|
411
|
+
|
412
|
+
def __getFirstService(
|
413
|
+
self,
|
414
|
+
abstract_or_aliasses: list
|
415
|
+
) -> Binding:
|
416
|
+
"""
|
417
|
+
Retrieves the first binding from a list of abstract types or aliases.
|
418
|
+
|
419
|
+
Parameters
|
420
|
+
----------
|
421
|
+
abstract_or_aliasses : list
|
422
|
+
A list of abstract classes, interfaces, or aliases (str) to retrieve.
|
423
|
+
|
424
|
+
Returns
|
425
|
+
-------
|
426
|
+
Binding
|
427
|
+
The first binding found in the container for the provided abstract types or aliases.
|
428
|
+
"""
|
429
|
+
for item in abstract_or_aliasses:
|
430
|
+
binding = self.__getService(item)
|
431
|
+
if binding:
|
432
|
+
return binding
|
433
|
+
return None
|
434
|
+
|
382
435
|
def transient(
|
383
436
|
self,
|
384
437
|
abstract: Callable[..., Any],
|
@@ -715,7 +768,7 @@ class Container:
|
|
715
768
|
def function(
|
716
769
|
self,
|
717
770
|
alias: str,
|
718
|
-
|
771
|
+
fn: Callable[..., Any],
|
719
772
|
*,
|
720
773
|
lifetime: Lifetime = Lifetime.TRANSIENT
|
721
774
|
) -> bool:
|
@@ -726,7 +779,7 @@ class Container:
|
|
726
779
|
----------
|
727
780
|
alias : str
|
728
781
|
The alias to register the function under.
|
729
|
-
|
782
|
+
fn : Callable[..., Any]
|
730
783
|
The function or factory to register.
|
731
784
|
lifetime : Lifetime, optional
|
732
785
|
The lifetime of the function registration (default is TRANSIENT).
|
@@ -763,10 +816,10 @@ class Container:
|
|
763
816
|
self.__ensureAliasType(alias)
|
764
817
|
|
765
818
|
# Validate that the function is callable
|
766
|
-
self.__ensureIsCallable(
|
819
|
+
self.__ensureIsCallable(fn)
|
767
820
|
|
768
821
|
# Inspect the function signature
|
769
|
-
params = ReflectionCallable(
|
822
|
+
params = ReflectionCallable(fn).getDependencies()
|
770
823
|
|
771
824
|
# If the function requires arguments, only allow TRANSIENT
|
772
825
|
if (len(params.resolved) + len(params.unresolved)) > 0 and lifetime != Lifetime.TRANSIENT:
|
@@ -779,7 +832,7 @@ class Container:
|
|
779
832
|
|
780
833
|
# Register the function with the specified alias and lifetime
|
781
834
|
self.__bindings[alias] = Binding(
|
782
|
-
function=
|
835
|
+
function=fn,
|
783
836
|
lifetime=lifetime,
|
784
837
|
alias=alias
|
785
838
|
)
|
@@ -819,8 +872,8 @@ class Container:
|
|
819
872
|
def make(
|
820
873
|
self,
|
821
874
|
abstract_or_alias: Any,
|
822
|
-
*args,
|
823
|
-
**kwargs
|
875
|
+
*args: tuple,
|
876
|
+
**kwargs: dict
|
824
877
|
) -> Any:
|
825
878
|
"""
|
826
879
|
Resolves and returns an instance of the requested service.
|
@@ -844,11 +897,341 @@ class Container:
|
|
844
897
|
OrionisContainerException
|
845
898
|
If the requested service is not registered in the container.
|
846
899
|
"""
|
847
|
-
|
900
|
+
# Retrieve the binding for the requested abstract or alias
|
901
|
+
binding = self.__getService(abstract_or_alias)
|
902
|
+
|
903
|
+
# Check if the requested service is registered in the container
|
904
|
+
if not binding:
|
848
905
|
raise OrionisContainerException(
|
849
906
|
f"The requested service '{abstract_or_alias}' is not registered in the container."
|
850
907
|
)
|
851
908
|
|
852
|
-
binding
|
909
|
+
# Handle based on binding type and lifetime
|
910
|
+
if binding.lifetime == Lifetime.TRANSIENT:
|
911
|
+
return self.__resolveTransient(binding, *args, **kwargs)
|
912
|
+
elif binding.lifetime == Lifetime.SINGLETON:
|
913
|
+
return self.__resolveSingleton(binding, *args, **kwargs)
|
914
|
+
elif binding.lifetime == Lifetime.SCOPED:
|
915
|
+
# TODO: Implement scoped lifetime resolution
|
916
|
+
raise OrionisContainerException(
|
917
|
+
"Scoped lifetime resolution is not yet implemented."
|
918
|
+
)
|
919
|
+
|
920
|
+
def __resolveTransient(self, binding: Binding, *args, **kwargs) -> Any:
|
921
|
+
"""
|
922
|
+
Resolves a service with transient lifetime.
|
923
|
+
|
924
|
+
Parameters
|
925
|
+
----------
|
926
|
+
binding : Binding
|
927
|
+
The binding to resolve.
|
928
|
+
*args : tuple
|
929
|
+
Positional arguments to pass to the constructor.
|
930
|
+
**kwargs : dict
|
931
|
+
Keyword arguments to pass to the constructor.
|
932
|
+
|
933
|
+
Returns
|
934
|
+
-------
|
935
|
+
Any
|
936
|
+
A new instance of the requested service.
|
937
|
+
"""
|
938
|
+
|
939
|
+
# Check if the binding has a concrete class or function defined
|
940
|
+
if binding.concrete:
|
941
|
+
if args or kwargs:
|
942
|
+
return self.__instantiateConcreteWithArgs(binding.concrete, *args, **kwargs)
|
943
|
+
else:
|
944
|
+
return self.__instantiateConcreteReflective(binding.concrete)
|
945
|
+
|
946
|
+
# If the binding has a function defined
|
947
|
+
elif binding.function:
|
948
|
+
if args or kwargs:
|
949
|
+
return self.__instantiateCallableWithArgs(binding.function, *args, **kwargs)
|
950
|
+
else:
|
951
|
+
return self.__instantiateCallableReflective(binding.function)
|
952
|
+
|
953
|
+
# If neither concrete class nor function is defined
|
954
|
+
else:
|
955
|
+
raise OrionisContainerException(
|
956
|
+
"Cannot resolve transient binding: neither a concrete class nor a function is defined."
|
957
|
+
)
|
958
|
+
|
959
|
+
def __resolveSingleton(self, binding: Binding, *args, **kwargs) -> Any:
|
960
|
+
"""
|
961
|
+
Resolves a service with singleton lifetime.
|
962
|
+
|
963
|
+
Parameters
|
964
|
+
----------
|
965
|
+
binding : Binding
|
966
|
+
The binding to resolve.
|
967
|
+
*args : tuple
|
968
|
+
Positional arguments to pass to the constructor (only used if instance doesn't exist yet).
|
969
|
+
**kwargs : dict
|
970
|
+
Keyword arguments to pass to the constructor (only used if instance doesn't exist yet).
|
971
|
+
|
972
|
+
Returns
|
973
|
+
-------
|
974
|
+
Any
|
975
|
+
The singleton instance of the requested service.
|
976
|
+
"""
|
977
|
+
# Return existing instance if available
|
978
|
+
if binding.instance:
|
979
|
+
return binding.instance
|
980
|
+
|
981
|
+
# Create instance if needed
|
982
|
+
if binding.concrete:
|
983
|
+
if args or kwargs:
|
984
|
+
binding.instance = self.__instantiateConcreteWithArgs(binding.concrete, *args, **kwargs)
|
985
|
+
else:
|
986
|
+
binding.instance = self.__instantiateConcreteReflective(binding.concrete)
|
987
|
+
return binding.instance
|
988
|
+
|
989
|
+
# If the binding has a function defined
|
990
|
+
elif binding.function:
|
991
|
+
if args or kwargs:
|
992
|
+
result = self.__instantiateCallableWithArgs(binding.function, *args, **kwargs)
|
993
|
+
else:
|
994
|
+
result = self.__instantiateCallableReflective(binding.function)
|
995
|
+
|
996
|
+
# Store the result directly as the singleton instance
|
997
|
+
# We don't automatically invoke factory function results anymore
|
998
|
+
binding.instance = result
|
999
|
+
return binding.instance
|
1000
|
+
|
1001
|
+
# If neither concrete class nor function is defined
|
1002
|
+
else:
|
1003
|
+
raise OrionisContainerException(
|
1004
|
+
"Cannot resolve singleton binding: neither a concrete class, instance, nor function is defined."
|
1005
|
+
)
|
1006
|
+
|
1007
|
+
def __instantiateConcreteWithArgs(self, concrete: Callable[..., Any], *args, **kwargs) -> Any:
|
1008
|
+
"""
|
1009
|
+
Instantiates a concrete class with the provided arguments.
|
1010
|
+
|
1011
|
+
Parameters
|
1012
|
+
----------
|
1013
|
+
concrete : Callable[..., Any]
|
1014
|
+
Class to instantiate.
|
1015
|
+
*args : tuple
|
1016
|
+
Positional arguments to pass to the constructor.
|
1017
|
+
**kwargs : dict
|
1018
|
+
Keyword arguments to pass to the constructor.
|
853
1019
|
|
854
|
-
|
1020
|
+
Returns
|
1021
|
+
-------
|
1022
|
+
object
|
1023
|
+
A new instance of the specified concrete class.
|
1024
|
+
"""
|
1025
|
+
|
1026
|
+
# try to instantiate the concrete class with the provided arguments
|
1027
|
+
try:
|
1028
|
+
|
1029
|
+
# If the concrete is a class, instantiate it directly
|
1030
|
+
return concrete(*args, **kwargs)
|
1031
|
+
|
1032
|
+
except TypeError as e:
|
1033
|
+
|
1034
|
+
# If instantiation fails, use ReflectionConcrete to get class name and constructor signature
|
1035
|
+
rf_concrete = ReflectionConcrete(concrete)
|
1036
|
+
class_name = rf_concrete.getClassName()
|
1037
|
+
signature = rf_concrete.getConstructorSignature()
|
1038
|
+
|
1039
|
+
# Raise an exception with detailed information about the failure
|
1040
|
+
raise OrionisContainerException(
|
1041
|
+
f"Failed to instantiate [{class_name}] with the provided arguments: {e}\n"
|
1042
|
+
f"Expected constructor signature: [{signature}]"
|
1043
|
+
) from e
|
1044
|
+
|
1045
|
+
def __instantiateCallableWithArgs(self, fn: Callable[..., Any], *args, **kwargs) -> Any:
|
1046
|
+
"""
|
1047
|
+
Invokes a callable with the provided arguments.
|
1048
|
+
|
1049
|
+
Parameters
|
1050
|
+
----------
|
1051
|
+
fn : Callable[..., Any]
|
1052
|
+
The callable to invoke.
|
1053
|
+
*args : tuple
|
1054
|
+
Positional arguments to pass to the callable.
|
1055
|
+
**kwargs : dict
|
1056
|
+
Keyword arguments to pass to the callable.
|
1057
|
+
|
1058
|
+
Returns
|
1059
|
+
-------
|
1060
|
+
Any
|
1061
|
+
The result of the callable.
|
1062
|
+
"""
|
1063
|
+
|
1064
|
+
# Try to invoke the callable with the provided arguments
|
1065
|
+
try:
|
1066
|
+
|
1067
|
+
# If the callable is a function, invoke it directly
|
1068
|
+
return fn(*args, **kwargs)
|
1069
|
+
|
1070
|
+
except TypeError as e:
|
1071
|
+
|
1072
|
+
# If invocation fails, use ReflectionCallable to get function name and signature
|
1073
|
+
rf_callable = ReflectionCallable(fn)
|
1074
|
+
function_name = rf_callable.getName()
|
1075
|
+
signature = rf_callable.getSignature()
|
1076
|
+
|
1077
|
+
# Raise an exception with detailed information about the failure
|
1078
|
+
raise OrionisContainerException(
|
1079
|
+
f"Failed to invoke function [{function_name}] with the provided arguments: {e}\n"
|
1080
|
+
f"Expected function signature: [{signature}]"
|
1081
|
+
) from e
|
1082
|
+
|
1083
|
+
def __instantiateConcreteReflective(self, concrete: Callable[..., Any]) -> Any:
|
1084
|
+
"""
|
1085
|
+
Instantiates a concrete class reflectively, resolving its dependencies from the container.
|
1086
|
+
|
1087
|
+
Parameters
|
1088
|
+
----------
|
1089
|
+
concrete : Callable[..., Any]
|
1090
|
+
The concrete class to instantiate.
|
1091
|
+
|
1092
|
+
Returns
|
1093
|
+
-------
|
1094
|
+
Any
|
1095
|
+
A new instance of the concrete class.
|
1096
|
+
"""
|
1097
|
+
# Resolve dependencies for the concrete class
|
1098
|
+
params = self.__resolveDependencies(concrete, is_class=True)
|
1099
|
+
|
1100
|
+
# Instantiate the concrete class with resolved dependencies
|
1101
|
+
return concrete(**params)
|
1102
|
+
|
1103
|
+
def __instantiateCallableReflective(self, fn: Callable[..., Any]) -> Any:
|
1104
|
+
"""
|
1105
|
+
Invokes a callable reflectively, resolving its dependencies from the container.
|
1106
|
+
|
1107
|
+
Parameters
|
1108
|
+
----------
|
1109
|
+
fn : Callable[..., Any]
|
1110
|
+
The callable to invoke.
|
1111
|
+
|
1112
|
+
Returns
|
1113
|
+
-------
|
1114
|
+
Any
|
1115
|
+
The result of the callable.
|
1116
|
+
"""
|
1117
|
+
|
1118
|
+
# Resolve dependencies for the callable
|
1119
|
+
params = self.__resolveDependencies(fn, is_class=False)
|
1120
|
+
|
1121
|
+
# Invoke the callable with resolved dependencies
|
1122
|
+
return fn(**params)
|
1123
|
+
|
1124
|
+
def __resolveDependencies(
|
1125
|
+
self,
|
1126
|
+
target: Callable[..., Any],
|
1127
|
+
*,
|
1128
|
+
is_class: bool = False
|
1129
|
+
) -> dict:
|
1130
|
+
"""
|
1131
|
+
Resolves dependencies for a target callable or class.
|
1132
|
+
|
1133
|
+
Parameters
|
1134
|
+
----------
|
1135
|
+
target : Callable[..., Any]
|
1136
|
+
The target callable or class whose dependencies to resolve.
|
1137
|
+
is_class : bool, optional
|
1138
|
+
Whether the target is a class (True) or a callable (False).
|
1139
|
+
|
1140
|
+
Returns
|
1141
|
+
-------
|
1142
|
+
dict
|
1143
|
+
A dictionary of resolved dependencies.
|
1144
|
+
"""
|
1145
|
+
try:
|
1146
|
+
|
1147
|
+
# Use ReflectionConcrete for classes and ReflectionCallable for callables
|
1148
|
+
if is_class:
|
1149
|
+
reflection = ReflectionConcrete(target)
|
1150
|
+
dependencies = reflection.getConstructorDependencies()
|
1151
|
+
name = reflection.getClassName()
|
1152
|
+
|
1153
|
+
# If the target is a callable, use ReflectionCallable
|
1154
|
+
else:
|
1155
|
+
reflection = ReflectionCallable(target)
|
1156
|
+
dependencies = reflection.getDependencies()
|
1157
|
+
name = reflection.getName()
|
1158
|
+
|
1159
|
+
# Check for unresolved dependencies
|
1160
|
+
if dependencies.unresolved:
|
1161
|
+
unresolved_args = ', '.join(dependencies.unresolved)
|
1162
|
+
raise OrionisContainerException(
|
1163
|
+
f"Cannot resolve '{name}' because the following required arguments are missing: [{unresolved_args}]."
|
1164
|
+
)
|
1165
|
+
|
1166
|
+
# Resolve dependencies
|
1167
|
+
params = {}
|
1168
|
+
for param_name, dep in dependencies.resolved.items():
|
1169
|
+
|
1170
|
+
# If the dependency is a ResolvedDependency, resolve it
|
1171
|
+
if isinstance(dep, ResolvedDependency):
|
1172
|
+
|
1173
|
+
# If the dependency is a built-in type, raise an exception
|
1174
|
+
if dep.module_name == 'builtins':
|
1175
|
+
raise OrionisContainerException(
|
1176
|
+
f"Cannot resolve '{name}' because parameter '{param_name}' depends on built-in type '{dep.type.__name__}'."
|
1177
|
+
)
|
1178
|
+
|
1179
|
+
# Try to resolve from container
|
1180
|
+
service = self.__getFirstService([dep.type, dep.full_class_path])
|
1181
|
+
if service:
|
1182
|
+
params[param_name] = self.make(service.alias)
|
1183
|
+
|
1184
|
+
# Try to instantiate directly if it's a concrete class
|
1185
|
+
elif ReflectionConcrete.isConcreteClass(dep.type):
|
1186
|
+
params[param_name] = dep.type(**self.__resolveDependencies(dep.type, is_class=True))
|
1187
|
+
|
1188
|
+
# Try to call directly if it's a callable
|
1189
|
+
elif callable(dep.type) and not isinstance(dep.type, type):
|
1190
|
+
params[param_name] = dep.type(**self.__resolveDependencies(dep.type, is_class=False))
|
1191
|
+
|
1192
|
+
# If the dependency cannot be resolved, raise an exception
|
1193
|
+
else:
|
1194
|
+
raise OrionisContainerException(
|
1195
|
+
f"Cannot resolve dependency '{param_name}' of type '{dep.type.__name__}' for '{name}'."
|
1196
|
+
)
|
1197
|
+
else:
|
1198
|
+
# Use default value
|
1199
|
+
params[param_name] = dep
|
1200
|
+
|
1201
|
+
# Return the resolved parameters
|
1202
|
+
return params
|
1203
|
+
|
1204
|
+
except ImportError as e:
|
1205
|
+
|
1206
|
+
# Extract module name from the error message if possible
|
1207
|
+
import_msg = str(e)
|
1208
|
+
module_name = target.__module__ if hasattr(target, '__module__') else "unknown module"
|
1209
|
+
|
1210
|
+
# Check for potential circular import patterns
|
1211
|
+
if "circular import" in import_msg.lower() or "cannot import name" in import_msg.lower():
|
1212
|
+
raise OrionisContainerException(
|
1213
|
+
f"Circular import detected while resolving dependencies for '{target.__name__}' in module '{module_name}'.\n"
|
1214
|
+
f"This typically happens when two modules import each other. Consider:\n"
|
1215
|
+
f"1. Restructuring your code to avoid circular dependencies\n"
|
1216
|
+
f"2. Using delayed imports inside methods rather than at module level\n"
|
1217
|
+
f"3. Using dependency injection to break the cycle\n"
|
1218
|
+
f"Original error: {import_msg}"
|
1219
|
+
) from e
|
1220
|
+
else:
|
1221
|
+
raise OrionisContainerException(
|
1222
|
+
f"Import error while resolving dependencies for '{target.__name__}' in module '{module_name}':\n"
|
1223
|
+
f"{import_msg}"
|
1224
|
+
) from e
|
1225
|
+
|
1226
|
+
except Exception as e:
|
1227
|
+
|
1228
|
+
# Get more context about where the error occurred
|
1229
|
+
target_type = "class" if isinstance(target, type) else "function"
|
1230
|
+
target_name = target.__name__ if hasattr(target, '__name__') else str(target)
|
1231
|
+
module_name = target.__module__ if hasattr(target, '__module__') else "unknown module"
|
1232
|
+
|
1233
|
+
raise OrionisContainerException(
|
1234
|
+
f"Error resolving dependencies for {target_type} '{target_name}' in '{module_name}':\n"
|
1235
|
+
f"{str(e)}\n"
|
1236
|
+
f"Check that all dependencies are properly registered in the container."
|
1237
|
+
) from e
|
@@ -0,0 +1,202 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Any, Callable
|
3
|
+
from orionis.container.enums.lifetimes import Lifetime
|
4
|
+
|
5
|
+
class IContainer(ABC):
|
6
|
+
"""
|
7
|
+
IContainer is an interface that defines the structure for a dependency injection container.
|
8
|
+
It provides methods for registering and resolving services with different lifetimes.
|
9
|
+
"""
|
10
|
+
|
11
|
+
@abstractmethod
|
12
|
+
def singleton(
|
13
|
+
self,
|
14
|
+
abstract: Callable[..., Any],
|
15
|
+
concrete: Callable[..., Any],
|
16
|
+
*,
|
17
|
+
alias: str = None,
|
18
|
+
enforce_decoupling: bool = False
|
19
|
+
) -> bool:
|
20
|
+
"""
|
21
|
+
Register a service as a singleton.
|
22
|
+
|
23
|
+
Parameters
|
24
|
+
----------
|
25
|
+
abstract : Callable[..., Any]
|
26
|
+
The abstract base type or interface to be bound.
|
27
|
+
concrete : Callable[..., Any]
|
28
|
+
The concrete implementation to associate with the abstract type.
|
29
|
+
alias : str, optional
|
30
|
+
An alternative name to register the service under.
|
31
|
+
enforce_decoupling : bool, optional
|
32
|
+
Whether to enforce that concrete is not a subclass of abstract.
|
33
|
+
|
34
|
+
Returns
|
35
|
+
-------
|
36
|
+
bool
|
37
|
+
True if the service was registered successfully.
|
38
|
+
"""
|
39
|
+
pass
|
40
|
+
|
41
|
+
@abstractmethod
|
42
|
+
def transient(
|
43
|
+
self,
|
44
|
+
abstract: Callable[..., Any],
|
45
|
+
concrete: Callable[..., Any],
|
46
|
+
*,
|
47
|
+
alias: str = None,
|
48
|
+
enforce_decoupling: bool = False
|
49
|
+
) -> bool:
|
50
|
+
"""
|
51
|
+
Register a service as transient.
|
52
|
+
|
53
|
+
Parameters
|
54
|
+
----------
|
55
|
+
abstract : Callable[..., Any]
|
56
|
+
The abstract base type or interface to be bound.
|
57
|
+
concrete : Callable[..., Any]
|
58
|
+
The concrete implementation to associate with the abstract type.
|
59
|
+
alias : str, optional
|
60
|
+
An alternative name to register the service under.
|
61
|
+
enforce_decoupling : bool, optional
|
62
|
+
Whether to enforce that concrete is not a subclass of abstract.
|
63
|
+
|
64
|
+
Returns
|
65
|
+
-------
|
66
|
+
bool
|
67
|
+
True if the service was registered successfully.
|
68
|
+
"""
|
69
|
+
pass
|
70
|
+
|
71
|
+
@abstractmethod
|
72
|
+
def scoped(
|
73
|
+
self,
|
74
|
+
abstract: Callable[..., Any],
|
75
|
+
concrete: Callable[..., Any],
|
76
|
+
*,
|
77
|
+
alias: str = None,
|
78
|
+
enforce_decoupling: bool = False
|
79
|
+
) -> bool:
|
80
|
+
"""
|
81
|
+
Register a service as scoped.
|
82
|
+
|
83
|
+
Parameters
|
84
|
+
----------
|
85
|
+
abstract : Callable[..., Any]
|
86
|
+
The abstract base type or interface to be bound.
|
87
|
+
concrete : Callable[..., Any]
|
88
|
+
The concrete implementation to associate with the abstract type.
|
89
|
+
alias : str, optional
|
90
|
+
An alternative name to register the service under.
|
91
|
+
enforce_decoupling : bool, optional
|
92
|
+
Whether to enforce that concrete is not a subclass of abstract.
|
93
|
+
|
94
|
+
Returns
|
95
|
+
-------
|
96
|
+
bool
|
97
|
+
True if the service was registered successfully.
|
98
|
+
"""
|
99
|
+
pass
|
100
|
+
|
101
|
+
@abstractmethod
|
102
|
+
def instance(
|
103
|
+
self,
|
104
|
+
abstract: Callable[..., Any],
|
105
|
+
instance: Any,
|
106
|
+
*,
|
107
|
+
alias: str = None,
|
108
|
+
enforce_decoupling: bool = False
|
109
|
+
) -> bool:
|
110
|
+
"""
|
111
|
+
Register an instance of a service.
|
112
|
+
|
113
|
+
Parameters
|
114
|
+
----------
|
115
|
+
abstract : Callable[..., Any]
|
116
|
+
The abstract class or interface to associate with the instance.
|
117
|
+
instance : Any
|
118
|
+
The concrete instance to register.
|
119
|
+
alias : str, optional
|
120
|
+
An optional alias to register the instance under.
|
121
|
+
enforce_decoupling : bool, optional
|
122
|
+
Whether to enforce that instance's class is not a subclass of abstract.
|
123
|
+
|
124
|
+
Returns
|
125
|
+
-------
|
126
|
+
bool
|
127
|
+
True if the instance was successfully registered.
|
128
|
+
"""
|
129
|
+
pass
|
130
|
+
|
131
|
+
@abstractmethod
|
132
|
+
def function(
|
133
|
+
self,
|
134
|
+
alias: str,
|
135
|
+
fn: Callable[..., Any],
|
136
|
+
*,
|
137
|
+
lifetime: Lifetime = Lifetime.TRANSIENT
|
138
|
+
) -> bool:
|
139
|
+
"""
|
140
|
+
Register a function as a service.
|
141
|
+
|
142
|
+
Parameters
|
143
|
+
----------
|
144
|
+
alias : str
|
145
|
+
The alias to register the function under.
|
146
|
+
fn : Callable[..., Any]
|
147
|
+
The function or factory to register.
|
148
|
+
lifetime : Lifetime, optional
|
149
|
+
The lifetime of the function registration (default is TRANSIENT).
|
150
|
+
|
151
|
+
Returns
|
152
|
+
-------
|
153
|
+
bool
|
154
|
+
True if the function was registered successfully.
|
155
|
+
"""
|
156
|
+
pass
|
157
|
+
|
158
|
+
@abstractmethod
|
159
|
+
def make(
|
160
|
+
self,
|
161
|
+
abstract_or_alias: Any,
|
162
|
+
*args: tuple,
|
163
|
+
**kwargs: dict
|
164
|
+
) -> Any:
|
165
|
+
"""
|
166
|
+
Resolve a service from the container.
|
167
|
+
|
168
|
+
Parameters
|
169
|
+
----------
|
170
|
+
abstract_or_alias : Any
|
171
|
+
The abstract class, interface, or alias (str) to resolve.
|
172
|
+
*args : tuple
|
173
|
+
Positional arguments to pass to the constructor.
|
174
|
+
**kwargs : dict
|
175
|
+
Keyword arguments to pass to the constructor.
|
176
|
+
|
177
|
+
Returns
|
178
|
+
-------
|
179
|
+
Any
|
180
|
+
An instance of the requested service.
|
181
|
+
"""
|
182
|
+
pass
|
183
|
+
|
184
|
+
@abstractmethod
|
185
|
+
def bound(
|
186
|
+
self,
|
187
|
+
abstract_or_alias: Any
|
188
|
+
) -> bool:
|
189
|
+
"""
|
190
|
+
Check if a service is registered in the container.
|
191
|
+
|
192
|
+
Parameters
|
193
|
+
----------
|
194
|
+
abstract_or_alias : Any
|
195
|
+
The abstract class, interface, or alias (str) to check.
|
196
|
+
|
197
|
+
Returns
|
198
|
+
-------
|
199
|
+
bool
|
200
|
+
True if the service is registered, False otherwise.
|
201
|
+
"""
|
202
|
+
pass
|
orionis/metadata/framework.py
CHANGED
@@ -130,6 +130,20 @@ class ReflectionCallable:
|
|
130
130
|
return Coroutine(self.__function(*args, **kwargs)).run()
|
131
131
|
return self.__function(*args, **kwargs)
|
132
132
|
|
133
|
+
def getSignature(self) -> inspect.Signature:
|
134
|
+
"""
|
135
|
+
Retrieve the signature of the callable function.
|
136
|
+
Returns
|
137
|
+
-------
|
138
|
+
inspect.Signature
|
139
|
+
An `inspect.Signature` object representing the callable's signature.
|
140
|
+
Notes
|
141
|
+
-----
|
142
|
+
This method provides detailed information about the parameters of the callable,
|
143
|
+
including their names, default values, and annotations.
|
144
|
+
"""
|
145
|
+
return inspect.signature(self.__function)
|
146
|
+
|
133
147
|
def getDependencies(self) -> CallableDependency:
|
134
148
|
"""
|
135
149
|
Analyzes the callable associated with this instance and retrieves its dependencies.
|
@@ -1442,6 +1442,17 @@ class ReflectionConcrete(IReflectionConcrete):
|
|
1442
1442
|
|
1443
1443
|
return prop.fget.__doc__ if prop.fget else None
|
1444
1444
|
|
1445
|
+
def getConstructorSignature(self) -> inspect.Signature:
|
1446
|
+
"""
|
1447
|
+
Get the signature of the constructor of the instance's class.
|
1448
|
+
|
1449
|
+
Returns
|
1450
|
+
-------
|
1451
|
+
inspect.Signature
|
1452
|
+
The signature of the constructor
|
1453
|
+
"""
|
1454
|
+
return inspect.signature(self._concrete.__init__)
|
1455
|
+
|
1445
1456
|
def getConstructorDependencies(self) -> ClassDependency:
|
1446
1457
|
"""
|
1447
1458
|
Get the resolved and unresolved dependencies from the constructor of the instance's class.
|
@@ -135,7 +135,8 @@ orionis/console/output/console.py,sha256=TE_Hl720ADd82dbERFSWhkoQRukDQZmETSw4nkw
|
|
135
135
|
orionis/console/output/executor.py,sha256=bdvkzW2-buy0BPpy2r5qUGrRFW2Ay6k-5rSeHb0gQ3o,3352
|
136
136
|
orionis/console/output/progress_bar.py,sha256=vFy582z6VJS46LV6tuyrmr9qvdVeTEtw3hyNcEHezeg,3088
|
137
137
|
orionis/console/output/styles.py,sha256=6a4oQCOBOKMh2ARdeq5GlIskJ3wjiylYmh66tUKKmpQ,4053
|
138
|
-
orionis/container/container.py,sha256=
|
138
|
+
orionis/container/container.py,sha256=ZL3S0HyAt8Hm58dPjltNMCSMNNP30VR61ByslzmCGgs,45066
|
139
|
+
orionis/container/contracts/container.py,sha256=LkZ5til_3Um8ctCVTwuO36HkysL59saKwUzXR5cWUBs,5750
|
139
140
|
orionis/container/entities/binding.py,sha256=Qp6Lf4XUDp2NjqXDAC2lzvhOFQWiBDKiGFcKfwb4axw,4342
|
140
141
|
orionis/container/enums/lifetimes.py,sha256=RqQmugMIB1Ev_j_vFLcWorndm-to7xg4stQ7yKFDdDw,190
|
141
142
|
orionis/container/exceptions/container_exception.py,sha256=goTDEwC70xTMD2qppN8KV-xyR0Nps218OD4D1LZ2-3s,470
|
@@ -232,7 +233,7 @@ orionis/foundation/contracts/config.py,sha256=Rpz6U6t8OXHO9JJKSTnCimytXE-tfCB-1i
|
|
232
233
|
orionis/foundation/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
233
234
|
orionis/foundation/exceptions/integrity.py,sha256=mc4pL1UMoYRHEmphnpW2oGk5URhu7DJRREyzHaV-cs8,472
|
234
235
|
orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
235
|
-
orionis/metadata/framework.py,sha256=
|
236
|
+
orionis/metadata/framework.py,sha256=MyXQ31_zvD5irlN_XvuSicVccDNBPb64XCKc8qujCEc,4960
|
236
237
|
orionis/metadata/package.py,sha256=tqLfBRo-w1j_GN4xvzUNFyweWYFS-qhSgAEc-AmCH1M,5452
|
237
238
|
orionis/patterns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
238
239
|
orionis/patterns/singleton/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -259,9 +260,9 @@ orionis/services/introspection/reflection.py,sha256=6z4VkDICohMIkm9jEd7nmFABwVuU
|
|
259
260
|
orionis/services/introspection/abstract/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
260
261
|
orionis/services/introspection/abstract/reflection_abstract.py,sha256=SPK2X11VvGORxxPOYloaD6hPAvky--obRU4CO1DE4zM,43865
|
261
262
|
orionis/services/introspection/callables/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
262
|
-
orionis/services/introspection/callables/reflection_callable.py,sha256=
|
263
|
+
orionis/services/introspection/callables/reflection_callable.py,sha256=0B2B6ZV-ql_aZk97W4Q12MVvi0gwErjekgWaMBxjfV0,6147
|
263
264
|
orionis/services/introspection/concretes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
264
|
-
orionis/services/introspection/concretes/reflection_concrete.py,sha256=
|
265
|
+
orionis/services/introspection/concretes/reflection_concrete.py,sha256=SdE2IWEgt30eO9-f_VRNdIZ_OWWidauQzQZQVrXjcXY,51310
|
265
266
|
orionis/services/introspection/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
266
267
|
orionis/services/introspection/contracts/reflect_dependencies.py,sha256=5fdImZarC1ixoFM-1JSBx28RvYbY3GGZhDGjar7cvHc,1771
|
267
268
|
orionis/services/introspection/contracts/reflection_abstract.py,sha256=-ugpFcAkGTlk0Md5ft8NrvupnlfVji8QZjGYqWBxqeY,22370
|
@@ -344,7 +345,7 @@ orionis/test/suite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
|
|
344
345
|
orionis/test/suite/test_unit.py,sha256=MWgW8dRCRyT1XZ5LsbXQ7-KVPReasoXwzEEL1EWWfE4,52190
|
345
346
|
orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
346
347
|
orionis/test/view/render.py,sha256=jXZkbITBknbUwm_mD8bcTiwLDvsFkrO9qrf0ZgPwqxc,4903
|
347
|
-
orionis-0.
|
348
|
+
orionis-0.319.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
|
348
349
|
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
349
350
|
tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
350
351
|
tests/example/test_example.py,sha256=kvWgiW3ADEZf718dGsMPtDh_rmOSx1ypEInKm7_6ZPQ,601
|
@@ -445,8 +446,8 @@ tests/support/wrapper/test_services_wrapper_docdict.py,sha256=yeVwl-VcwkWSQYyxZu
|
|
445
446
|
tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
446
447
|
tests/testing/test_testing_result.py,sha256=MrGK3ZimedL0b5Ydu69Dg8Iul017AzLTm7VPxpXlpfU,4315
|
447
448
|
tests/testing/test_testing_unit.py,sha256=DjLBtvVn8B1KlVJNNkstBT8_csA1yeaMqnGrbanN_J4,7438
|
448
|
-
orionis-0.
|
449
|
-
orionis-0.
|
450
|
-
orionis-0.
|
451
|
-
orionis-0.
|
452
|
-
orionis-0.
|
449
|
+
orionis-0.319.0.dist-info/METADATA,sha256=426gql1AFezl4n3-_ORGM8wdkUYcEsZr8n3ykl6ruOI,4772
|
450
|
+
orionis-0.319.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
|
451
|
+
orionis-0.319.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
|
452
|
+
orionis-0.319.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
|
453
|
+
orionis-0.319.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|