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.
- orionis/container/container.py +155 -101
- orionis/metadata/framework.py +1 -1
- {orionis-0.656.0.dist-info → orionis-0.658.0.dist-info}/METADATA +1 -1
- {orionis-0.656.0.dist-info → orionis-0.658.0.dist-info}/RECORD +7 -7
- {orionis-0.656.0.dist-info → orionis-0.658.0.dist-info}/WHEEL +0 -0
- {orionis-0.656.0.dist-info → orionis-0.658.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.656.0.dist-info → orionis-0.658.0.dist-info}/top_level.txt +0 -0
orionis/container/container.py
CHANGED
|
@@ -3,8 +3,7 @@ import asyncio
|
|
|
3
3
|
import inspect
|
|
4
4
|
import threading
|
|
5
5
|
import typing
|
|
6
|
-
from
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
911
|
-
|
|
912
|
-
|
|
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
|
-
|
|
916
|
-
IsAbstractClass(abstract, f"Instance {Lifetime.SCOPED}")
|
|
962
|
+
try:
|
|
917
963
|
|
|
918
|
-
|
|
919
|
-
|
|
964
|
+
# Ensure that the abstract is an abstract class
|
|
965
|
+
ReflectionAbstract.ensureIsAbstractClass(abstract)
|
|
920
966
|
|
|
921
|
-
|
|
922
|
-
|
|
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
|
-
|
|
929
|
-
|
|
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
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
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
|
-
|
|
953
|
-
|
|
979
|
+
# Validate and generate the alias key (either provided or default)
|
|
980
|
+
alias = self.__makeAliasKey(abstract, alias)
|
|
954
981
|
|
|
955
|
-
|
|
956
|
-
|
|
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
|
-
|
|
963
|
-
|
|
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
|
-
|
|
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
|
-
|
|
984
|
-
The
|
|
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
|
|
997
|
-
"""
|
|
1044
|
+
If an unexpected error occurs during registration.
|
|
998
1045
|
|
|
999
|
-
|
|
1000
|
-
|
|
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
|
-
|
|
1003
|
-
|
|
1053
|
+
try:
|
|
1054
|
+
# Validate and normalize the alias using the internal alias key generator
|
|
1055
|
+
alias = self.__makeAliasKey(lambda: None, alias)
|
|
1004
1056
|
|
|
1005
|
-
|
|
1006
|
-
|
|
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
|
-
|
|
1009
|
-
|
|
1063
|
+
# Remove any existing registration under this alias
|
|
1064
|
+
self.drop(None, alias)
|
|
1010
1065
|
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
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
|
-
|
|
1018
|
-
|
|
1073
|
+
# Register the alias for lookup in the aliases dictionary
|
|
1074
|
+
self.__aliases[alias] = self.__bindings[alias]
|
|
1019
1075
|
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
function=fn,
|
|
1023
|
-
lifetime=lifetime,
|
|
1024
|
-
alias=alias
|
|
1025
|
-
)
|
|
1076
|
+
# Return True to indicate successful registration
|
|
1077
|
+
return True
|
|
1026
1078
|
|
|
1027
|
-
|
|
1028
|
-
self.__aliases[alias] = self.__bindings[alias]
|
|
1079
|
+
except Exception as e:
|
|
1029
1080
|
|
|
1030
|
-
|
|
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
|
orionis/metadata/framework.py
CHANGED
|
@@ -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=
|
|
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=
|
|
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.
|
|
408
|
-
orionis-0.
|
|
409
|
-
orionis-0.
|
|
410
|
-
orionis-0.
|
|
411
|
-
orionis-0.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|