orionis 0.656.0__py3-none-any.whl → 0.658.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.
@@ -3,8 +3,7 @@ import asyncio
3
3
  import inspect
4
4
  import threading
5
5
  import typing
6
- from pathlib import Path
7
- from typing import Any, Callable, Optional
6
+ from typing import Any, Callable, Optional, Union
8
7
  from orionis.container.context.manager import ScopeManager
9
8
  from orionis.container.context.scope import ScopedContext
10
9
  from orionis.container.contracts.container import IContainer
@@ -13,13 +12,7 @@ from orionis.container.enums.lifetimes import Lifetime
13
12
  from orionis.container.exceptions import OrionisContainerException
14
13
  from orionis.container.exceptions.container import OrionisContainerTypeError
15
14
  from orionis.container.validators import (
16
- ImplementsAbstractMethods,
17
- IsAbstractClass,
18
- IsConcreteClass,
19
- IsInstance,
20
15
  IsCallable,
21
- IsSubclass,
22
- IsNotSubclass,
23
16
  IsValidAlias,
24
17
  LifetimeValidator
25
18
  )
@@ -452,6 +445,7 @@ class Container(IContainer):
452
445
 
453
446
  # If an alias is provided, validate and use it directly
454
447
  if alias:
448
+
455
449
  # Check for None, empty string, or whitespace-only alias
456
450
  if alias is None or alias == "" or str(alias).isspace():
457
451
  raise OrionisContainerTypeError(
@@ -477,6 +471,47 @@ class Container(IContainer):
477
471
  # If no alias is provided, generate a default alias using module and class name
478
472
  return f"{abstract.__module__}.{abstract.__name__}"
479
473
 
474
+ def __validateLifetime(self, lifetime: Union[str, Lifetime, Any]) -> Lifetime:
475
+ """
476
+ Validates and normalizes the provided lifetime value.
477
+
478
+ Parameters
479
+ ----------
480
+ lifetime : Union[str, Lifetime, Any]
481
+ The lifetime value to validate. Can be a Lifetime enum or a string
482
+ representing a valid lifetime.
483
+
484
+ Returns
485
+ -------
486
+ Lifetime
487
+ The validated Lifetime enum value.
488
+
489
+ Raises
490
+ ------
491
+ OrionisContainerTypeError
492
+ If the value is not a valid Lifetime enum or string representation,
493
+ or if the string doesn't match any valid Lifetime value.
494
+ """
495
+ # Already a Lifetime enum
496
+ if isinstance(lifetime, Lifetime):
497
+ return lifetime
498
+
499
+ # String that might represent a Lifetime
500
+ if isinstance(lifetime, str):
501
+ lifetime_key = lifetime.strip().upper()
502
+ if lifetime_key in Lifetime.__members__:
503
+ return Lifetime[lifetime_key]
504
+
505
+ valid_options = ', '.join(Lifetime.__members__.keys())
506
+ raise OrionisContainerTypeError(
507
+ f"Invalid lifetime '{lifetime}'. Valid options are: {valid_options}."
508
+ )
509
+
510
+ # Invalid type
511
+ raise OrionisContainerTypeError(
512
+ f"Lifetime must be of type str or Lifetime enum, got {type(lifetime).__name__}."
513
+ )
514
+
480
515
  def transient(
481
516
  self,
482
517
  abstract: Callable[..., Any],
@@ -879,155 +914,174 @@ class Container(IContainer):
879
914
  """
880
915
  Registers an instance of a class or interface in the container with scoped lifetime.
881
916
 
917
+ This method validates the abstract type, the instance, and the alias (if provided).
918
+ It ensures that the instance is a valid implementation of the abstract class or interface,
919
+ optionally enforces decoupling, and registers the instance in the container under both
920
+ the abstract type and the alias. The registered instance will be available only within
921
+ the current active scope context.
922
+
882
923
  Parameters
883
924
  ----------
884
925
  abstract : Callable[..., Any]
885
- The abstract class or interface to associate with the instance.
926
+ The abstract class or interface to associate with the instance. Must be an abstract class.
886
927
  instance : Any
887
- The concrete instance to register.
928
+ The concrete instance to register. Must be a valid instance of the abstract type.
888
929
  alias : str, optional
889
- An optional alias to register the instance under. If not provided,
890
- the abstract's `__name__` attribute will be used as the alias if available.
930
+ An optional alias to register the instance under. If not provided, a default alias is generated
931
+ using the abstract's module and class name.
891
932
  enforce_decoupling : bool, optional
892
- Whether to enforce decoupling between abstract and concrete types.
933
+ If True, enforces that the instance's class does NOT inherit from the abstract class.
934
+ If False, requires that the instance's class is a subclass of the abstract.
893
935
 
894
936
  Returns
895
937
  -------
896
- bool
897
- True if the instance was successfully registered.
938
+ bool or None
939
+ Returns True if the instance was successfully registered in the container and scope.
940
+ Returns None if registration fails due to an exception.
898
941
 
899
942
  Raises
900
943
  ------
901
- TypeError
944
+ OrionisContainerTypeError
902
945
  If `abstract` is not an abstract class or if `alias` is not a valid string.
903
- ValueError
904
- If `instance` is not a valid instance of `abstract`.
905
946
  OrionisContainerException
906
- If no active scope is found.
947
+ If the instance is not a valid implementation, fails decoupling check,
948
+ or if registration fails for any other reason.
949
+ OrionisContainerException
950
+ If no active scope is found when attempting to store the instance in the scope.
907
951
 
908
952
  Notes
909
953
  -----
910
- This method registers the instance with scoped lifetime, meaning it will be
911
- available only within the current active scope. If no scope is active,
912
- an exception will be raised.
954
+ - The instance is registered with scoped lifetime, meaning it will be available only
955
+ within the current active scope context.
956
+ - All abstract methods must be implemented by the instance.
957
+ - If a service is already registered under the same abstract or alias, it is removed
958
+ before registering the new instance.
959
+ - If no scope is active, the instance will not be stored in any scope context.
913
960
  """
914
961
 
915
- # Ensure that the abstract is an abstract class
916
- IsAbstractClass(abstract, f"Instance {Lifetime.SCOPED}")
962
+ try:
917
963
 
918
- # Ensure that the instance is a valid instance
919
- IsInstance(instance)
964
+ # Ensure that the abstract is an abstract class
965
+ ReflectionAbstract.ensureIsAbstractClass(abstract)
920
966
 
921
- # Ensure that instance is NOT a subclass of abstract
922
- if enforce_decoupling:
923
- IsNotSubclass(abstract, instance.__class__)
924
- else:
925
- # Validate that instance is a subclass of abstract
926
- IsSubclass(abstract, instance.__class__)
967
+ # Ensure that the instance is a valid instance of the abstract
968
+ ReflectionInstance.ensureIsInstance(instance)
927
969
 
928
- # Ensure implementation
929
- ImplementsAbstractMethods(
930
- abstract=abstract,
931
- instance=instance
932
- )
970
+ # Enforce decoupling or subclass relationship as specified
971
+ self.__decouplingCheck(abstract, instance.__class__, enforce_decoupling)
933
972
 
934
- # Ensure that the alias is a valid string if provided
935
- if alias:
936
- IsValidAlias(alias)
937
- else:
938
- alias = f"{abstract.__module__}.{abstract.__name__}"
939
-
940
- # If the service is already registered in container bindings, drop it
941
- self.drop(abstract, alias)
942
-
943
- # Register the binding in the container for future scope resolutions
944
- self.__bindings[abstract] = Binding(
945
- contract=abstract,
946
- instance=instance,
947
- lifetime=Lifetime.SCOPED,
948
- enforce_decoupling=enforce_decoupling,
949
- alias=alias
950
- )
973
+ # Ensure all abstract methods are implemented by the instance
974
+ self.__implementsAbstractMethods(
975
+ abstract=abstract,
976
+ instance=instance
977
+ )
951
978
 
952
- # Register the alias
953
- self.__aliases[alias] = self.__bindings[abstract]
979
+ # Validate and generate the alias key (either provided or default)
980
+ alias = self.__makeAliasKey(abstract, alias)
954
981
 
955
- # Store the instance directly in the current scope
956
- scope = ScopedContext.getCurrentScope()
957
- if scope:
958
- scope[abstract] = instance
959
- if alias != abstract:
960
- scope[alias] = instance
982
+ # Remove any existing binding for this abstract or alias
983
+ self.drop(abstract, alias)
961
984
 
962
- # Return True to indicate successful registration
963
- return True
985
+ # Register the instance with scoped lifetime in the container bindings
986
+ self.__bindings[abstract] = Binding(
987
+ contract=abstract,
988
+ instance=instance,
989
+ lifetime=Lifetime.SCOPED,
990
+ enforce_decoupling=enforce_decoupling,
991
+ alias=alias
992
+ )
993
+
994
+ # Register the alias for lookup
995
+ self.__aliases[alias] = self.__bindings[abstract]
996
+
997
+ # Store the instance directly in the current scope, if a scope is active
998
+ scope = ScopedContext.getCurrentScope()
999
+ if scope:
1000
+ scope[abstract] = instance
1001
+ scope[alias] = scope[abstract]
964
1002
 
965
-
1003
+ # Return True to indicate successful registration
1004
+ return True
1005
+
1006
+ except Exception as e:
1007
+
1008
+ # Raise a container exception with details if registration fails
1009
+ raise OrionisContainerException(
1010
+ f"Unexpected error registering scoped instance: {e}"
1011
+ ) from e
966
1012
 
967
1013
  def callable(
968
1014
  self,
969
- alias: str,
970
1015
  fn: Callable[..., Any],
971
1016
  *,
972
- lifetime: Lifetime = Lifetime.TRANSIENT
1017
+ alias: str
973
1018
  ) -> Optional[bool]:
974
1019
  """
975
- Registers a function or factory under a given alias.
1020
+ Registers a function or factory under a given alias with transient lifetime.
1021
+
1022
+ This method registers a callable (function or factory) in the container and associates it with a unique alias.
1023
+ The registered function will be resolved with transient lifetime, meaning a new result is produced each time it is invoked.
1024
+ The alias is validated for uniqueness and correctness, and any previous registration under the same alias is removed.
976
1025
 
977
1026
  Parameters
978
1027
  ----------
979
- alias : str
980
- The alias to register the function under.
981
1028
  fn : Callable[..., Any]
982
- The function or factory to register.
983
- lifetime : Lifetime, optional
984
- The lifetime of the function registration (default is TRANSIENT).
1029
+ The function or factory to register. Must be a valid Python callable.
1030
+ alias : str
1031
+ The alias to register the function under. Must be a non-empty, valid string.
985
1032
 
986
1033
  Returns
987
1034
  -------
988
- bool
989
- True if the function was registered successfully.
1035
+ bool or None
1036
+ Returns True if the function was registered successfully.
1037
+ Returns None if registration fails due to an exception.
990
1038
 
991
1039
  Raises
992
1040
  ------
993
1041
  OrionisContainerTypeError
994
1042
  If the alias is invalid or the function is not callable.
995
1043
  OrionisContainerException
996
- If the lifetime is not allowed for the function signature.
997
- """
1044
+ If an unexpected error occurs during registration.
998
1045
 
999
- # Normalize and validate the lifetime parameter
1000
- lifetime = LifetimeValidator(lifetime)
1046
+ Notes
1047
+ -----
1048
+ - The function is registered with transient lifetime, so each invocation produces a new result.
1049
+ - If a service is already registered under the same alias, it is removed before registering the new function.
1050
+ - The alias is validated for uniqueness and correctness.
1051
+ """
1001
1052
 
1002
- # Ensure that the alias is a valid string
1003
- IsValidAlias(alias)
1053
+ try:
1054
+ # Validate and normalize the alias using the internal alias key generator
1055
+ alias = self.__makeAliasKey(lambda: None, alias)
1004
1056
 
1005
- # Validate that the function is callable
1006
- IsCallable(fn)
1057
+ # Ensure the provided fn is actually callable
1058
+ if not callable(fn):
1059
+ raise OrionisContainerTypeError(
1060
+ f"Expected a callable type, but got {type(fn).__name__} instead."
1061
+ )
1007
1062
 
1008
- # Inspect the function signature
1009
- params: ResolveArguments = ReflectionCallable(fn).getDependencies()
1063
+ # Remove any existing registration under this alias
1064
+ self.drop(None, alias)
1010
1065
 
1011
- # If the function requires arguments, only allow TRANSIENT
1012
- if (len(params.resolved) + len(params.unresolved)) > 0 and lifetime != Lifetime.TRANSIENT:
1013
- raise OrionisContainerException(
1014
- "Functions that require arguments can only be registered with a TRANSIENT lifetime."
1066
+ # Register the function in the bindings dictionary with transient lifetime
1067
+ self.__bindings[alias] = Binding(
1068
+ function=fn,
1069
+ lifetime=Lifetime.TRANSIENT,
1070
+ alias=alias
1015
1071
  )
1016
1072
 
1017
- # If the service is already registered, drop it
1018
- self.drop(None, alias)
1073
+ # Register the alias for lookup in the aliases dictionary
1074
+ self.__aliases[alias] = self.__bindings[alias]
1019
1075
 
1020
- # Register the function with the specified alias and lifetime
1021
- self.__bindings[alias] = Binding(
1022
- function=fn,
1023
- lifetime=lifetime,
1024
- alias=alias
1025
- )
1076
+ # Return True to indicate successful registration
1077
+ return True
1026
1078
 
1027
- # Register the function as a binding
1028
- self.__aliases[alias] = self.__bindings[alias]
1079
+ except Exception as e:
1029
1080
 
1030
- return True
1081
+ # Raise a container exception with details if registration fails
1082
+ raise OrionisContainerException(
1083
+ f"Unexpected error registering callable: {e}"
1084
+ ) from e
1031
1085
 
1032
1086
  def bound(
1033
1087
  self,
@@ -2954,4 +3008,4 @@ class Container(IContainer):
2954
3008
  except TypeError:
2955
3009
  raise OrionisContainerException(
2956
3010
  f"Failed to call method '{method.__name__}': {reflection_error}"
2957
- ) from reflection_error
3011
+ ) from reflection_error
@@ -5,7 +5,7 @@
5
5
  NAME = "orionis"
6
6
 
7
7
  # Current version of the framework
8
- VERSION = "0.656.0"
8
+ VERSION = "0.658.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.656.0
3
+ Version: 0.658.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
@@ -67,7 +67,7 @@ orionis/console/stubs/listener.stub,sha256=DbX-ghx2-vb73kzQ6L20lyg5vSKn58jSLTwFu
67
67
  orionis/console/tasks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
68
  orionis/console/tasks/schedule.py,sha256=ZeeuQ9Tbu5KNowKC5oIk7yWeJXzlDQiQ278mWEgoCXc,87989
69
69
  orionis/container/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
- orionis/container/container.py,sha256=wykXRHbXuj5uR_dLWSGV2dP6HJhat9O22xhZ1qrJ710,112458
70
+ orionis/container/container.py,sha256=I6cTRRlDW0kf_7MMKIKXdeNXRur-slMfUTtLRNFyzbA,115945
71
71
  orionis/container/context/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
72
  orionis/container/context/manager.py,sha256=I08K_jKXSKmrq18Pv33qYyMKIlAovVOwIgmfiVm-J7c,2971
73
73
  orionis/container/context/scope.py,sha256=p_oCzR7dDz-5ZAd16ab4vfLc3gBf34CaN0f4iR9D0bQ,1155
@@ -217,7 +217,7 @@ orionis/foundation/providers/scheduler_provider.py,sha256=IrPQJwvQVLRm5Qnz0Cxon4
217
217
  orionis/foundation/providers/testing_provider.py,sha256=eI1p2lUlxl25b5Z487O4nmqLE31CTDb4c3Q21xFadkE,1615
218
218
  orionis/foundation/providers/workers_provider.py,sha256=GdHENYV_yGyqmHJHn0DCyWmWId5xWjD48e6Zq2PGCWY,1674
219
219
  orionis/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
220
- orionis/metadata/framework.py,sha256=uH6CGiZrnrIE3TYvHlpii4F1uxO9t1BN4Id6KNwWxJk,4089
220
+ orionis/metadata/framework.py,sha256=KfWV8jwgxZcdeY4hlBEHan1VFrSifKQbbvgnOH4dY64,4089
221
221
  orionis/metadata/package.py,sha256=k7Yriyp5aUcR-iR8SK2ec_lf0_Cyc-C7JczgXa-I67w,16039
222
222
  orionis/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
223
223
  orionis/services/asynchrony/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -404,8 +404,8 @@ orionis/test/validators/workers.py,sha256=rWcdRexINNEmGaO7mnc1MKUxkHKxrTsVuHgbnI
404
404
  orionis/test/view/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
405
405
  orionis/test/view/render.py,sha256=R55ykeRs0wDKcdTf4O1YZ8GDHTFmJ0IK6VQkbJkYUvo,5571
406
406
  orionis/test/view/report.stub,sha256=QLqqCdRoENr3ECiritRB3DO_MOjRQvgBh5jxZ3Hs1r0,28189
407
- orionis-0.656.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
408
- orionis-0.656.0.dist-info/METADATA,sha256=79-r5BwwIuNJpbXoN6ATvQMgwYDppodZ7F9pWmW4-9Y,4772
409
- orionis-0.656.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
410
- orionis-0.656.0.dist-info/top_level.txt,sha256=lyXi6jArpqJ-0zzNqd_uwsH-z9TCEBVBL-pC3Ekv7hU,8
411
- orionis-0.656.0.dist-info/RECORD,,
407
+ orionis-0.658.0.dist-info/licenses/LICENCE,sha256=JhC-z_9mbpUrCfPjcl3DhDA8trNDMzb57cvRSam1avc,1463
408
+ orionis-0.658.0.dist-info/METADATA,sha256=pmVWTkhfAxoTOYGL1SFjGeLl-Kat1Un9nPNGZvp56rE,4772
409
+ orionis-0.658.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
410
+ orionis-0.658.0.dist-info/top_level.txt,sha256=lyXi6jArpqJ-0zzNqd_uwsH-z9TCEBVBL-pC3Ekv7hU,8
411
+ orionis-0.658.0.dist-info/RECORD,,