orionis 0.320.0__py3-none-any.whl → 0.321.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.
@@ -4,7 +4,7 @@ from orionis.container.contracts.container import IContainer
4
4
  from orionis.container.entities.binding import Binding
5
5
  from orionis.container.enums.lifetimes import Lifetime
6
6
  from orionis.container.exceptions.container_exception import OrionisContainerException
7
- from orionis.container.exceptions.type_error_exception import OrionisContainerTypeError
7
+ from orionis.container.resolver import Resolver
8
8
  from orionis.container.validators.implements import ImplementsAbstractMethods
9
9
  from orionis.container.validators.is_abstract_class import IsAbstractClass
10
10
  from orionis.container.validators.is_callable import IsCallable
@@ -13,12 +13,9 @@ from orionis.container.validators.is_instance import IsInstance
13
13
  from orionis.container.validators.is_not_subclass import IsNotSubclass
14
14
  from orionis.container.validators.is_subclass import IsSubclass
15
15
  from orionis.container.validators.is_valid_alias import IsValidAlias
16
+ from orionis.container.validators.lifetime import LifetimeValidator
16
17
  from orionis.services.introspection.abstract.reflection_abstract import ReflectionAbstract
17
18
  from orionis.services.introspection.callables.reflection_callable import ReflectionCallable
18
- from orionis.services.introspection.concretes.reflection_concrete import ReflectionConcrete
19
- from orionis.services.introspection.dependencies.entities.resolved_dependencies import ResolvedDependency
20
- from orionis.services.introspection.instances.reflection_instance import ReflectionInstance
21
- from orionis.services.introspection.reflection import Reflection
22
19
 
23
20
  class Container(IContainer):
24
21
 
@@ -37,9 +34,7 @@ class Container(IContainer):
37
34
  _initialized = False
38
35
 
39
36
  def __new__(
40
- cls,
41
- *args,
42
- **kwargs
37
+ cls
43
38
  ) -> 'Container':
44
39
  """
45
40
  Creates and returns a singleton instance of the class.
@@ -48,12 +43,7 @@ class Container(IContainer):
48
43
  of the class exists. If an instance does not exist, it acquires a lock to
49
44
  ensure thread safety and creates the instance. Subsequent calls return the
50
45
  existing instance.
51
- Parameters
52
- ----------
53
- *args : tuple
54
- Variable length argument list.
55
- **kwargs : dict
56
- Arbitrary keyword arguments.
46
+
57
47
  Returns
58
48
  -------
59
49
  Container
@@ -471,21 +461,9 @@ class Container(IContainer):
471
461
  OrionisContainerException
472
462
  If the lifetime is not allowed for the function signature.
473
463
  """
464
+
474
465
  # Normalize and validate the lifetime parameter
475
- if not isinstance(lifetime, Lifetime):
476
- if isinstance(lifetime, str):
477
- lifetime_key = lifetime.strip().upper()
478
- if lifetime_key in Lifetime.__members__:
479
- lifetime = Lifetime[lifetime_key]
480
- else:
481
- valid = ', '.join(Lifetime.__members__.keys())
482
- raise OrionisContainerTypeError(
483
- f"Invalid lifetime '{lifetime}'. Valid options are: {valid}."
484
- )
485
- else:
486
- raise OrionisContainerTypeError(
487
- f"Lifetime must be of type str or Lifetime enum, got {type(lifetime).__name__}."
488
- )
466
+ lifetime = LifetimeValidator(lifetime)
489
467
 
490
468
  # Ensure that the alias is a valid string
491
469
  IsValidAlias(alias)
@@ -544,7 +522,7 @@ class Container(IContainer):
544
522
  or abstract_or_alias in self.__aliasses
545
523
  )
546
524
 
547
- def __getService(
525
+ def getBinding(
548
526
  self,
549
527
  abstract_or_alias: Any
550
528
  ) -> Binding:
@@ -563,29 +541,6 @@ class Container(IContainer):
563
541
  """
564
542
  return self.__bindings.get(abstract_or_alias) or self.__aliasses.get(abstract_or_alias)
565
543
 
566
- def __getFirstService(
567
- self,
568
- abstract_or_aliasses: list
569
- ) -> Binding:
570
- """
571
- Retrieves the first binding from a list of abstract types or aliases.
572
-
573
- Parameters
574
- ----------
575
- abstract_or_aliasses : list
576
- A list of abstract classes, interfaces, or aliases (str) to retrieve.
577
-
578
- Returns
579
- -------
580
- Binding
581
- The first binding found in the container for the provided abstract types or aliases.
582
- """
583
- for item in abstract_or_aliasses:
584
- binding = self.__getService(item)
585
- if binding:
586
- return binding
587
- return None
588
-
589
544
  def drop(
590
545
  self,
591
546
  abstract: Callable[..., Any] = None,
@@ -665,341 +620,17 @@ class Container(IContainer):
665
620
  OrionisContainerException
666
621
  If the requested service is not registered in the container.
667
622
  """
668
- # Retrieve the binding for the requested abstract or alias
669
- binding = self.__getService(abstract_or_alias)
670
-
671
- # Check if the requested service is registered in the container
672
- if not binding:
623
+ if not self.bound(abstract_or_alias):
673
624
  raise OrionisContainerException(
674
625
  f"The requested service '{abstract_or_alias}' is not registered in the container."
675
626
  )
676
627
 
677
- # Handle based on binding type and lifetime
678
- if binding.lifetime == Lifetime.TRANSIENT:
679
- return self.__resolveTransient(binding, *args, **kwargs)
680
- elif binding.lifetime == Lifetime.SINGLETON:
681
- return self.__resolveSingleton(binding, *args, **kwargs)
682
- elif binding.lifetime == Lifetime.SCOPED:
683
- # TODO: Implement scoped lifetime resolution
684
- raise OrionisContainerException(
685
- "Scoped lifetime resolution is not yet implemented."
686
- )
687
-
688
- def __resolveTransient(self, binding: Binding, *args, **kwargs) -> Any:
689
- """
690
- Resolves a service with transient lifetime.
691
-
692
- Parameters
693
- ----------
694
- binding : Binding
695
- The binding to resolve.
696
- *args : tuple
697
- Positional arguments to pass to the constructor.
698
- **kwargs : dict
699
- Keyword arguments to pass to the constructor.
700
-
701
- Returns
702
- -------
703
- Any
704
- A new instance of the requested service.
705
- """
706
-
707
- # Check if the binding has a concrete class or function defined
708
- if binding.concrete:
709
- if args or kwargs:
710
- return self.__instantiateConcreteWithArgs(binding.concrete, *args, **kwargs)
711
- else:
712
- return self.__instantiateConcreteReflective(binding.concrete)
713
-
714
- # If the binding has a function defined
715
- elif binding.function:
716
- if args or kwargs:
717
- return self.__instantiateCallableWithArgs(binding.function, *args, **kwargs)
718
- else:
719
- return self.__instantiateCallableReflective(binding.function)
720
-
721
- # If neither concrete class nor function is defined
722
- else:
723
- raise OrionisContainerException(
724
- "Cannot resolve transient binding: neither a concrete class nor a function is defined."
725
- )
726
-
727
- def __resolveSingleton(self, binding: Binding, *args, **kwargs) -> Any:
728
- """
729
- Resolves a service with singleton lifetime.
730
-
731
- Parameters
732
- ----------
733
- binding : Binding
734
- The binding to resolve.
735
- *args : tuple
736
- Positional arguments to pass to the constructor (only used if instance doesn't exist yet).
737
- **kwargs : dict
738
- Keyword arguments to pass to the constructor (only used if instance doesn't exist yet).
739
-
740
- Returns
741
- -------
742
- Any
743
- The singleton instance of the requested service.
744
- """
745
- # Return existing instance if available
746
- if binding.instance:
747
- return binding.instance
748
-
749
- # Create instance if needed
750
- if binding.concrete:
751
- if args or kwargs:
752
- binding.instance = self.__instantiateConcreteWithArgs(binding.concrete, *args, **kwargs)
753
- else:
754
- binding.instance = self.__instantiateConcreteReflective(binding.concrete)
755
- return binding.instance
756
-
757
- # If the binding has a function defined
758
- elif binding.function:
759
- if args or kwargs:
760
- result = self.__instantiateCallableWithArgs(binding.function, *args, **kwargs)
761
- else:
762
- result = self.__instantiateCallableReflective(binding.function)
763
-
764
- # Store the result directly as the singleton instance
765
- # We don't automatically invoke factory function results anymore
766
- binding.instance = result
767
- return binding.instance
768
-
769
- # If neither concrete class nor function is defined
770
- else:
771
- raise OrionisContainerException(
772
- "Cannot resolve singleton binding: neither a concrete class, instance, nor function is defined."
773
- )
774
-
775
- def __instantiateConcreteWithArgs(self, concrete: Callable[..., Any], *args, **kwargs) -> Any:
776
- """
777
- Instantiates a concrete class with the provided arguments.
778
-
779
- Parameters
780
- ----------
781
- concrete : Callable[..., Any]
782
- Class to instantiate.
783
- *args : tuple
784
- Positional arguments to pass to the constructor.
785
- **kwargs : dict
786
- Keyword arguments to pass to the constructor.
787
-
788
- Returns
789
- -------
790
- object
791
- A new instance of the specified concrete class.
792
- """
793
-
794
- # try to instantiate the concrete class with the provided arguments
795
- try:
796
-
797
- # If the concrete is a class, instantiate it directly
798
- return concrete(*args, **kwargs)
799
-
800
- except TypeError as e:
801
-
802
- # If instantiation fails, use ReflectionConcrete to get class name and constructor signature
803
- rf_concrete = ReflectionConcrete(concrete)
804
- class_name = rf_concrete.getClassName()
805
- signature = rf_concrete.getConstructorSignature()
806
-
807
- # Raise an exception with detailed information about the failure
808
- raise OrionisContainerException(
809
- f"Failed to instantiate [{class_name}] with the provided arguments: {e}\n"
810
- f"Expected constructor signature: [{signature}]"
811
- ) from e
812
-
813
- def __instantiateCallableWithArgs(self, fn: Callable[..., Any], *args, **kwargs) -> Any:
814
- """
815
- Invokes a callable with the provided arguments.
816
-
817
- Parameters
818
- ----------
819
- fn : Callable[..., Any]
820
- The callable to invoke.
821
- *args : tuple
822
- Positional arguments to pass to the callable.
823
- **kwargs : dict
824
- Keyword arguments to pass to the callable.
825
-
826
- Returns
827
- -------
828
- Any
829
- The result of the callable.
830
- """
831
-
832
- # Try to invoke the callable with the provided arguments
833
- try:
834
-
835
- # If the callable is a function, invoke it directly
836
- return fn(*args, **kwargs)
837
-
838
- except TypeError as e:
839
-
840
- # If invocation fails, use ReflectionCallable to get function name and signature
841
- rf_callable = ReflectionCallable(fn)
842
- function_name = rf_callable.getName()
843
- signature = rf_callable.getSignature()
844
-
845
- # Raise an exception with detailed information about the failure
846
- raise OrionisContainerException(
847
- f"Failed to invoke function [{function_name}] with the provided arguments: {e}\n"
848
- f"Expected function signature: [{signature}]"
849
- ) from e
850
-
851
- def __instantiateConcreteReflective(self, concrete: Callable[..., Any]) -> Any:
852
- """
853
- Instantiates a concrete class reflectively, resolving its dependencies from the container.
854
-
855
- Parameters
856
- ----------
857
- concrete : Callable[..., Any]
858
- The concrete class to instantiate.
628
+ # Get the binding for the requested abstract type or alias
629
+ binding = self.getBinding(abstract_or_alias)
859
630
 
860
- Returns
861
- -------
862
- Any
863
- A new instance of the concrete class.
864
- """
865
- # Resolve dependencies for the concrete class
866
- params = self.__resolveDependencies(concrete, is_class=True)
867
-
868
- # Instantiate the concrete class with resolved dependencies
869
- return concrete(**params)
870
-
871
- def __instantiateCallableReflective(self, fn: Callable[..., Any]) -> Any:
872
- """
873
- Invokes a callable reflectively, resolving its dependencies from the container.
874
-
875
- Parameters
876
- ----------
877
- fn : Callable[..., Any]
878
- The callable to invoke.
879
-
880
- Returns
881
- -------
882
- Any
883
- The result of the callable.
884
- """
885
-
886
- # Resolve dependencies for the callable
887
- params = self.__resolveDependencies(fn, is_class=False)
888
-
889
- # Invoke the callable with resolved dependencies
890
- return fn(**params)
891
-
892
- def __resolveDependencies(
893
- self,
894
- target: Callable[..., Any],
895
- *,
896
- is_class: bool = False
897
- ) -> dict:
898
- """
899
- Resolves dependencies for a target callable or class.
900
-
901
- Parameters
902
- ----------
903
- target : Callable[..., Any]
904
- The target callable or class whose dependencies to resolve.
905
- is_class : bool, optional
906
- Whether the target is a class (True) or a callable (False).
907
-
908
- Returns
909
- -------
910
- dict
911
- A dictionary of resolved dependencies.
912
- """
913
- try:
914
-
915
- # Use ReflectionConcrete for classes and ReflectionCallable for callables
916
- if is_class:
917
- reflection = ReflectionConcrete(target)
918
- dependencies = reflection.getConstructorDependencies()
919
- name = reflection.getClassName()
920
-
921
- # If the target is a callable, use ReflectionCallable
922
- else:
923
- reflection = ReflectionCallable(target)
924
- dependencies = reflection.getDependencies()
925
- name = reflection.getName()
926
-
927
- # Check for unresolved dependencies
928
- if dependencies.unresolved:
929
- unresolved_args = ', '.join(dependencies.unresolved)
930
- raise OrionisContainerException(
931
- f"Cannot resolve '{name}' because the following required arguments are missing: [{unresolved_args}]."
932
- )
933
-
934
- # Resolve dependencies
935
- params = {}
936
- for param_name, dep in dependencies.resolved.items():
937
-
938
- # If the dependency is a ResolvedDependency, resolve it
939
- if isinstance(dep, ResolvedDependency):
940
-
941
- # If the dependency is a built-in type, raise an exception
942
- if dep.module_name == 'builtins':
943
- raise OrionisContainerException(
944
- f"Cannot resolve '{name}' because parameter '{param_name}' depends on built-in type '{dep.type.__name__}'."
945
- )
946
-
947
- # Try to resolve from container
948
- service = self.__getFirstService([dep.type, dep.full_class_path])
949
- if service:
950
- params[param_name] = self.make(service.alias)
951
-
952
- # Try to instantiate directly if it's a concrete class
953
- elif ReflectionConcrete.isConcreteClass(dep.type):
954
- params[param_name] = dep.type(**self.__resolveDependencies(dep.type, is_class=True))
955
-
956
- # Try to call directly if it's a callable
957
- elif callable(dep.type) and not isinstance(dep.type, type):
958
- params[param_name] = dep.type(**self.__resolveDependencies(dep.type, is_class=False))
959
-
960
- # If the dependency cannot be resolved, raise an exception
961
- else:
962
- raise OrionisContainerException(
963
- f"Cannot resolve dependency '{param_name}' of type '{dep.type.__name__}' for '{name}'."
964
- )
965
- else:
966
- # Use default value
967
- params[param_name] = dep
968
-
969
- # Return the resolved parameters
970
- return params
971
-
972
- except ImportError as e:
973
-
974
- # Extract module name from the error message if possible
975
- import_msg = str(e)
976
- module_name = target.__module__ if hasattr(target, '__module__') else "unknown module"
977
-
978
- # Check for potential circular import patterns
979
- if "circular import" in import_msg.lower() or "cannot import name" in import_msg.lower():
980
- raise OrionisContainerException(
981
- f"Circular import detected while resolving dependencies for '{target.__name__}' in module '{module_name}'.\n"
982
- f"This typically happens when two modules import each other. Consider:\n"
983
- f"1. Restructuring your code to avoid circular dependencies\n"
984
- f"2. Using delayed imports inside methods rather than at module level\n"
985
- f"3. Using dependency injection to break the cycle\n"
986
- f"Original error: {import_msg}"
987
- ) from e
988
- else:
989
- raise OrionisContainerException(
990
- f"Import error while resolving dependencies for '{target.__name__}' in module '{module_name}':\n"
991
- f"{import_msg}"
992
- ) from e
993
-
994
- except Exception as e:
995
-
996
- # Get more context about where the error occurred
997
- target_type = "class" if isinstance(target, type) else "function"
998
- target_name = target.__name__ if hasattr(target, '__name__') else str(target)
999
- module_name = target.__module__ if hasattr(target, '__module__') else "unknown module"
1000
-
1001
- raise OrionisContainerException(
1002
- f"Error resolving dependencies for {target_type} '{target_name}' in '{module_name}':\n"
1003
- f"{str(e)}\n"
1004
- f"Check that all dependencies are properly registered in the container."
1005
- ) from e
631
+ # Resolve the binding using the Resolver class
632
+ return Resolver(self).resolve(
633
+ binding,
634
+ *args,
635
+ **kwargs
636
+ )
@@ -1,6 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from typing import Any, Callable
3
3
  from orionis.container.enums.lifetimes import Lifetime
4
+ from orionis.container.entities.binding import Binding
4
5
 
5
6
  class IContainer(ABC):
6
7
  """
@@ -18,7 +19,7 @@ class IContainer(ABC):
18
19
  enforce_decoupling: bool = False
19
20
  ) -> bool:
20
21
  """
21
- Register a service as a singleton.
22
+ Registers a service with a singleton lifetime.
22
23
 
23
24
  Parameters
24
25
  ----------
@@ -27,7 +28,7 @@ class IContainer(ABC):
27
28
  concrete : Callable[..., Any]
28
29
  The concrete implementation to associate with the abstract type.
29
30
  alias : str, optional
30
- An alternative name to register the service under.
31
+ An alternative name to register the service under. If not provided, the abstract's class name is used.
31
32
  enforce_decoupling : bool, optional
32
33
  Whether to enforce that concrete is not a subclass of abstract.
33
34
 
@@ -48,7 +49,7 @@ class IContainer(ABC):
48
49
  enforce_decoupling: bool = False
49
50
  ) -> bool:
50
51
  """
51
- Register a service as transient.
52
+ Registers a service with a transient lifetime.
52
53
 
53
54
  Parameters
54
55
  ----------
@@ -57,7 +58,7 @@ class IContainer(ABC):
57
58
  concrete : Callable[..., Any]
58
59
  The concrete implementation to associate with the abstract type.
59
60
  alias : str, optional
60
- An alternative name to register the service under.
61
+ An alternative name to register the service under. If not provided, the abstract's class name is used.
61
62
  enforce_decoupling : bool, optional
62
63
  Whether to enforce that concrete is not a subclass of abstract.
63
64
 
@@ -78,7 +79,7 @@ class IContainer(ABC):
78
79
  enforce_decoupling: bool = False
79
80
  ) -> bool:
80
81
  """
81
- Register a service as scoped.
82
+ Registers a service with a scoped lifetime.
82
83
 
83
84
  Parameters
84
85
  ----------
@@ -87,7 +88,7 @@ class IContainer(ABC):
87
88
  concrete : Callable[..., Any]
88
89
  The concrete implementation to associate with the abstract type.
89
90
  alias : str, optional
90
- An alternative name to register the service under.
91
+ An alternative name to register the service under. If not provided, the abstract's class name is used.
91
92
  enforce_decoupling : bool, optional
92
93
  Whether to enforce that concrete is not a subclass of abstract.
93
94
 
@@ -108,7 +109,7 @@ class IContainer(ABC):
108
109
  enforce_decoupling: bool = False
109
110
  ) -> bool:
110
111
  """
111
- Register an instance of a service.
112
+ Registers an instance of a class or interface in the container.
112
113
 
113
114
  Parameters
114
115
  ----------
@@ -117,7 +118,8 @@ class IContainer(ABC):
117
118
  instance : Any
118
119
  The concrete instance to register.
119
120
  alias : str, optional
120
- An optional alias to register the instance under.
121
+ An optional alias to register the instance under. If not provided,
122
+ the abstract's `__name__` attribute will be used as the alias if available.
121
123
  enforce_decoupling : bool, optional
122
124
  Whether to enforce that instance's class is not a subclass of abstract.
123
125
 
@@ -137,7 +139,7 @@ class IContainer(ABC):
137
139
  lifetime: Lifetime = Lifetime.TRANSIENT
138
140
  ) -> bool:
139
141
  """
140
- Register a function as a service.
142
+ Registers a function or factory under a given alias.
141
143
 
142
144
  Parameters
143
145
  ----------
@@ -163,16 +165,16 @@ class IContainer(ABC):
163
165
  **kwargs: dict
164
166
  ) -> Any:
165
167
  """
166
- Resolve a service from the container.
168
+ Resolves and returns an instance of the requested service.
167
169
 
168
170
  Parameters
169
171
  ----------
170
172
  abstract_or_alias : Any
171
173
  The abstract class, interface, or alias (str) to resolve.
172
174
  *args : tuple
173
- Positional arguments to pass to the constructor.
175
+ Positional arguments to pass to the constructor of the resolved service.
174
176
  **kwargs : dict
175
- Keyword arguments to pass to the constructor.
177
+ Keyword arguments to pass to the constructor of the resolved service.
176
178
 
177
179
  Returns
178
180
  -------
@@ -187,16 +189,60 @@ class IContainer(ABC):
187
189
  abstract_or_alias: Any
188
190
  ) -> bool:
189
191
  """
190
- Check if a service is registered in the container.
192
+ Checks if a service (by abstract type or alias) is registered in the container.
191
193
 
192
194
  Parameters
193
195
  ----------
194
196
  abstract_or_alias : Any
195
- The abstract class, interface, or alias (str) to check.
197
+ The abstract class, interface, or alias (str) to check for registration.
196
198
 
197
199
  Returns
198
200
  -------
199
201
  bool
200
- True if the service is registered, False otherwise.
202
+ True if the service is registered (either as an abstract type or alias), False otherwise.
203
+ """
204
+ pass
205
+
206
+ @abstractmethod
207
+ def getBinding(
208
+ self,
209
+ abstract_or_alias: Any
210
+ ) -> Binding:
211
+ """
212
+ Retrieves the binding for the requested abstract type or alias.
213
+
214
+ Parameters
215
+ ----------
216
+ abstract_or_alias : Any
217
+ The abstract class, interface, or alias (str) to retrieve.
218
+
219
+ Returns
220
+ -------
221
+ Binding
222
+ The binding associated with the requested abstract type or alias.
223
+ """
224
+ pass
225
+
226
+ @abstractmethod
227
+ def drop(
228
+ self,
229
+ abstract: Callable[..., Any] = None,
230
+ alias: str = None
231
+ ) -> None:
232
+ """
233
+ Drops a service from the container by removing its bindings and aliases.
234
+
235
+ Warning
236
+ -------
237
+ Using this method irresponsibly can severely damage the system's logic.
238
+ Only use it when you are certain about the consequences, as removing
239
+ critical services may lead to system failures and unexpected behavior.
240
+
241
+ Parameters
242
+ ----------
243
+ abstract : Callable[..., Any], optional
244
+ The abstract type or interface to be removed from the container.
245
+ alias : str, optional
246
+ The alias of the service to be removed.
201
247
  """
202
248
  pass
@@ -1,7 +1,11 @@
1
+ from typing import Any, Callable
1
2
  from orionis.container.contracts.container import IContainer
2
3
  from orionis.container.entities.binding import Binding
3
4
  from orionis.container.enums.lifetimes import Lifetime
4
-
5
+ from orionis.container.exceptions.container_exception import OrionisContainerException
6
+ from orionis.services.introspection.callables.reflection_callable import ReflectionCallable
7
+ from orionis.services.introspection.concretes.reflection_concrete import ReflectionConcrete
8
+ from orionis.services.introspection.dependencies.entities.resolved_dependencies import ResolvedDependency
5
9
 
6
10
  class Resolver:
7
11
  """
@@ -10,19 +14,391 @@ class Resolver:
10
14
 
11
15
  def __init__(
12
16
  self,
13
- container:IContainer,
14
- lifetime:Lifetime
17
+ container:IContainer
15
18
  ):
19
+ """
20
+ Initialize the resolver.
21
+
22
+ This method initializes the resolver with a reference to the container.
23
+
24
+ Parameters
25
+ ----------
26
+ container : IContainer
27
+ The container instance that this resolver will use to resolve dependencies.
28
+ """
16
29
  self.container = container
17
- self.lifetime = lifetime
18
30
 
19
- def transient(
31
+ def resolve(
20
32
  self,
21
33
  binding:Binding,
22
34
  *args,
23
35
  **kwargs
24
36
  ):
25
37
  """
26
- Register a transient service.
38
+ Resolves an instance from a binding.
39
+ This method resolves an instance based on the binding's lifetime and type.
40
+ It delegates to specific resolution methods based on the lifetime (transient, singleton, or scoped).
41
+ Args:
42
+ binding (Binding): The binding to resolve.
43
+ *args: Additional positional arguments to pass to the constructor.
44
+ **kwargs: Additional keyword arguments to pass to the constructor.
45
+ Returns:
46
+ Any: The resolved instance.
47
+ Raises:
48
+ OrionisContainerException: If the binding is not an instance of Binding
49
+ or if scoped lifetime resolution is attempted (not yet implemented).
50
+ """
51
+
52
+ # Ensure the binding is an instance of Binding
53
+ if not isinstance(binding, Binding):
54
+ raise OrionisContainerException(
55
+ "The binding must be an instance of Binding."
56
+ )
57
+
58
+ # Handle based on binding type and lifetime
59
+ if binding.lifetime == Lifetime.TRANSIENT:
60
+ return self.__resolveTransient(binding, *args, **kwargs)
61
+ elif binding.lifetime == Lifetime.SINGLETON:
62
+ return self.__resolveSingleton(binding, *args, **kwargs)
63
+ elif binding.lifetime == Lifetime.SCOPED:
64
+ # TODO: Implement scoped lifetime resolution
65
+ raise OrionisContainerException(
66
+ "Scoped lifetime resolution is not yet implemented."
67
+ )
68
+
69
+ def __resolveTransient(self, binding: Binding, *args, **kwargs) -> Any:
70
+ """
71
+ Resolves a service with transient lifetime.
72
+
73
+ Parameters
74
+ ----------
75
+ binding : Binding
76
+ The binding to resolve.
77
+ *args : tuple
78
+ Positional arguments to pass to the constructor.
79
+ **kwargs : dict
80
+ Keyword arguments to pass to the constructor.
81
+
82
+ Returns
83
+ -------
84
+ Any
85
+ A new instance of the requested service.
86
+ """
87
+
88
+ # Check if the binding has a concrete class or function defined
89
+ if binding.concrete:
90
+ if args or kwargs:
91
+ return self.__instantiateConcreteWithArgs(binding.concrete, *args, **kwargs)
92
+ else:
93
+ return self.__instantiateConcreteReflective(binding.concrete)
94
+
95
+ # If the binding has a function defined
96
+ elif binding.function:
97
+ if args or kwargs:
98
+ return self.__instantiateCallableWithArgs(binding.function, *args, **kwargs)
99
+ else:
100
+ return self.__instantiateCallableReflective(binding.function)
101
+
102
+ # If neither concrete class nor function is defined
103
+ else:
104
+ raise OrionisContainerException(
105
+ "Cannot resolve transient binding: neither a concrete class nor a function is defined."
106
+ )
107
+
108
+ def __resolveSingleton(self, binding: Binding, *args, **kwargs) -> Any:
109
+ """
110
+ Resolves a service with singleton lifetime.
111
+
112
+ Parameters
113
+ ----------
114
+ binding : Binding
115
+ The binding to resolve.
116
+ *args : tuple
117
+ Positional arguments to pass to the constructor (only used if instance doesn't exist yet).
118
+ **kwargs : dict
119
+ Keyword arguments to pass to the constructor (only used if instance doesn't exist yet).
120
+
121
+ Returns
122
+ -------
123
+ Any
124
+ The singleton instance of the requested service.
125
+ """
126
+ # Return existing instance if available
127
+ if binding.instance:
128
+ return binding.instance
129
+
130
+ # Create instance if needed
131
+ if binding.concrete:
132
+ if args or kwargs:
133
+ binding.instance = self.__instantiateConcreteWithArgs(binding.concrete, *args, **kwargs)
134
+ else:
135
+ binding.instance = self.__instantiateConcreteReflective(binding.concrete)
136
+ return binding.instance
137
+
138
+ # If the binding has a function defined
139
+ elif binding.function:
140
+ if args or kwargs:
141
+ result = self.__instantiateCallableWithArgs(binding.function, *args, **kwargs)
142
+ else:
143
+ result = self.__instantiateCallableReflective(binding.function)
144
+
145
+ # Store the result directly as the singleton instance
146
+ # We don't automatically invoke factory function results anymore
147
+ binding.instance = result
148
+ return binding.instance
149
+
150
+ # If neither concrete class nor function is defined
151
+ else:
152
+ raise OrionisContainerException(
153
+ "Cannot resolve singleton binding: neither a concrete class, instance, nor function is defined."
154
+ )
155
+
156
+ def __instantiateConcreteWithArgs(self, concrete: Callable[..., Any], *args, **kwargs) -> Any:
157
+ """
158
+ Instantiates a concrete class with the provided arguments.
159
+
160
+ Parameters
161
+ ----------
162
+ concrete : Callable[..., Any]
163
+ Class to instantiate.
164
+ *args : tuple
165
+ Positional arguments to pass to the constructor.
166
+ **kwargs : dict
167
+ Keyword arguments to pass to the constructor.
168
+
169
+ Returns
170
+ -------
171
+ object
172
+ A new instance of the specified concrete class.
173
+ """
174
+
175
+ # try to instantiate the concrete class with the provided arguments
176
+ try:
177
+
178
+ # If the concrete is a class, instantiate it directly
179
+ return concrete(*args, **kwargs)
180
+
181
+ except TypeError as e:
182
+
183
+ # If instantiation fails, use ReflectionConcrete to get class name and constructor signature
184
+ rf_concrete = ReflectionConcrete(concrete)
185
+ class_name = rf_concrete.getClassName()
186
+ signature = rf_concrete.getConstructorSignature()
187
+
188
+ # Raise an exception with detailed information about the failure
189
+ raise OrionisContainerException(
190
+ f"Failed to instantiate [{class_name}] with the provided arguments: {e}\n"
191
+ f"Expected constructor signature: [{signature}]"
192
+ ) from e
193
+
194
+ def __instantiateCallableWithArgs(self, fn: Callable[..., Any], *args, **kwargs) -> Any:
195
+ """
196
+ Invokes a callable with the provided arguments.
197
+
198
+ Parameters
199
+ ----------
200
+ fn : Callable[..., Any]
201
+ The callable to invoke.
202
+ *args : tuple
203
+ Positional arguments to pass to the callable.
204
+ **kwargs : dict
205
+ Keyword arguments to pass to the callable.
206
+
207
+ Returns
208
+ -------
209
+ Any
210
+ The result of the callable.
211
+ """
212
+
213
+ # Try to invoke the callable with the provided arguments
214
+ try:
215
+
216
+ # If the callable is a function, invoke it directly
217
+ return fn(*args, **kwargs)
218
+
219
+ except TypeError as e:
220
+
221
+ # If invocation fails, use ReflectionCallable to get function name and signature
222
+ rf_callable = ReflectionCallable(fn)
223
+ function_name = rf_callable.getName()
224
+ signature = rf_callable.getSignature()
225
+
226
+ # Raise an exception with detailed information about the failure
227
+ raise OrionisContainerException(
228
+ f"Failed to invoke function [{function_name}] with the provided arguments: {e}\n"
229
+ f"Expected function signature: [{signature}]"
230
+ ) from e
231
+
232
+ def __instantiateConcreteReflective(self, concrete: Callable[..., Any]) -> Any:
233
+ """
234
+ Instantiates a concrete class reflectively, resolving its dependencies from the container.
235
+
236
+ Parameters
237
+ ----------
238
+ concrete : Callable[..., Any]
239
+ The concrete class to instantiate.
240
+
241
+ Returns
242
+ -------
243
+ Any
244
+ A new instance of the concrete class.
245
+ """
246
+ # Resolve dependencies for the concrete class
247
+ params = self.__resolveDependencies(concrete, is_class=True)
248
+
249
+ # Instantiate the concrete class with resolved dependencies
250
+ return concrete(**params)
251
+
252
+ def __instantiateCallableReflective(self, fn: Callable[..., Any]) -> Any:
253
+ """
254
+ Invokes a callable reflectively, resolving its dependencies from the container.
255
+
256
+ Parameters
257
+ ----------
258
+ fn : Callable[..., Any]
259
+ The callable to invoke.
260
+
261
+ Returns
262
+ -------
263
+ Any
264
+ The result of the callable.
265
+ """
266
+
267
+ # Resolve dependencies for the callable
268
+ params = self.__resolveDependencies(fn, is_class=False)
269
+
270
+ # Invoke the callable with resolved dependencies
271
+ return fn(**params)
272
+
273
+ def __resolveDependencies(
274
+ self,
275
+ target: Callable[..., Any],
276
+ *,
277
+ is_class: bool = False
278
+ ) -> dict:
279
+ """
280
+ Resolves dependencies for a target callable or class.
281
+
282
+ Parameters
283
+ ----------
284
+ target : Callable[..., Any]
285
+ The target callable or class whose dependencies to resolve.
286
+ is_class : bool, optional
287
+ Whether the target is a class (True) or a callable (False).
288
+
289
+ Returns
290
+ -------
291
+ dict
292
+ A dictionary of resolved dependencies.
27
293
  """
28
- return self.container.transient(service, implementation, **kwargs)
294
+ try:
295
+
296
+ # Use ReflectionConcrete for classes and ReflectionCallable for callables
297
+ if is_class:
298
+ reflection = ReflectionConcrete(target)
299
+ dependencies = reflection.getConstructorDependencies()
300
+ name = reflection.getClassName()
301
+
302
+ # If the target is a callable, use ReflectionCallable
303
+ else:
304
+ reflection = ReflectionCallable(target)
305
+ dependencies = reflection.getDependencies()
306
+ name = reflection.getName()
307
+
308
+ # Check for unresolved dependencies
309
+ if dependencies.unresolved:
310
+ unresolved_args = ', '.join(dependencies.unresolved)
311
+ raise OrionisContainerException(
312
+ f"Cannot resolve '{name}' because the following required arguments are missing: [{unresolved_args}]."
313
+ )
314
+
315
+ # Resolve dependencies
316
+ params = {}
317
+ for param_name, dep in dependencies.resolved.items():
318
+
319
+ # If the dependency is a ResolvedDependency, resolve it
320
+ if isinstance(dep, ResolvedDependency):
321
+
322
+ # If the dependency is a built-in type, raise an exception
323
+ if dep.module_name == 'builtins':
324
+ raise OrionisContainerException(
325
+ f"Cannot resolve '{name}' because parameter '{param_name}' depends on built-in type '{dep.type.__name__}'."
326
+ )
327
+
328
+ # Try to resolve from container using type (Abstract or Interface)
329
+ if self.container.bound(dep.type):
330
+ params[param_name] = self.resolve(
331
+ self.container.getBinding(dep.type)
332
+ )
333
+
334
+ # Try to resolve from container using full class path
335
+ elif self.container.bound(dep.full_class_path):
336
+ params[param_name] = self.resolve(
337
+ self.container.getBinding(dep.full_class_path)
338
+ )
339
+
340
+ # Try to instantiate directly if it's a concrete class
341
+ elif ReflectionConcrete.isConcreteClass(dep.type):
342
+ params[param_name] = dep.type(**self.__resolveDependencies(dep.type, is_class=True))
343
+
344
+ # Try to call directly if it's a callable
345
+ elif callable(dep.type) and not isinstance(dep.type, type):
346
+ params[param_name] = dep.type(**self.__resolveDependencies(dep.type, is_class=False))
347
+
348
+ # If the dependency cannot be resolved, raise an exception
349
+ else:
350
+ raise OrionisContainerException(
351
+ f"Cannot resolve dependency '{param_name}' of type '{dep.type.__name__}' for '{name}'."
352
+ )
353
+ else:
354
+ # Use default value
355
+ params[param_name] = dep
356
+
357
+ # Return the resolved parameters
358
+ return params
359
+
360
+ except ImportError as e:
361
+
362
+ # Get target name safely
363
+ target_name = getattr(target, '__name__', str(target))
364
+ module_name = getattr(target, '__module__', "unknown module")
365
+
366
+ # Improved circular import detection with more helpful guidance
367
+ if "circular import" in str(e).lower() or "cannot import name" in str(e).lower():
368
+ raise OrionisContainerException(
369
+ f"Circular import detected while resolving dependencies for '{target_name}' in module '{module_name}'.\n"
370
+ f"This typically happens when two modules import each other. Consider:\n"
371
+ f"1. Restructuring your code to avoid circular dependencies\n"
372
+ f"2. Using delayed imports inside methods rather than at module level\n"
373
+ f"3. Using dependency injection to break the cycle\n"
374
+ f"Original error: {str(e)}"
375
+ ) from e
376
+ else:
377
+ raise OrionisContainerException(
378
+ f"Import error while resolving dependencies for '{target_name}' in module '{module_name}':\n"
379
+ f"{str(e)}"
380
+ ) from e
381
+
382
+ except Exception as e:
383
+
384
+ # More robust attribute extraction with fallbacks
385
+ target_type = "class" if isinstance(target, type) else "function"
386
+ target_name = getattr(target, '__name__', str(target))
387
+ module_name = getattr(target, '__module__', "unknown module")
388
+
389
+ # More detailed error message with context about the failure
390
+ error_msg = (
391
+ f"Error resolving dependencies for {target_type} '{target_name}' in '{module_name}':\n"
392
+ f"{str(e)}\n"
393
+ )
394
+
395
+ # Add specific guidance based on error type
396
+ if isinstance(e, TypeError):
397
+ error_msg += "This may be caused by incompatible argument types or missing required parameters."
398
+ elif isinstance(e, AttributeError):
399
+ error_msg += "This may be caused by accessing undefined attributes in the dependency chain."
400
+ error_msg += "\nCheck that all dependencies are properly registered in the container."
401
+
402
+ # Raise a more informative exception
403
+ raise OrionisContainerException(error_msg) from e
404
+
@@ -0,0 +1,53 @@
1
+ from typing import Any, Union
2
+ from orionis.container.enums.lifetimes import Lifetime
3
+ from orionis.container.exceptions.type_error_exception import OrionisContainerTypeError
4
+
5
+ class __LifetimeValidator:
6
+ """
7
+ Validator that checks if a value is a valid lifetime and converts string representations
8
+ to Lifetime enum values.
9
+ """
10
+
11
+ def __call__(self, lifetime: Union[str, Lifetime, Any]) -> Lifetime:
12
+ """
13
+ Validates and normalizes the provided lifetime value.
14
+
15
+ Parameters
16
+ ----------
17
+ lifetime : Union[str, Lifetime, Any]
18
+ The lifetime value to validate. Can be a Lifetime enum or a string
19
+ representing a valid lifetime.
20
+
21
+ Returns
22
+ -------
23
+ Lifetime
24
+ The validated Lifetime enum value.
25
+
26
+ Raises
27
+ ------
28
+ OrionisContainerTypeError
29
+ If the value is not a valid Lifetime enum or string representation,
30
+ or if the string doesn't match any valid Lifetime value.
31
+ """
32
+ # Already a Lifetime enum
33
+ if isinstance(lifetime, Lifetime):
34
+ return lifetime
35
+
36
+ # String that might represent a Lifetime
37
+ if isinstance(lifetime, str):
38
+ lifetime_key = lifetime.strip().upper()
39
+ if lifetime_key in Lifetime.__members__:
40
+ return Lifetime[lifetime_key]
41
+
42
+ valid_options = ', '.join(Lifetime.__members__.keys())
43
+ raise OrionisContainerTypeError(
44
+ f"Invalid lifetime '{lifetime}'. Valid options are: {valid_options}."
45
+ )
46
+
47
+ # Invalid type
48
+ raise OrionisContainerTypeError(
49
+ f"Lifetime must be of type str or Lifetime enum, got {type(lifetime).__name__}."
50
+ )
51
+
52
+ # Exported singleton instance
53
+ LifetimeValidator = __LifetimeValidator()
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.320.0"
8
+ VERSION = "0.321.0"
9
9
 
10
10
  # Full name of the author or maintainer of the project
11
11
  AUTHOR = "Raul Mauricio Uñate Castro"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: orionis
3
- Version: 0.320.0
3
+ Version: 0.321.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,9 +135,9 @@ 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=HHEXnObv1Q_022dfGpTOx6cwyRPa7ooD8FfqJ_cDCro,37291
139
- orionis/container/resolver.py,sha256=9nojqjrYYXw2kQUzyuLkHs-os5dDxq6ntZ8e8IbYIu0,704
140
- orionis/container/contracts/container.py,sha256=LkZ5til_3Um8ctCVTwuO36HkysL59saKwUzXR5cWUBs,5750
138
+ orionis/container/container.py,sha256=eBVQMzfSvXcxrdm-0hq0gmXZzxf3uabIcxbGVvGZmIo,22342
139
+ orionis/container/resolver.py,sha256=iuyTWCnCqWXQCtWvFXPdS9cR_g3Q58_wjHVn0Yg0YNM,16209
140
+ orionis/container/contracts/container.py,sha256=ksMn6ZLnFkIxd9_hbUGz3OpHzaYTA5izcDtH959kOow,7603
141
141
  orionis/container/entities/binding.py,sha256=Qp6Lf4XUDp2NjqXDAC2lzvhOFQWiBDKiGFcKfwb4axw,4342
142
142
  orionis/container/enums/lifetimes.py,sha256=RqQmugMIB1Ev_j_vFLcWorndm-to7xg4stQ7yKFDdDw,190
143
143
  orionis/container/exceptions/container_exception.py,sha256=goTDEwC70xTMD2qppN8KV-xyR0Nps218OD4D1LZ2-3s,470
@@ -152,6 +152,7 @@ orionis/container/validators/is_instance.py,sha256=vUwWIMeG1zLMsGX9GyoOU-LZtCgqB
152
152
  orionis/container/validators/is_not_subclass.py,sha256=-iZw5ZCYnQtlU-xPO-o7lUj5DxPkN0G67-bLbLVhwtw,1167
153
153
  orionis/container/validators/is_subclass.py,sha256=O2aF0KLg5ZnDutKQ1xDvet34kentftlqgEkRNvumCoM,1135
154
154
  orionis/container/validators/is_valid_alias.py,sha256=avm6uz-huVi_kRov4BksSToslIHQfz-MLynjkzhdSRM,1246
155
+ orionis/container/validators/lifetime.py,sha256=78o7BHBCRReG8vnF0gW2RUf6m6wwMhr7w4VA6F2B46A,1865
155
156
  orionis/foundation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
156
157
  orionis/foundation/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
157
158
  orionis/foundation/config/startup.py,sha256=JKAH2ZRhlAZgkD2w11LR1-TVktfjSH9cHo3PsZXOLrg,8275
@@ -243,7 +244,7 @@ orionis/foundation/contracts/config.py,sha256=Rpz6U6t8OXHO9JJKSTnCimytXE-tfCB-1i
243
244
  orionis/foundation/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
244
245
  orionis/foundation/exceptions/integrity.py,sha256=mc4pL1UMoYRHEmphnpW2oGk5URhu7DJRREyzHaV-cs8,472
245
246
  orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
246
- orionis/metadata/framework.py,sha256=sBQIEDehFdisleRXQSFyOhHx2bVTGjprHNNBI_Famsw,4960
247
+ orionis/metadata/framework.py,sha256=gv-aVbvyQwRvOTPPCJTIwJffZOEd9Ak6L9jdwT46oOE,4960
247
248
  orionis/metadata/package.py,sha256=tqLfBRo-w1j_GN4xvzUNFyweWYFS-qhSgAEc-AmCH1M,5452
248
249
  orionis/patterns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
249
250
  orionis/patterns/singleton/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -355,7 +356,7 @@ orionis/test/suite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuF
355
356
  orionis/test/suite/test_unit.py,sha256=MWgW8dRCRyT1XZ5LsbXQ7-KVPReasoXwzEEL1EWWfE4,52190
356
357
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
357
358
  orionis/test/view/render.py,sha256=jXZkbITBknbUwm_mD8bcTiwLDvsFkrO9qrf0ZgPwqxc,4903
358
- orionis-0.320.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
359
+ orionis-0.321.0.dist-info/licenses/LICENCE,sha256=-_4cF2EBKuYVS_SQpy1uapq0oJPUU1vl_RUWSy2jJTo,1111
359
360
  tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
360
361
  tests/example/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
361
362
  tests/example/test_example.py,sha256=kvWgiW3ADEZf718dGsMPtDh_rmOSx1ypEInKm7_6ZPQ,601
@@ -456,8 +457,8 @@ tests/support/wrapper/test_services_wrapper_docdict.py,sha256=yeVwl-VcwkWSQYyxZu
456
457
  tests/testing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
457
458
  tests/testing/test_testing_result.py,sha256=MrGK3ZimedL0b5Ydu69Dg8Iul017AzLTm7VPxpXlpfU,4315
458
459
  tests/testing/test_testing_unit.py,sha256=DjLBtvVn8B1KlVJNNkstBT8_csA1yeaMqnGrbanN_J4,7438
459
- orionis-0.320.0.dist-info/METADATA,sha256=J4HkjZk_1Wa2wuK0UZboolNPepT3CA4uflTNQrXlGk0,4772
460
- orionis-0.320.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
461
- orionis-0.320.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
462
- orionis-0.320.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
463
- orionis-0.320.0.dist-info/RECORD,,
460
+ orionis-0.321.0.dist-info/METADATA,sha256=kQ_DamwqrnAqkqPJQZd6mR4u-U_ERbRLwEw8FqTgTwE,4772
461
+ orionis-0.321.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
462
+ orionis-0.321.0.dist-info/top_level.txt,sha256=2bdoHgyGZhOtLAXS6Om8OCTmL24dUMC_L1quMe_ETbk,14
463
+ orionis-0.321.0.dist-info/zip-safe,sha256=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN_XKdLCPjaYaY,2
464
+ orionis-0.321.0.dist-info/RECORD,,