orionis 0.317.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.
@@ -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,12 +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 inspect
12
- import threading
13
14
 
14
- class Container:
15
+ class Container(IContainer):
15
16
 
16
17
  # Singleton instance of the container.
17
18
  # This is a class variable that holds the single instance of the Container class.
@@ -27,7 +28,11 @@ class Container:
27
28
  # regardless of how many times the class is instantiated.
28
29
  _initialized = False
29
30
 
30
- def __new__(cls, *args, **kwargs):
31
+ def __new__(
32
+ cls,
33
+ *args,
34
+ **kwargs
35
+ ) -> 'Container':
31
36
  """
32
37
  Creates and returns a singleton instance of the class.
33
38
 
@@ -52,24 +57,29 @@ class Container:
52
57
  cls._instance = super(Container, cls).__new__(cls)
53
58
  return cls._instance
54
59
 
55
- def __init__(self):
60
+ def __init__(
61
+ self
62
+ ) -> None:
56
63
  """
57
64
  Initializes a new instance of the container.
58
65
 
59
66
  This constructor sets up the internal dictionaries for bindings and aliases,
60
67
  ensuring that these are only initialized once per class. The initialization
61
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.
62
70
 
63
71
  Notes
64
72
  -----
65
73
  - The `__bindings` dictionary is used to store service bindings.
66
74
  - The `__aliasses` dictionary is used to store service aliases.
67
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.
68
77
  """
69
78
  if not self.__class__._initialized:
70
79
  self.__bindings = {}
71
80
  self.__aliasses = {}
72
81
  self.__class__._initialized = True
82
+ self.instance(IContainer, self)
73
83
 
74
84
  def __dropService(
75
85
  self,
@@ -380,6 +390,48 @@ class Container:
380
390
  "Please ensure that all abstract methods are implemented."
381
391
  )
382
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
+
383
435
  def transient(
384
436
  self,
385
437
  abstract: Callable[..., Any],
@@ -716,7 +768,7 @@ class Container:
716
768
  def function(
717
769
  self,
718
770
  alias: str,
719
- function: Callable[..., Any],
771
+ fn: Callable[..., Any],
720
772
  *,
721
773
  lifetime: Lifetime = Lifetime.TRANSIENT
722
774
  ) -> bool:
@@ -727,7 +779,7 @@ class Container:
727
779
  ----------
728
780
  alias : str
729
781
  The alias to register the function under.
730
- function : Callable[..., Any]
782
+ fn : Callable[..., Any]
731
783
  The function or factory to register.
732
784
  lifetime : Lifetime, optional
733
785
  The lifetime of the function registration (default is TRANSIENT).
@@ -764,10 +816,10 @@ class Container:
764
816
  self.__ensureAliasType(alias)
765
817
 
766
818
  # Validate that the function is callable
767
- self.__ensureIsCallable(function)
819
+ self.__ensureIsCallable(fn)
768
820
 
769
821
  # Inspect the function signature
770
- params = ReflectionCallable(function).getDependencies()
822
+ params = ReflectionCallable(fn).getDependencies()
771
823
 
772
824
  # If the function requires arguments, only allow TRANSIENT
773
825
  if (len(params.resolved) + len(params.unresolved)) > 0 and lifetime != Lifetime.TRANSIENT:
@@ -780,7 +832,7 @@ class Container:
780
832
 
781
833
  # Register the function with the specified alias and lifetime
782
834
  self.__bindings[alias] = Binding(
783
- function=function,
835
+ function=fn,
784
836
  lifetime=lifetime,
785
837
  alias=alias
786
838
  )
@@ -820,8 +872,8 @@ class Container:
820
872
  def make(
821
873
  self,
822
874
  abstract_or_alias: Any,
823
- *args,
824
- **kwargs
875
+ *args: tuple,
876
+ **kwargs: dict
825
877
  ) -> Any:
826
878
  """
827
879
  Resolves and returns an instance of the requested service.
@@ -845,11 +897,341 @@ class Container:
845
897
  OrionisContainerException
846
898
  If the requested service is not registered in the container.
847
899
  """
848
- if not self.bound(abstract_or_alias):
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:
849
905
  raise OrionisContainerException(
850
906
  f"The requested service '{abstract_or_alias}' is not registered in the container."
851
907
  )
852
908
 
853
- binding = self.__bindings.get(abstract_or_alias) or self.__aliasses.get(abstract_or_alias)
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.
854
1019
 
855
- print(binding)
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
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.317.0"
8
+ VERSION = "0.319.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -1,10 +1,9 @@
1
1
  import inspect
2
2
  from orionis.services.asynchrony.coroutines import Coroutine
3
- from orionis.services.introspection.dependencies.entities.method_dependencies import MethodDependency as CallableDependency
3
+ from orionis.services.introspection.dependencies.entities.callable_dependencies import CallableDependency
4
4
  from orionis.services.introspection.dependencies.reflect_dependencies import ReflectDependencies
5
5
  from orionis.services.introspection.exceptions.reflection_attribute_error import ReflectionAttributeError
6
6
  from orionis.services.introspection.exceptions.reflection_type_error import ReflectionTypeError
7
- import asyncio
8
7
 
9
8
  class ReflectionCallable:
10
9
 
@@ -131,6 +130,20 @@ class ReflectionCallable:
131
130
  return Coroutine(self.__function(*args, **kwargs)).run()
132
131
  return self.__function(*args, **kwargs)
133
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
+
134
147
  def getDependencies(self) -> CallableDependency:
135
148
  """
136
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.
@@ -0,0 +1,55 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, Dict, List
3
+ from orionis.services.introspection.dependencies.entities.resolved_dependencies import ResolvedDependency
4
+ from orionis.services.introspection.exceptions.reflection_type_error import ReflectionTypeError
5
+
6
+ @dataclass(frozen=True, kw_only=True)
7
+ class CallableDependency:
8
+ """
9
+ Represents the dependencies of a callable, separating resolved and unresolved dependencies.
10
+
11
+ Parameters
12
+ ----------
13
+ resolved : Dict[ResolvedDependency, Any]
14
+ A dictionary mapping resolved dependency descriptors to their corresponding
15
+ resolved instances or values for the method. All keys must be ResolvedDependency instances.
16
+ unresolved : List[str]
17
+ A list of method parameter names or dependency identifiers that could not be resolved.
18
+ Must contain only non-empty strings.
19
+
20
+ Raises
21
+ ------
22
+ ReflectionTypeError
23
+ If types don't match the expected:
24
+ - resolved: Dict[ResolvedDependency, Any]
25
+ - unresolved: List[str]
26
+ ValueError
27
+ If resolved contains None keys or unresolved contains empty strings
28
+ """
29
+ resolved: Dict[ResolvedDependency, Any]
30
+ unresolved: List[str]
31
+
32
+ def __post_init__(self):
33
+ """
34
+ Validates types and values of attributes during initialization.
35
+
36
+ Raises
37
+ ------
38
+ ReflectionTypeError
39
+ If types don't match the expected:
40
+ - resolved: Dict[ResolvedDependency, Any]
41
+ - unresolved: List[str]
42
+ ValueError
43
+ If resolved contains None keys or unresolved contains empty strings
44
+ """
45
+ # Validate 'resolved' is a dict with proper key types
46
+ if not isinstance(self.resolved, dict):
47
+ raise ReflectionTypeError(
48
+ f"'resolved' must be a dict, got {type(self.resolved).__name__}"
49
+ )
50
+
51
+ # Validate 'unresolved' is a list of valid parameter names
52
+ if not isinstance(self.unresolved, list):
53
+ raise ReflectionTypeError(
54
+ f"'unresolved' must be a list, got {type(self.unresolved).__name__}"
55
+ )
@@ -1,6 +1,7 @@
1
1
  import inspect
2
2
  from typing import Any, Dict, List
3
3
  from orionis.services.introspection.contracts.reflect_dependencies import IReflectDependencies
4
+ from orionis.services.introspection.dependencies.entities.callable_dependencies import CallableDependency
4
5
  from orionis.services.introspection.dependencies.entities.class_dependencies import ClassDependency
5
6
  from orionis.services.introspection.dependencies.entities.method_dependencies import MethodDependency
6
7
  from orionis.services.introspection.dependencies.entities.resolved_dependencies import ResolvedDependency
@@ -176,7 +177,7 @@ class ReflectDependencies(IReflectDependencies):
176
177
  unresolved=unresolved_dependencies
177
178
  )
178
179
 
179
- def getCallableDependencies(self, fn: callable) -> MethodDependency:
180
+ def getCallableDependencies(self, fn: callable) -> CallableDependency:
180
181
  """
181
182
  Get the resolved and unresolved dependencies from a callable function.
182
183
 
@@ -222,7 +223,7 @@ class ReflectDependencies(IReflectDependencies):
222
223
  full_class_path=f"{module_path}.{param.annotation.__name__}"
223
224
  )
224
225
 
225
- return MethodDependency(
226
+ return CallableDependency(
226
227
  resolved=resolved_dependencies,
227
228
  unresolved=unresolved_dependencies
228
229
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.317.0
3
+ Version: 0.319.0
4
4
  Summary: Orionis Framework – Elegant, Fast, and Powerful.
5
5
  Home-page: https://github.com/orionis-framework/framework
6
6
  Author: Raul Mauricio Uñate Castro
@@ -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=BPBO0i1EgL-9g9sgDotrInM2UiuDMT7TNDRHDdrQCZo,30121
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=IhaXribsZwgUQ1ZD4sXOCEanSM6ZfeiOASiDyOpzViA,4960
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=AWTw9T9DGpCscwvrqYtYAYdTeoJqIHzdRDS29Sb5cSI,5667
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=1GxD-y8LPGL6kI4Y3XbeLcFjR5Y8cOqbVEPCtPnsuYA,50982
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
@@ -269,8 +270,9 @@ orionis/services/introspection/contracts/reflection_concrete.py,sha256=9ZQjJpZwv
269
270
  orionis/services/introspection/contracts/reflection_instance.py,sha256=D9sH-uOSZ_E7luAfbjI_ML6kfxuO5MtvLk6037iQJ7o,20936
270
271
  orionis/services/introspection/contracts/reflection_module.py,sha256=YLqKg5EhaddUBrytMHX1-uz9mNsRISK1iVyG_iUiVYA,9666
271
272
  orionis/services/introspection/dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
272
- orionis/services/introspection/dependencies/reflect_dependencies.py,sha256=xjY9RRBbALfifoefUuHu2EU5XUNdV9zKFaJ9-x7TP2I,9303
273
+ orionis/services/introspection/dependencies/reflect_dependencies.py,sha256=OXMfWqacFM7Mo5y0zmPprP4ECHqImChDFsfzTyhqS9A,9414
273
274
  orionis/services/introspection/dependencies/entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
275
+ orionis/services/introspection/dependencies/entities/callable_dependencies.py,sha256=GJPS1pO8aIhYjtYw7bEoV8WfUCn-ZPGt5mD1WvfoAxg,2198
274
276
  orionis/services/introspection/dependencies/entities/class_dependencies.py,sha256=pALvV_duAvDYmNp7PJYWkpIIQYmqWxuc_RGruEckfPA,2063
275
277
  orionis/services/introspection/dependencies/entities/method_dependencies.py,sha256=FDwroILMPhqPxaxisPVEeKeLUg57GNQ4tQfWjGMh40E,2194
276
278
  orionis/services/introspection/dependencies/entities/resolved_dependencies.py,sha256=0qnEj-3H8iclCc79AduQrqAAdAihv7k39gipo3RT2zc,2216
@@ -343,7 +345,7 @@ orionis/test/suite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
343
345
  orionis/test/suite/test_unit.py,sha256=MWgW8dRCRyT1XZ5LsbXQ7-KVPReasoXwzEEL1EWWfE4,52190
344
346
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
345
347
  orionis/test/view/render.py,sha256=jXZkbITBknbUwm_mD8bcTiwLDvsFkrO9qrf0ZgPwqxc,4903
346
- orionis-0.317.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
348
+ orionis-0.319.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
347
349
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
348
350
  tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
349
351
  tests/example/test_example.py,sha256=kvWgiW3ADEZf718dGsMPtDh_rmOSx1ypEInKm7_6ZPQ,601
@@ -414,14 +416,14 @@ tests/services/environment/test_services_environment.py,sha256=fdkjwbY-aDEA1FT-9
414
416
  tests/services/inspection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
415
417
  tests/services/inspection/test_reflection.py,sha256=ZApQeaDxYLsrfGN6UqEDPbyNzocMV9CURflQ35YMfqk,13678
416
418
  tests/services/inspection/dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
417
- tests/services/inspection/dependencies/test_reflect_dependencies.py,sha256=xjcbfWlCT-QuI0wDgXbbsfvWG5phvrvPcfMOwmUnpHQ,7279
419
+ tests/services/inspection/dependencies/test_reflect_dependencies.py,sha256=s_P4ST_dmjzRKmUL4bPFs-oR-Mf5PENGmYk56WiGO9g,7388
418
420
  tests/services/inspection/dependencies/mocks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
419
421
  tests/services/inspection/dependencies/mocks/mock_user.py,sha256=RxATxe0-Vm4HfX5jKz9Tny42E2fmrdtEN6ZEntbqRL8,912
420
422
  tests/services/inspection/dependencies/mocks/mock_user_controller.py,sha256=P3sOUXVZ55auudwiNtvNCEQuTz0cgAZjvhicLZ4xaz4,1208
421
423
  tests/services/inspection/dependencies/mocks/mock_users_permissions.py,sha256=oENXbS2qmQUudYSmnhB8fgHBqXZdbplplB-Y2nbx4hw,1388
422
424
  tests/services/inspection/reflection/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
423
425
  tests/services/inspection/reflection/test_reflection_abstract.py,sha256=MbGDfCDkfj8wVJcyAlwV6na98JLzbGaYa3QjXR1alL8,27395
424
- tests/services/inspection/reflection/test_reflection_callable.py,sha256=GBZL9oIwfBDfhQ3VXVrZDkdwoTkymMqLfFQSBCSs8pE,7021
426
+ tests/services/inspection/reflection/test_reflection_callable.py,sha256=ZcZ1_v4Nv22gIleflCRzE0Kfwy5Kjj8XjZ9Z1cKdXt8,6855
425
427
  tests/services/inspection/reflection/test_reflection_concrete.py,sha256=5-iQh1whfpBa47jBWwtg-MIk6ysg92my5J9JdrTBm5E,44622
426
428
  tests/services/inspection/reflection/test_reflection_instance.py,sha256=ZCFTLY_KtLAIq58PuDWak-T1c2PcCKiwTOdI9EDubww,46281
427
429
  tests/services/inspection/reflection/test_reflection_module.py,sha256=Cl-3kWoJMQ2ufOO4VP6M28Tk6kmY4OhVEoW_b0wqw7Y,19849
@@ -444,8 +446,8 @@ tests/support/wrapper/test_services_wrapper_docdict.py,sha256=yeVwl-VcwkWSQYyxZu
444
446
  tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
445
447
  tests/testing/test_testing_result.py,sha256=MrGK3ZimedL0b5Ydu69Dg8Iul017AzLTm7VPxpXlpfU,4315
446
448
  tests/testing/test_testing_unit.py,sha256=DjLBtvVn8B1KlVJNNkstBT8_csA1yeaMqnGrbanN_J4,7438
447
- orionis-0.317.0.dist-info/METADATA,sha256=ArEDpHbIszs0kFP63nOxlOuuEJygJsbjqGR8xCRQ9gM,4772
448
- orionis-0.317.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
449
- orionis-0.317.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
450
- orionis-0.317.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
451
- orionis-0.317.0.dist-info/RECORD,,
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,,
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ from orionis.services.introspection.dependencies.entities.callable_dependencies import CallableDependency
2
3
  from orionis.services.introspection.dependencies.reflect_dependencies import (
3
4
  ReflectDependencies,
4
5
  ClassDependency,
@@ -113,7 +114,7 @@ class TestReflectDependencies(TestCase):
113
114
  callable_dependencies = depend.getCallableDependencies(fake_function)
114
115
 
115
116
  # Check Instance of MethodDependency
116
- self.assertIsInstance(callable_dependencies, MethodDependency)
117
+ self.assertIsInstance(callable_dependencies, CallableDependency)
117
118
 
118
119
  # Check unresolved dependencies
119
120
  self.assertEqual(callable_dependencies.unresolved, [])
@@ -1,9 +1,7 @@
1
1
  from orionis.services.introspection.callables.reflection_callable import ReflectionCallable
2
+ from orionis.services.introspection.dependencies.entities.callable_dependencies import CallableDependency
2
3
  from orionis.unittesting import TestCase
3
4
  from orionis.services.introspection.exceptions.reflection_type_error import ReflectionTypeError
4
- from orionis.services.introspection.exceptions.reflection_attribute_error import ReflectionAttributeError
5
- from orionis.services.introspection.dependencies.entities.method_dependencies import MethodDependency as CallableDependency
6
- from orionis.services.introspection.dependencies.reflect_dependencies import ReflectDependencies
7
5
 
8
6
  class TestReflectionCallable(TestCase):
9
7
 
@@ -153,5 +151,6 @@ class TestReflectionCallable(TestCase):
153
151
  return a + b
154
152
  rc = ReflectionCallable(sample_function)
155
153
  deps = rc.getDependencies()
154
+ self.assertIsInstance(deps, CallableDependency)
156
155
  self.assertTrue(hasattr(deps, "resolved"))
157
156
  self.assertTrue(hasattr(deps, "unresolved"))