orionis 0.21.0__py3-none-any.whl → 0.23.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/framework.py CHANGED
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.21.0"
8
+ VERSION = "0.23.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -28,7 +28,7 @@ class Register(IRegister):
28
28
  """
29
29
  self.cache = cache
30
30
 
31
- def config(self, config_class: type) -> type:
31
+ def config(self, config_class: type) -> None:
32
32
  """
33
33
  Registers a configuration class and ensures it meets the required structure.
34
34
 
@@ -1,385 +1,420 @@
1
1
  import inspect
2
- from typing import Any, Callable
2
+ from collections import deque
3
+ from threading import Lock
4
+ from typing import Callable, Any, Dict
5
+ from orionis.luminate.container.exception import OrionisContainerException, OrionisContainerValueError, OrionisContainerTypeError
3
6
  from orionis.luminate.container.types import Types
4
- from orionis.luminate.tools.dot_dict import DotDict
5
7
  from orionis.luminate.contracts.container.container_interface import IContainer
6
- from orionis.luminate.container.exception import OrionisContainerException, OrionisContainerValueError
8
+
9
+ BINDING = 'binding'
10
+ TRANSIENT = 'transient'
11
+ SINGLETON = 'singleton'
12
+ SCOPED = 'scoped'
13
+ INSTANCE = 'instance'
7
14
 
8
15
  class Container(IContainer):
9
- """Service container and dependency injection."""
16
+ """
17
+ Service container and dependency injection manager.
18
+
19
+ This class follows the singleton pattern to manage service bindings, instances,
20
+ and different lifecycle types such as transient, singleton, and scoped.
21
+ """
10
22
 
11
23
  _instance = None
24
+ _lock = Lock()
12
25
 
13
26
  def __new__(cls):
14
27
  if cls._instance is None:
15
- cls._instance = super().__new__(cls)
16
- cls._config = {}
17
-
18
- cls._instance._bindings = {}
19
- cls._instance._transients = {}
20
- cls._instance._singletons = {}
21
- cls._instance._scoped_services = {}
22
- cls._instance._instances = {}
23
- cls._instance._aliases = {}
24
- cls._instance._scoped_instances = {}
25
- cls._instance._conditional_bindings = {}
26
- # Initialize the PrimitiveTypes validator
27
- cls._instance._primitive_types_validator = Types()
28
+ with cls._lock:
29
+ if cls._instance is None:
30
+ cls._instance = super().__new__(cls)
31
+ cls._instance._bindings = {}
32
+ cls._instance._transients = {}
33
+ cls._instance._singletons = {}
34
+ cls._instance._scoped_services = {}
35
+ cls._instance._instances = {}
36
+ cls._instance._aliases = {}
37
+ cls._instance._scoped_instances = {}
38
+ cls._instance._validate_types = Types()
28
39
  return cls._instance
29
40
 
30
- def config(self, section: str, data: dict):
41
+ def _newRequest(self) -> None:
31
42
  """
32
- Registers a configuration section in the container.
33
-
34
- Args:
35
- section (str): _description_
36
- data (dict): _description_
43
+ Reset scoped instances at the beginning of a new request.
37
44
  """
45
+ self._scoped_instances = {}
38
46
 
39
- # Validate that data is a dictionary
40
- if not isinstance(data, dict):
41
- raise OrionisContainerValueError("The 'data' parameter must be a dictionary.")
42
-
43
- # Validate that the section is a string
44
- if not isinstance(section, str):
45
- raise OrionisContainerValueError("The 'section' parameter must be a string.")
46
-
47
- if section not in self._config:
48
- self._config[section] = data
49
- else:
50
- self._config[section].update(data)
47
+ def _ensureNotMain(self, concrete: Callable[..., Any]) -> str:
48
+ """
49
+ Ensure that a class is not defined in the main script.
50
+
51
+ Parameters
52
+ ----------
53
+ concrete : Callable[..., Any]
54
+ The class or function to check.
55
+
56
+ Returns
57
+ -------
58
+ str
59
+ The fully qualified name of the class.
60
+
61
+ Raises
62
+ ------
63
+ OrionisContainerValueError
64
+ If the class is defined in the main module.
65
+ """
66
+ if concrete.__module__ == "__main__":
67
+ raise OrionisContainerValueError(
68
+ "Cannot register a class from the main module in the container."
69
+ )
70
+ return f"{concrete.__module__}.{concrete.__name__}"
51
71
 
52
- def bind(self, abstract: str, concrete: Callable[..., Any]) -> None:
53
- """Registers a service with a specific implementation.
72
+ def _ensureUniqueService(self, obj: Any) -> None:
73
+ """
74
+ Ensure that a service is not already registered.
54
75
 
55
- Args:
56
- abstract (str): Name or key of the service to register.
57
- concrete (Callable[..., Any]): Concrete implementation of the service.
76
+ Parameters
77
+ ----------
78
+ obj : Any
79
+ The service to check.
58
80
 
59
- Raises:
60
- OrionisContainerException: If the service is already registered.
61
- TypeError: If the implementation is not a callable or instantiable class.
81
+ Raises
82
+ ------
83
+ OrionisContainerValueError
84
+ If the service is already registered.
62
85
  """
86
+ if self.has(obj):
87
+ raise OrionisContainerValueError("The service is already registered in the container.")
63
88
 
64
- # Validate that the abstract name is not a primitive type
65
- self._primitiveTypeValidator(abstract)
89
+ def _ensureIsCallable(self, concrete: Callable[..., Any]) -> None:
90
+ """
91
+ Ensure that the given implementation is callable or instantiable.
66
92
 
67
- if self.has(abstract):
68
- raise OrionisContainerException(f"The service '{abstract}' is already registered in the container.")
93
+ Parameters
94
+ ----------
95
+ concrete : Callable[..., Any]
96
+ The implementation to check.
69
97
 
98
+ Raises
99
+ ------
100
+ OrionisContainerTypeError
101
+ If the implementation is not callable.
102
+ """
70
103
  if not callable(concrete):
71
- raise TypeError(f"The implementation of '{abstract}' must be a callable or instantiable class.")
72
-
73
- self._bindings[abstract] = concrete
74
-
75
- def transient(self, abstract: str, concrete: Callable[..., Any]) -> None:
76
- """Registers a service as Transient, creating a new instance on each request.
104
+ raise OrionisContainerTypeError(
105
+ f"The implementation '{str(concrete)}' must be callable or an instantiable class."
106
+ )
77
107
 
78
- Args:
79
- abstract (str): Name or key of the service to register.
80
- concrete (Callable[..., Any]): Concrete implementation of the service.
81
-
82
- Raises:
83
- OrionisContainerException: If the service is already registered.
84
- TypeError: If the implementation is not a callable or instantiable class.
108
+ def _ensureIsInstance(self, instance: Any) -> None:
85
109
  """
110
+ Ensure that the given instance is a valid object.
86
111
 
87
- # Validate that the abstract name is not a primitive type
88
- self._primitiveTypeValidator(abstract)
112
+ Parameters
113
+ ----------
114
+ instance : Any
115
+ The instance to check.
89
116
 
90
- if self.has(abstract):
91
- raise OrionisContainerException(f"The service '{abstract}' is already registered in the container.")
92
-
93
- if not callable(concrete):
94
- raise TypeError(f"The implementation of '{abstract}' must be a callable or instantiable class.")
95
-
96
- self._transients[abstract] = concrete
97
-
98
- def singleton(self, abstract: str, concrete: Callable[..., Any]) -> None:
99
- """Registers a service as Singleton, ensuring a single shared instance.
117
+ Raises
118
+ ------
119
+ OrionisContainerValueError
120
+ If the instance is not a valid object.
121
+ """
122
+ if not isinstance(instance, object) or instance.__class__.__module__ in ['builtins', 'abc']:
123
+ raise OrionisContainerValueError(
124
+ f"The instance '{str(instance)}' must be a valid object."
125
+ )
100
126
 
127
+ def bind(self, concrete: Callable[..., Any]) -> str:
128
+ """
129
+ Bind a callable to the container.
130
+ This method ensures that the provided callable is not the main function,
131
+ is unique within the container, and is indeed callable. It then creates
132
+ a unique key for the callable based on its module and name, and stores
133
+ the callable in the container's bindings.
101
134
  Args:
102
- abstract (str): Name or key of the service to register.
103
- concrete (Callable[..., Any]): Concrete implementation of the service.
104
-
105
- Raises:
106
- OrionisContainerException: If the service is already registered.
107
- TypeError: If the implementation is not a callable or instantiable class.
135
+ concrete (Callable[..., Any]): The callable to be bound to the container.
136
+ Returns:
137
+ str: The unique key generated for the callable.
108
138
  """
139
+ self._ensureNotMain(concrete)
140
+ self._ensureUniqueService(concrete)
141
+ self._ensureIsCallable(concrete)
109
142
 
110
- # Validate that the abstract name is not a primitive type
111
- self._primitiveTypeValidator(abstract)
112
-
113
- if self.has(abstract):
114
- raise OrionisContainerException(f"The service '{abstract}' is already registered in the container.")
115
-
116
- if not callable(concrete):
117
- raise TypeError(f"The implementation of '{abstract}' must be a callable or instantiable class.")
118
-
119
- self._singletons[abstract] = concrete
143
+ key = f"{concrete.__module__}.{concrete.__name__}"
144
+ self._bindings[key] = {
145
+ 'callback': concrete,
146
+ 'module': concrete.__module__,
147
+ 'name': concrete.__name__,
148
+ 'type': BINDING
149
+ }
120
150
 
121
- def scoped(self, abstract: str, concrete: Callable[..., Any]) -> None:
122
- """Registers a service as Scoped, shared within the same request.
151
+ return key
123
152
 
153
+ def transient(self, concrete: Callable[..., Any]) -> str:
154
+ """
155
+ Registers a transient service in the container.
156
+ A transient service is created each time it is requested.
124
157
  Args:
125
- abstract (str): Name or key of the service to register.
126
- concrete (Callable[..., Any]): Concrete implementation of the service.
127
-
128
- Raises:
129
- OrionisContainerException: If the service is already registered.
130
- TypeError: If the implementation is not a callable or instantiable class.
158
+ concrete (Callable[..., Any]): The callable that defines the service.
159
+ Returns:
160
+ str: The unique key generated for the callable.
131
161
  """
162
+ self._ensureNotMain(concrete)
163
+ self._ensureUniqueService(concrete)
164
+ self._ensureIsCallable(concrete)
132
165
 
133
- # Validate that the abstract name is not a primitive type
134
- self._primitiveTypeValidator(abstract)
166
+ key = f"{concrete.__module__}.{concrete.__name__}"
167
+ self._transients[key] = {
168
+ 'callback': concrete,
169
+ 'module': concrete.__module__,
170
+ 'name': concrete.__name__,
171
+ 'type': TRANSIENT
172
+ }
135
173
 
136
- if self.has(abstract):
137
- raise OrionisContainerException(f"The service '{abstract}' is already registered in the container.")
138
-
139
- if not callable(concrete):
140
- raise TypeError(f"The implementation of '{abstract}' must be a callable or instantiable class.")
141
-
142
- self._scoped_services[abstract] = concrete
143
-
144
- def instance(self, abstract: str, instance: Any) -> None:
145
- """Registers a specific instance in the container, allowing it to be reused.
174
+ return key
146
175
 
176
+ def singleton(self, concrete: Callable[..., Any]) -> str:
177
+ """
178
+ Registers a callable as a singleton in the container.
179
+ This method ensures that the provided callable is not the main module,
180
+ is unique within the container, and is indeed callable. It then registers
181
+ the callable as a singleton, storing it in the container's singleton registry.
147
182
  Args:
148
- abstract (str): Name or key of the service to register.
149
- instance (Any): Specific instance of the service to register.
150
-
151
- Raises:
152
- OrionisContainerException: If the instance is already registered.
153
- ValueError: If the provided instance is of an unexpected or invalid type.
183
+ concrete (Callable[..., Any]): The callable to be registered as a singleton.
184
+ Returns:
185
+ str: The key under which the singleton is registered in the container.
154
186
  """
187
+ self._ensureNotMain(concrete)
188
+ self._ensureUniqueService(concrete)
189
+ self._ensureIsCallable(concrete)
155
190
 
156
- # Validate that the abstract name is not a primitive type
157
- self._primitiveTypeValidator(abstract)
158
-
159
- if abstract in self._instances:
160
- raise OrionisContainerException(f"The instance '{abstract}' is already registered in the container.")
161
-
162
- if not isinstance(instance, object):
163
- raise ValueError(f"The instance of '{abstract}' must be a valid object.")
164
-
165
- self._instances[abstract] = instance
191
+ key = f"{concrete.__module__}.{concrete.__name__}"
192
+ self._singletons[key] = {
193
+ 'callback': concrete,
194
+ 'module': concrete.__module__,
195
+ 'name': concrete.__name__,
196
+ 'type': SINGLETON
197
+ }
166
198
 
167
- def has(self, abstract: str) -> bool:
168
- """Checks if a service is registered in the container.
199
+ return key
169
200
 
170
- Args:
171
- abstract (str): Name or key of the service to check.
172
-
173
- Returns:
174
- bool: True if the service is registered, False otherwise.
175
-
176
- Raises:
177
- ValueError: If the service name (abstract) is not a valid string.
201
+ def scoped(self, concrete: Callable[..., Any]) -> str:
178
202
  """
179
- # Check that 'abstract' is a string
180
- if not isinstance(abstract, str):
181
- raise ValueError(f"The service name '{abstract}' must be a string.")
182
-
183
- # Efficient check if the service is in any of the containers
184
- return any(abstract in container for container in [
185
- self._bindings,
186
- self._transients,
187
- self._singletons,
188
- self._scoped_services,
189
- self._instances
190
- ])
191
-
192
- def alias(self, abstract: str, alias: str) -> None:
193
- """Creates an alias for a registered service, allowing access to the service using an alternative name.
194
-
203
+ Registers a callable as a scoped service.
204
+ This method ensures that the provided callable is not the main service,
205
+ is unique, and is indeed callable. It then registers the callable in the
206
+ scoped services dictionary with relevant metadata.
195
207
  Args:
196
- abstract (str): Name or key of the original service.
197
- alias (str): The alias to assign to the service.
198
-
199
- Raises:
200
- OrionisContainerException: If the original service is not registered.
201
- ValueError: If the alias is not a valid string or is already in use.
208
+ concrete (Callable[..., Any]): The callable to be registered as a scoped service.
209
+ Returns:
210
+ str: The key under which the callable is registered in the scoped services dictionary.
202
211
  """
212
+ self._ensureNotMain(concrete)
213
+ self._ensureUniqueService(concrete)
214
+ self._ensureIsCallable(concrete)
203
215
 
204
- # Validate that the abstract name is not a primitive type
205
- self._primitiveTypeValidator(abstract)
206
-
207
- # Validate alias type
208
- if not isinstance(alias, str) or not alias:
209
- raise ValueError("The alias must be a non-empty string.")
210
-
211
- # Check if the original service is registered
212
- if not self.has(abstract):
213
- raise OrionisContainerException(f"The service '{abstract}' is not registered in the container.")
214
-
215
- # Check if the alias is already in use
216
- if alias in self._aliases:
217
- raise ValueError(f"The alias '{alias}' is already in use.")
216
+ key = f"{concrete.__module__}.{concrete.__name__}"
217
+ self._scoped_services[key] = {
218
+ 'callback': concrete,
219
+ 'module': concrete.__module__,
220
+ 'name': concrete.__name__,
221
+ 'type': SCOPED
222
+ }
218
223
 
219
- self._aliases[alias] = abstract
220
-
221
- def make(self, abstract: str):
222
- """Automatically resolves a dependency, handling instances, singletons, scoped, transients, and aliases.
223
-
224
- This method resolves the dependencies of a service and handles the following service types:
225
- 1. **Instances**: Returns a specific instance.
226
- 2. **Singletons**: Returns the same unique instance each time.
227
- 3. **Scoped**: Returns a shared instance within the same request.
228
- 4. **Transients**: Creates a new instance each time.
229
- 5. **Aliases**: Resolves an alias to the original service.
224
+ return key
230
225
 
226
+ def instance(self, instance: Any) -> str:
227
+ """
228
+ Registers an instance as a singleton in the container.
231
229
  Args:
232
- abstract (str): Name or key of the service to resolve.
233
-
230
+ instance (Any): The instance to be registered as a singleton.
234
231
  Returns:
235
- Any: The resolved instance or service.
236
-
232
+ str: The key under which the instance is registered in the container.
233
+ """
234
+ self._ensureNotMain(instance.__class__)
235
+ self._ensureUniqueService(instance)
236
+ self._ensureIsInstance(instance)
237
+
238
+ concrete = instance.__class__
239
+ key = f"{concrete.__module__}.{concrete.__name__}"
240
+ self._instances[key] = {
241
+ 'instance': instance,
242
+ 'module': concrete.__module__,
243
+ 'name': concrete.__name__,
244
+ 'type': INSTANCE
245
+ }
246
+
247
+ return key
248
+
249
+ def alias(self, alias: str, concrete: Any) -> None:
250
+ """
251
+ Creates an alias for a registered service.
252
+ Args:
253
+ alias (str): The alias name to be used for the service.
254
+ concrete (Any): The actual service instance or callable to be aliased.
237
255
  Raises:
238
- OrionisContainerException: If the service is not found.
256
+ OrionisContainerException: If the concrete instance is not a valid object or if the alias is a primitive type.
239
257
  """
240
- # If the service is a specific instance, return it directly
241
- if abstract in self._instances:
242
- return self._instances[abstract]
258
+ if not callable(concrete) and not isinstance(concrete, object):
259
+ raise OrionisContainerException(f"The instance '{str(concrete)}' must be a valid object.")
243
260
 
244
- # If it is a singleton, return the same instance or resolve it if it is not yet resolved
245
- if abstract in self._singletons:
246
- if abstract not in self._instances:
247
- self._instances[abstract] = self._resolve(self._singletons[abstract])
248
- return self._instances[abstract]
261
+ if self._instance._validate_types.isPrimitive(alias):
262
+ raise OrionisContainerException(f"Cannot use primitive type '{alias}' as an alias.")
249
263
 
250
- # If it is a scoped service, validate that it is in the same request context
251
- if abstract in self._scoped_services:
252
- if abstract not in self._scoped_instances:
253
- self._scoped_instances[abstract] = self._resolve(self._scoped_services[abstract])
254
- return self._scoped_instances[abstract]
264
+ if isinstance(concrete, object) and concrete.__class__.__module__ not in ['builtins', 'abc']:
265
+ cls_concrete = concrete.__class__
266
+ current_key = f"{cls_concrete.__module__}.{cls_concrete.__name__}"
267
+ elif callable(concrete):
268
+ current_key = f"{concrete.__module__}.{concrete.__name__}"
255
269
 
256
- # If it is a transient service, create a new instance each time
257
- if abstract in self._transients:
258
- return self._resolve(self._transients[abstract])
270
+ self._aliases[alias] = current_key
259
271
 
260
- # If it is a regular binding, resolve it directly
261
- if abstract in self._bindings:
262
- return self._resolve(self._bindings[abstract])
272
+ def has(self, obj: Any) -> bool:
273
+ """
274
+ Checks if a service is registered in the container.
263
275
 
264
- # If it is an alias, resolve the alias recursively
265
- if abstract in self._aliases:
266
- return self.make(self._aliases[abstract])
276
+ Parameters
277
+ ----------
278
+ obj : Any
279
+ The service class, instance, or alias to check.
267
280
 
268
- raise OrionisContainerValueError(f"No definition found for '{abstract}'. Ensure the service is registered.")
281
+ Returns
282
+ -------
283
+ bool
284
+ True if the service is registered, False otherwise.
285
+ """
286
+ if isinstance(obj, str):
287
+ return obj in self._aliases or obj in (
288
+ self._bindings | self._transients | self._singletons | self._scoped_services | self._instances
289
+ )
269
290
 
270
- def call(self, instance: Any, method_name: str, **overrides):
271
- """Llama a un método del objeto resolviendo automáticamente las dependencias registradas.
291
+ if isinstance(obj, object) and obj.__class__.__module__ not in {'builtins', 'abc'}:
292
+ key = f"{obj.__class__.__module__}.{obj.__class__.__name__}"
293
+ return key in self._instances
272
294
 
273
- Args:
274
- instance (Any): Instancia del objeto en el cual se ejecutará el método.
275
- method_name (str): Nombre del método a llamar.
276
- **overrides: Argumentos que se deben pasar manualmente en lugar de resolverlos automáticamente.
295
+ if callable(obj):
296
+ key = f"{obj.__module__}.{obj.__name__}"
297
+ return key in (
298
+ self._bindings | self._transients | self._singletons | self._scoped_services | self._aliases
299
+ )
277
300
 
278
- Returns:
279
- Any: El resultado de ejecutar el método con las dependencias resueltas.
301
+ return False
280
302
 
281
- Raises:
282
- AttributeError: Si el método no existe en la instancia.
303
+ def make(self, abstract: Any) -> Any:
283
304
  """
284
- if not hasattr(instance, method_name):
285
- raise AttributeError(f"'{instance.__class__.__name__}' has no method '{method_name}'")
286
-
287
- method = getattr(instance, method_name)
288
- signature = inspect.signature(method)
289
- dependencies = {}
290
-
291
- for name, param in signature.parameters.items():
292
- if name in overrides:
293
- dependencies[name] = overrides[name]
294
- elif param.annotation != inspect.Parameter.empty:
295
- # Check if the type is primitive
296
- if isinstance(param.annotation, type):
297
- # It is a primitive type like str, int, etc.
298
- dependencies[name] = param.default if param.default != inspect.Parameter.empty else param.annotation()
299
- else:
300
- dependencies[name] = self.make(param.annotation.__name__)
301
- elif param.default != inspect.Parameter.empty:
302
- dependencies[name] = param.default
303
- else:
304
- raise OrionisContainerValueError(f"Cannot resolve parameter '{name}' in method '{method_name}'")
305
-
306
- return method(**dependencies)
307
-
305
+ Create and return an instance of a registered service.
306
+
307
+ Parameters
308
+ ----------
309
+ abstract : Any
310
+ The service class or alias to instantiate.
311
+
312
+ Returns
313
+ -------
314
+ Any
315
+ An instance of the requested service.
316
+
317
+ Raises
318
+ ------
319
+ OrionisContainerException
320
+ If the service is not found in the container.
321
+ """
322
+ key = self._aliases.get(abstract, abstract)
308
323
 
309
- def _primitiveTypeValidator(self, abstract: str) -> None:
310
- """Validates that the abstract name is not a primitive type.
324
+ if key in self._instances:
325
+ return self._instances[key]['instance']
311
326
 
312
- Args:
313
- abstract (str): Name of the service to validate.
327
+ if key in self._singletons:
328
+ self._instances[key] = {'instance': self._resolve(self._singletons[key]['callback'])}
329
+ return self._instances[key]['instance']
314
330
 
315
- Raises:
316
- OrionisContainerException: If the service name matches a primitive type.
317
- """
318
- if self._primitive_types_validator.isPrimitive(abstract):
319
- raise OrionisContainerException(f"Cannot register a service with a name equal to a primitive type: '{abstract}'.")
331
+ if key in self._scoped_services:
332
+ if key not in self._scoped_instances:
333
+ self._scoped_instances[key] = self._resolve(self._scoped_services[key]['callback'])
334
+ return self._scoped_instances[key]
320
335
 
321
- def _resolve(self, concrete: Callable[..., Any]):
322
- """Automatically resolves the dependencies of a service, handling its constructor dependencies.
336
+ if key in self._transients:
337
+ return self._resolve(self._transients[key]['callback'])
323
338
 
324
- If the service is a class, it recursively resolves its dependencies (constructor parameters).
339
+ if key in self._bindings:
340
+ return self._resolve(self._bindings[key]['callback'])
325
341
 
326
- Args:
327
- concrete (Callable[..., Any]): Concrete implementation of the service.
342
+ raise OrionisContainerException(f"Service '{abstract}' is not registered in the container.")
328
343
 
329
- Returns:
330
- Any: The resolved service instance.
331
-
332
- Raises:
333
- ValueError: If there is a constructor parameter whose type cannot be resolved.
344
+ def _resolve(self, concrete: Callable[..., Any]) -> Any:
334
345
  """
335
- if inspect.isclass(concrete):
336
- constructor = inspect.signature(concrete.__init__)
337
- parameters = constructor.parameters
338
-
339
- # If the class has no parameters in its constructor, instantiate it directly
340
- if len(parameters) == 0 or (len(parameters) == 1 and "self" in parameters):
341
- return concrete()
346
+ Resolve and instantiate a given service class or function.
342
347
 
343
- # Resolve the dependencies of the class constructor
344
- dependencies = {}
345
- for name, param in parameters.items():
346
- if name == "self" or param.kind in (param.VAR_POSITIONAL, param.VAR_KEYWORD):
347
- continue
348
+ This method analyzes the constructor of the given class (or callable),
349
+ retrieves its dependencies, and resolves them recursively, while respecting
350
+ the service lifecycle.
351
+ """
348
352
 
353
+ # Step 1: Retrieve the constructor signature of the class or callable.
354
+ try:
355
+ signature = inspect.signature(concrete)
356
+ except ValueError as e:
357
+ raise OrionisContainerException(f"Unable to inspect signature of {concrete}: {str(e)}")
358
+
359
+ # Step 2: Prepare a dictionary for resolved dependencies and a queue for unresolved ones.
360
+ resolved_dependencies: Dict[str, Any] = {}
361
+ unresolved_dependencies = deque()
362
+
363
+ # Step 3: Iterate through the parameters of the constructor.
364
+ for param_name, param in signature.parameters.items():
365
+ if param_name == 'self':
366
+ # Skip 'self' in methods
367
+ continue
368
+
369
+ # If parameter has no annotation and no default value, it's unresolved
370
+ if param.annotation is param.empty and param.default is param.empty:
371
+ unresolved_dependencies.append(param_name)
372
+ continue
373
+
374
+ # Resolve dependencies based on annotations (excluding primitive types)
375
+ if param.annotation is not param.empty:
349
376
  param_type = param.annotation
350
-
351
- # If the parameter has a default value, use it directly
352
- if param_type == param.empty:
353
- if param.default != param.empty:
354
- # Use the default value if available
355
- dependencies[name] = param.default
356
- continue
377
+ # Check if it's a registered service, if so, resolve it through the container
378
+ if isinstance(param_type, type) and not isinstance(param_type, (int, str, bool, float)) and not issubclass(param_type, (int, str, bool, float)):
379
+ # Check if the service is registered in the container
380
+ if self.has(param_type):
381
+ resolved_dependencies[param_name] = self.make(f"{param_type.__module__}.{param_type.__name__}")
357
382
  else:
358
- raise OrionisContainerValueError(f"Parameter type {name} not specified in {concrete.__name__}")
359
-
360
- # If the parameter type is a primitive (str, int, etc.), pass it as is
361
- if isinstance(param_type, type):
362
- if param.default != param.empty:
363
- dependencies[name] = param.default if param.default != param.empty else param_type()
364
- else:
365
- dependencies[name] = param_type() # Provide default value for primitive types if not specified
383
+ resolved_dependencies[param_name] = self._resolve_dependency(param_type)
366
384
  else:
367
- dep_name = param_type.__name__
385
+ resolved_dependencies[param_name] = param_type # It's a primitive, use as-is
368
386
 
369
- # Conditional resolution of dependencies, if registered
370
- if concrete in self._conditional_bindings and dep_name in self._conditional_bindings[concrete]:
371
- dependencies[name] = self.make(self._conditional_bindings[concrete][dep_name])
372
- else:
373
- dependencies[name] = self.make(dep_name)
387
+ # Resolve parameters with default values (without annotations)
388
+ elif param.default is not param.empty:
389
+ resolved_dependencies[param_name] = param.default
374
390
 
375
- return concrete(**dependencies)
391
+ # Step 4: Resolve any remaining unresolved dependencies.
392
+ while unresolved_dependencies:
393
+ dep_name = unresolved_dependencies.popleft()
394
+ if dep_name not in resolved_dependencies:
395
+ resolved_dependencies[dep_name] = self._resolve_dependency(dep_name)
376
396
 
377
- return concrete(self)
397
+ # Step 5: Instantiate the class with resolved dependencies.
398
+ try:
399
+ return concrete(**resolved_dependencies)
400
+ except Exception as e:
401
+ raise OrionisContainerException(f"Failed to instantiate {concrete}: {str(e)}")
378
402
 
379
- def startRequest(self):
380
- """Starts a new request and clears the Scoped instances.
403
+ def _resolve_dependency(self, dep_type: Any) -> Any:
404
+ """
405
+ Resolves a dependency based on the provided type.
381
406
 
382
- This method should be called at the beginning of each request to ensure that
383
- scoped services do not persist between requests.
407
+ This method looks for the type in the container and returns the instance,
408
+ respecting the lifecycle of the service (transient, singleton, etc.).
384
409
  """
385
- self._scoped_instances = {}
410
+ # Check if the dependency exists in the container or create it if necessary
411
+ # If it's a class type
412
+ if isinstance(dep_type, type):
413
+ if self.has(dep_type):
414
+ # Resolves the service through the container
415
+ return self.make(f"{dep_type.__module__}.{dep_type.__name__}")
416
+ else:
417
+ # Instantiate the class if not found in the container
418
+ return self._resolve(dep_type)
419
+
420
+ raise OrionisContainerException(f"Cannot resolve dependency of type {dep_type}")
@@ -34,3 +34,21 @@ class OrionisContainerValueError(ValueError):
34
34
  def __str__(self) -> str:
35
35
  """Retorna una representación en cadena de la excepción."""
36
36
  return f"[OrionisContainerValueError] {self.args[0]}"
37
+
38
+ class OrionisContainerTypeError(TypeError):
39
+ """
40
+ Custom exception for TypeError related to the Orionis container.
41
+ """
42
+
43
+ def __init__(self, message: str) -> None:
44
+ """
45
+ Initializes the exception with an error message.
46
+
47
+ Args:
48
+ message (str): Descriptive error message.
49
+ """
50
+ super().__init__(message)
51
+
52
+ def __str__(self) -> str:
53
+ """Returns a string representation of the exception."""
54
+ return f"[OrionisContainerTypeError] {self.args[0]}"
@@ -14,12 +14,12 @@ class IRegister(ABC):
14
14
 
15
15
  Methods
16
16
  -------
17
- config(config_class: type) -> type
17
+ config(config_class: type) -> None
18
18
  Registers a configuration class and ensures it meets the necessary criteria.
19
19
  """
20
20
 
21
21
  @abstractmethod
22
- def config(self, config_class: type) -> type:
22
+ def config(self, config_class: type) -> None:
23
23
  """
24
24
  Registers a configuration class and ensures it meets the required structure.
25
25
 
@@ -2,151 +2,221 @@ from abc import ABC, abstractmethod
2
2
  from typing import Any, Callable
3
3
 
4
4
  class IContainer(ABC):
5
- """Service container and dependency injection."""
6
5
 
7
6
  @abstractmethod
8
- def bind(self, abstract: str, concrete: Callable[..., Any]) -> None:
9
- """Registers a service with a specific implementation.
10
-
11
- Args:
12
- abstract (str): Name or key of the service to register.
13
- concrete (Callable[..., Any]): Concrete implementation of the service.
7
+ def _newRequest(self) -> None:
8
+ """
9
+ Reset scoped instances at the beginning of a new request.
10
+ """
11
+ pass
14
12
 
15
- Raises:
16
- OrionisContainerException: If the service is already registered.
17
- TypeError: If the implementation is not a callable or instantiable class.
13
+ @abstractmethod
14
+ def _ensureNotMain(self, concrete: Callable[..., Any]) -> str:
15
+ """
16
+ Ensure that a class is not defined in the main script.
17
+
18
+ Parameters
19
+ ----------
20
+ concrete : Callable[..., Any]
21
+ The class or function to check.
22
+
23
+ Returns
24
+ -------
25
+ str
26
+ The fully qualified name of the class.
27
+
28
+ Raises
29
+ ------
30
+ OrionisContainerValueError
31
+ If the class is defined in the main module.
18
32
  """
19
33
  pass
20
34
 
21
35
  @abstractmethod
22
- def transient(self, abstract: str, concrete: Callable[..., Any]) -> None:
23
- """Registers a service as Transient, creating a new instance on each request.
36
+ def _ensureUniqueService(self, obj: Any) -> None:
37
+ """
38
+ Ensure that a service is not already registered.
24
39
 
25
- Args:
26
- abstract (str): Name or key of the service to register.
27
- concrete (Callable[..., Any]): Concrete implementation of the service.
40
+ Parameters
41
+ ----------
42
+ obj : Any
43
+ The service to check.
28
44
 
29
- Raises:
30
- OrionisContainerException: If the service is already registered.
31
- TypeError: If the implementation is not a callable or instantiable class.
45
+ Raises
46
+ ------
47
+ OrionisContainerValueError
48
+ If the service is already registered.
32
49
  """
33
50
  pass
34
51
 
35
52
  @abstractmethod
36
- def singleton(self, abstract: str, concrete: Callable[..., Any]) -> None:
37
- """Registers a service as Singleton, ensuring a single shared instance.
53
+ def _ensureIsCallable(self, concrete: Callable[..., Any]) -> None:
54
+ """
55
+ Ensure that the given implementation is callable or instantiable.
38
56
 
39
- Args:
40
- abstract (str): Name or key of the service to register.
41
- concrete (Callable[..., Any]): Concrete implementation of the service.
57
+ Parameters
58
+ ----------
59
+ concrete : Callable[..., Any]
60
+ The implementation to check.
42
61
 
43
- Raises:
44
- OrionisContainerException: If the service is already registered.
45
- TypeError: If the implementation is not a callable or instantiable class.
62
+ Raises
63
+ ------
64
+ OrionisContainerTypeError
65
+ If the implementation is not callable.
46
66
  """
47
67
  pass
48
68
 
49
69
  @abstractmethod
50
- def scoped(self, abstract: str, concrete: Callable[..., Any]) -> None:
51
- """Registers a service as Scoped, shared within the same request.
70
+ def _ensureIsInstance(self, instance: Any) -> None:
71
+ """
72
+ Ensure that the given instance is a valid object.
52
73
 
53
- Args:
54
- abstract (str): Name or key of the service to register.
55
- concrete (Callable[..., Any]): Concrete implementation of the service.
74
+ Parameters
75
+ ----------
76
+ instance : Any
77
+ The instance to check.
56
78
 
57
- Raises:
58
- OrionisContainerException: If the service is already registered.
59
- TypeError: If the implementation is not a callable or instantiable class.
79
+ Raises
80
+ ------
81
+ OrionisContainerValueError
82
+ If the instance is not a valid object.
60
83
  """
61
84
  pass
62
85
 
63
86
  @abstractmethod
64
- def instance(self, abstract: str, instance: Any) -> None:
65
- """Registers a specific instance in the container, allowing it to be reused.
66
-
87
+ def bind(self, concrete: Callable[..., Any]) -> str:
88
+ """
89
+ Bind a callable to the container.
90
+ This method ensures that the provided callable is not the main function,
91
+ is unique within the container, and is indeed callable. It then creates
92
+ a unique key for the callable based on its module and name, and stores
93
+ the callable in the container's bindings.
67
94
  Args:
68
- abstract (str): Name or key of the service to register.
69
- instance (Any): Specific instance of the service to register.
70
-
71
- Raises:
72
- OrionisContainerException: If the instance is already registered.
73
- ValueError: If the provided instance is of an unexpected or invalid type.
95
+ concrete (Callable[..., Any]): The callable to be bound to the container.
96
+ Returns:
97
+ str: The unique key generated for the callable.
74
98
  """
75
99
  pass
76
100
 
77
101
  @abstractmethod
78
- def has(self, abstract: str) -> bool:
79
- """Checks if a service is registered in the container.
80
-
102
+ def transient(self, concrete: Callable[..., Any]) -> str:
103
+ """
104
+ Registers a transient service in the container.
105
+ A transient service is created each time it is requested.
81
106
  Args:
82
- abstract (str): Name or key of the service to check.
83
-
107
+ concrete (Callable[..., Any]): The callable that defines the service.
84
108
  Returns:
85
- bool: True if the service is registered, False otherwise.
86
-
87
- Raises:
88
- ValueError: If the service name (abstract) is not a valid string.
109
+ str: The unique key generated for the callable.
89
110
  """
90
111
  pass
91
112
 
92
113
  @abstractmethod
93
- def alias(self, abstract: str, alias: str) -> None:
94
- """Creates an alias for a registered service, allowing access to the service using an alternative name.
95
-
114
+ def singleton(self, concrete: Callable[..., Any]) -> str:
115
+ """
116
+ Registers a callable as a singleton in the container.
117
+ This method ensures that the provided callable is not the main module,
118
+ is unique within the container, and is indeed callable. It then registers
119
+ the callable as a singleton, storing it in the container's singleton registry.
96
120
  Args:
97
- abstract (str): Name or key of the original service.
98
- alias (str): The alias to assign to the service.
99
-
100
- Raises:
101
- OrionisContainerException: If the original service is not registered.
102
- ValueError: If the alias is not a valid string or is already in use.
121
+ concrete (Callable[..., Any]): The callable to be registered as a singleton.
122
+ Returns:
123
+ str: The key under which the singleton is registered in the container.
103
124
  """
104
125
  pass
105
126
 
106
127
  @abstractmethod
107
- def make(self, abstract: str):
108
- """Automatically resolves a dependency, handling instances, singletons, scoped, transients, and aliases.
109
-
110
- This method resolves the dependencies of a service and handles the following service types:
111
- 1. **Instances**: Returns a specific instance.
112
- 2. **Singletons**: Returns the same unique instance each time.
113
- 3. **Scoped**: Returns a shared instance within the same request.
114
- 4. **Transients**: Creates a new instance each time.
115
- 5. **Aliases**: Resolves an alias to the original service.
116
-
128
+ def scoped(self, concrete: Callable[..., Any]) -> str:
129
+ """
130
+ Registers a callable as a scoped service.
131
+ This method ensures that the provided callable is not the main service,
132
+ is unique, and is indeed callable. It then registers the callable in the
133
+ scoped services dictionary with relevant metadata.
117
134
  Args:
118
- abstract (str): Name or key of the service to resolve.
135
+ concrete (Callable[..., Any]): The callable to be registered as a scoped service.
136
+ Returns:
137
+ str: The key under which the callable is registered in the scoped services dictionary.
138
+ """
139
+ pass
119
140
 
141
+ @abstractmethod
142
+ def instance(self, instance: Any) -> str:
143
+ """
144
+ Registers an instance as a singleton in the container.
145
+ Args:
146
+ instance (Any): The instance to be registered as a singleton.
120
147
  Returns:
121
- Any: The resolved instance or service.
148
+ str: The key under which the instance is registered in the container.
149
+ """
150
+ pass
122
151
 
152
+ @abstractmethod
153
+ def alias(self, alias: str, concrete: Any) -> None:
154
+ """
155
+ Creates an alias for a registered service.
156
+ Args:
157
+ alias (str): The alias name to be used for the service.
158
+ concrete (Any): The actual service instance or callable to be aliased.
123
159
  Raises:
124
- OrionisContainerException: If the service is not found.
160
+ OrionisContainerException: If the concrete instance is not a valid object or if the alias is a primitive type.
125
161
  """
126
162
  pass
127
163
 
128
164
  @abstractmethod
129
- def call(self, instance: Any, method_name: str, **overrides):
130
- """Llama a un método del objeto resolviendo automáticamente las dependencias registradas.
165
+ def has(self, obj: Any) -> bool:
166
+ """
167
+ Checks if a service is registered in the container.
131
168
 
132
- Args:
133
- instance (Any): Instancia del objeto en el cual se ejecutará el método.
134
- method_name (str): Nombre del método a llamar.
135
- **overrides: Argumentos que se deben pasar manualmente en lugar de resolverlos automáticamente.
169
+ Parameters
170
+ ----------
171
+ obj : Any
172
+ The service class, instance, or alias to check.
136
173
 
137
- Returns:
138
- Any: El resultado de ejecutar el método con las dependencias resueltas.
174
+ Returns
175
+ -------
176
+ bool
177
+ True if the service is registered, False otherwise.
178
+ """
179
+ pass
139
180
 
140
- Raises:
141
- AttributeError: Si el método no existe en la instancia.
181
+ @abstractmethod
182
+ def make(self, abstract: Any) -> Any:
183
+ """
184
+ Create and return an instance of a registered service.
185
+
186
+ Parameters
187
+ ----------
188
+ abstract : Any
189
+ The service class or alias to instantiate.
190
+
191
+ Returns
192
+ -------
193
+ Any
194
+ An instance of the requested service.
195
+
196
+ Raises
197
+ ------
198
+ OrionisContainerException
199
+ If the service is not found in the container.
142
200
  """
143
201
  pass
144
202
 
145
203
  @abstractmethod
146
- def startRequest(self):
147
- """Starts a new request and clears the Scoped instances.
204
+ def _resolve(self, concrete: Callable[..., Any]) -> Any:
205
+ """
206
+ Resolve and instantiate a given service class or function.
207
+
208
+ This method analyzes the constructor of the given class (or callable),
209
+ retrieves its dependencies, and resolves them recursively, while respecting
210
+ the service lifecycle.
211
+ """
212
+ pass
213
+
214
+ @abstractmethod
215
+ def _resolve_dependency(self, dep_type: Any) -> Any:
216
+ """
217
+ Resolves a dependency based on the provided type.
148
218
 
149
- This method should be called at the beginning of each request to ensure that
150
- scoped services do not persist between requests.
219
+ This method looks for the type in the container and returns the instance,
220
+ respecting the lifecycle of the service (transient, singleton, etc.).
151
221
  """
152
222
  pass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: orionis
3
- Version: 0.21.0
3
+ Version: 0.23.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
@@ -1,13 +1,13 @@
1
1
  orionis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  orionis/cli_manager.py,sha256=9wNVJxB0HyqUbNesUvkwlsqTyUbZwK6R46iVLE5WVBQ,1715
3
- orionis/framework.py,sha256=k_g_WMiIzgDUFiruVSieOBZuK9R93xSSJ6rtZh92Oqo,1386
3
+ orionis/framework.py,sha256=erUuf0Lfq0_tWOuxBX6r_arQFnqBRgk7te6RPxymQRI,1386
4
4
  orionis/luminate/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  orionis/luminate/app.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
6
  orionis/luminate/bootstrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
7
  orionis/luminate/bootstrap/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  orionis/luminate/bootstrap/config/bootstrapper.py,sha256=R1ixec362o-Td-kNSvi-0ylVqspFWJq6L6IHYzWHPmg,4227
9
9
  orionis/luminate/bootstrap/config/parser.py,sha256=Ay8dh3aax9xhVgeLSHT71ubtZJiKAJAP85dLFF7djyA,1950
10
- orionis/luminate/bootstrap/config/register.py,sha256=SYG-wBCiVbRAJWr33iWYesqVdWzGEmqxszryuwJankI,2760
10
+ orionis/luminate/bootstrap/config/register.py,sha256=uX0XGEWdo3iJou5B96vxvhZEjliTGC1HBaT8w9enMmk,2760
11
11
  orionis/luminate/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  orionis/luminate/cache/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
13
  orionis/luminate/cache/app/config.py,sha256=tSHZ7sL_nYSz0aQOo_WkhYYchV1Q9ZdHOWUeHx7uwxE,2242
@@ -53,13 +53,13 @@ orionis/luminate/console/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
53
53
  orionis/luminate/console/scripts/management.py,sha256=KT6Bg8kyuUw63SNAtZo6tLH6syOEkxM9J70tCpKgrUw,2860
54
54
  orionis/luminate/console/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
55
  orionis/luminate/console/tasks/scheduler.py,sha256=h3fRVTx6TuZVcY7zZ6oOzGDnlDAWaRwkj92pFTGUm6E,22686
56
- orionis/luminate/container/container.py,sha256=rgUQ1fJslFR-0nxzpubAGEMXAiZZlVd_TjONPOrXjv4,16498
57
- orionis/luminate/container/exception.py,sha256=Tf7KdfbEtr0YldhZ1WrKfkBlI5W_KvZO61xWAGFdF0A,1156
56
+ orionis/luminate/container/container.py,sha256=O4AKaRQK2XfN0es70lIxUGkP0mxxemSsHOOuvtJVcUU,16212
57
+ orionis/luminate/container/exception.py,sha256=ap1SqYEjQEEHXJJTNmL7V1jrmRjgT5_7geZ95MYkhMA,1691
58
58
  orionis/luminate/container/types.py,sha256=PbPNOJ8e4SGzCmu-zOmCQmDzt1b9I73v3fw_xzLq9RU,932
59
59
  orionis/luminate/contracts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
60
  orionis/luminate/contracts/bootstrap/config/bootstrapper_interface.py,sha256=De6nKQcZpN1dWsubXWUO229ZH4IbCoUaGtP5HOUw2Ps,2251
61
61
  orionis/luminate/contracts/bootstrap/config/parser_interface.py,sha256=7DLnp7yB7edayVkSm-piTi8JSf0QKaYYI82qDZudgM0,1641
62
- orionis/luminate/contracts/bootstrap/config/register_interface.py,sha256=600rNNM9SaYlVuDTSirfNpwEVHODdu_ZMj_dOx_vJkU,1506
62
+ orionis/luminate/contracts/bootstrap/config/register_interface.py,sha256=7kMW04ptvQ51PnprtCIR-iLHljVSGiH58GN493igeuw,1506
63
63
  orionis/luminate/contracts/cache/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
64
64
  orionis/luminate/contracts/cache/cache_commands_interface.py,sha256=NhJAh2OZHXKMwbJ8nOU4IFJRZpuJNmfN9rtnpbCnuOo,1936
65
65
  orionis/luminate/contracts/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -80,7 +80,7 @@ orionis/luminate/contracts/console/register_interface.py,sha256=rhHI_as_3yNWtdgQ
80
80
  orionis/luminate/contracts/console/runner_interface.py,sha256=vWLtMhl0m1T6rfCUHZbxGQJl9ZWTXlp3HjcTfCAztGk,1644
81
81
  orionis/luminate/contracts/console/schedule_interface.py,sha256=_dsR0gCvJ7_67lwPUAzBwQFHNvWM6jVjcg1EdPqDIIo,10117
82
82
  orionis/luminate/contracts/console/task_manager_interface.py,sha256=sOmeifoncpWCG2WYh4q3QZ7M7w7P9Onb3Jxw9X2lpXE,1197
83
- orionis/luminate/contracts/container/container_interface.py,sha256=Pzvyu5guJkHCxqzHf0Z6JGSLVMwekhrLvccDkPXgC-0,5764
83
+ orionis/luminate/contracts/container/container_interface.py,sha256=c_QRQHXGIujiDnYXyt--3J2LKXngVtoB3O-qchPmFDQ,6899
84
84
  orionis/luminate/contracts/container/types_interface.py,sha256=GCH7x3PjpXKPET3l84GcXbcM8cpne8AGrmTw-uFaT24,526
85
85
  orionis/luminate/contracts/facades/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
86
86
  orionis/luminate/contracts/facades/env_interface.py,sha256=aN9dZdsuCpy3meZqMxyxLCYlKEIF7XkRNXmP7aS7eCw,1664
@@ -140,9 +140,9 @@ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
140
  tests/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
141
141
  tests/tools/class_example.py,sha256=dIPD997Y15n6WmKhWoOFSwEldRm9MdOHTZZ49eF1p3c,1056
142
142
  tests/tools/test_reflection.py,sha256=dNN5p_xAosyEf0ddAElmmmTfhcTtBd4zBNl7qzgnsc0,5242
143
- orionis-0.21.0.dist-info/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
144
- orionis-0.21.0.dist-info/METADATA,sha256=oLJmgUO2APvJ2njDurl99MM_4TBcWGLfuYprWUV99nE,2978
145
- orionis-0.21.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
146
- orionis-0.21.0.dist-info/entry_points.txt,sha256=eef1_CVewfokKjrGBynXa06KabSJYo7LlDKKIKvs1cM,53
147
- orionis-0.21.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
148
- orionis-0.21.0.dist-info/RECORD,,
143
+ orionis-0.23.0.dist-info/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
144
+ orionis-0.23.0.dist-info/METADATA,sha256=IBV6Pic2odjpNmHFEEZUIDIGrkjVPg48VsrQApR669c,2978
145
+ orionis-0.23.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
146
+ orionis-0.23.0.dist-info/entry_points.txt,sha256=eef1_CVewfokKjrGBynXa06KabSJYo7LlDKKIKvs1cM,53
147
+ orionis-0.23.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
148
+ orionis-0.23.0.dist-info/RECORD,,