orionis 0.22.0__tar.gz → 0.24.0__tar.gz
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-0.22.0/orionis.egg-info → orionis-0.24.0}/PKG-INFO +1 -1
- {orionis-0.22.0 → orionis-0.24.0}/orionis/framework.py +1 -1
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/cache/app/config.py +6 -0
- orionis-0.24.0/orionis/luminate/container/container.py +420 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/container/exception.py +18 -0
- orionis-0.24.0/orionis/luminate/contracts/container/container_interface.py +222 -0
- {orionis-0.22.0 → orionis-0.24.0/orionis.egg-info}/PKG-INFO +1 -1
- orionis-0.22.0/orionis/luminate/container/container.py +0 -385
- orionis-0.22.0/orionis/luminate/contracts/container/container_interface.py +0 -152
- {orionis-0.22.0 → orionis-0.24.0}/LICENCE +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/MANIFEST.in +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/README.md +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/cli_manager.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/app.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/bootstrap/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/bootstrap/config/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/bootstrap/config/bootstrapper.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/bootstrap/config/parser.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/bootstrap/config/register.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/cache/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/cache/app/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/cache/console/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/cache/console/commands.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/app.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/auth.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/cache.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/cors.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/database.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/filesystems.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/logging.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/mail.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/queue.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/dataclass/session.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/config/environment.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/base/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/base/command.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/cache.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/command.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/command_filter.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/commands/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/commands/cache_clear.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/commands/help.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/commands/schedule_work.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/commands/tests.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/commands/version.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/exceptions/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/exceptions/cli_exception.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/kernel.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/output/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/output/console.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/output/executor.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/output/progress_bar.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/output/styles.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/parser.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/register.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/runner.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/scripts/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/scripts/management.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/tasks/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/console/tasks/scheduler.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/container/types.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/bootstrap/config/bootstrapper_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/bootstrap/config/parser_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/bootstrap/config/register_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/cache/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/cache/cache_commands_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/config/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/config/config_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/config/environment_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/base_command_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/cli_cache_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/command_filter_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/command_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/console_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/executor_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/kernel_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/management_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/parser_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/progress_bar_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/register_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/runner_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/schedule_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/console/task_manager_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/container/types_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/facades/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/facades/env_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/facades/log_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/facades/paths_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/facades/tests_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/files/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/files/paths_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/installer/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/installer/output_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/installer/setup_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/installer/upgrade_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/log/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/log/logger_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/pipelines/cli_pipeline_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/publisher/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/publisher/pypi_publisher_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/test/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/test/unit_test_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/tools/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/tools/reflection_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/contracts/tools/std_interface.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/facades/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/facades/environment.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/facades/log.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/facades/paths.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/facades/tests.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/files/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/files/paths.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/installer/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/installer/icon.ascii +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/installer/info.ascii +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/installer/output.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/installer/setup.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/installer/upgrade.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/log/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/log/logger.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/pipelines/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/pipelines/cli_pipeline.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/publisher/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/publisher/pypi.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/static/bg/galaxy.jpg +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/static/favicon/OrionisFrameworkFavicon.png +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/static/logos/OrionisFramework.jpg +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/static/logos/OrionisFramework.png +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/static/logos/OrionisFramework.psd +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/static/logos/OrionisFramework2.png +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/static/logos/OrionisFramework3.png +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/test/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/test/exception.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/test/unit_test.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/tools/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/tools/dot_dict.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/tools/reflection.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis/luminate/tools/std.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis.egg-info/SOURCES.txt +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis.egg-info/dependency_links.txt +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis.egg-info/entry_points.txt +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis.egg-info/requires.txt +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/orionis.egg-info/top_level.txt +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/setup.cfg +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/setup.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/tests/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/tests/tools/__init__.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/tests/tools/class_example.py +0 -0
- {orionis-0.22.0 → orionis-0.24.0}/tests/tools/test_reflection.py +0 -0
@@ -11,6 +11,12 @@ class CacheConfig:
|
|
11
11
|
A dictionary storing registered configuration sections.
|
12
12
|
"""
|
13
13
|
def __init__(self) -> None:
|
14
|
+
"""
|
15
|
+
Initializes a new instance of the class with an empty configuration dictionary.
|
16
|
+
|
17
|
+
Attributes:
|
18
|
+
config (dict): A dictionary to store configuration settings.
|
19
|
+
"""
|
14
20
|
self.config = {}
|
15
21
|
|
16
22
|
def register(self, section: str, data: Dict[str, Any]) -> None:
|
@@ -0,0 +1,420 @@
|
|
1
|
+
import inspect
|
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
|
6
|
+
from orionis.luminate.container.types import Types
|
7
|
+
from orionis.luminate.contracts.container.container_interface import IContainer
|
8
|
+
|
9
|
+
BINDING = 'binding'
|
10
|
+
TRANSIENT = 'transient'
|
11
|
+
SINGLETON = 'singleton'
|
12
|
+
SCOPED = 'scoped'
|
13
|
+
INSTANCE = 'instance'
|
14
|
+
|
15
|
+
class Container(IContainer):
|
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
|
+
"""
|
22
|
+
|
23
|
+
_instance = None
|
24
|
+
_lock = Lock()
|
25
|
+
|
26
|
+
def __new__(cls):
|
27
|
+
if cls._instance is None:
|
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()
|
39
|
+
return cls._instance
|
40
|
+
|
41
|
+
def _newRequest(self) -> None:
|
42
|
+
"""
|
43
|
+
Reset scoped instances at the beginning of a new request.
|
44
|
+
"""
|
45
|
+
self._scoped_instances = {}
|
46
|
+
|
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__}"
|
71
|
+
|
72
|
+
def _ensureUniqueService(self, obj: Any) -> None:
|
73
|
+
"""
|
74
|
+
Ensure that a service is not already registered.
|
75
|
+
|
76
|
+
Parameters
|
77
|
+
----------
|
78
|
+
obj : Any
|
79
|
+
The service to check.
|
80
|
+
|
81
|
+
Raises
|
82
|
+
------
|
83
|
+
OrionisContainerValueError
|
84
|
+
If the service is already registered.
|
85
|
+
"""
|
86
|
+
if self.has(obj):
|
87
|
+
raise OrionisContainerValueError("The service is already registered in the container.")
|
88
|
+
|
89
|
+
def _ensureIsCallable(self, concrete: Callable[..., Any]) -> None:
|
90
|
+
"""
|
91
|
+
Ensure that the given implementation is callable or instantiable.
|
92
|
+
|
93
|
+
Parameters
|
94
|
+
----------
|
95
|
+
concrete : Callable[..., Any]
|
96
|
+
The implementation to check.
|
97
|
+
|
98
|
+
Raises
|
99
|
+
------
|
100
|
+
OrionisContainerTypeError
|
101
|
+
If the implementation is not callable.
|
102
|
+
"""
|
103
|
+
if not callable(concrete):
|
104
|
+
raise OrionisContainerTypeError(
|
105
|
+
f"The implementation '{str(concrete)}' must be callable or an instantiable class."
|
106
|
+
)
|
107
|
+
|
108
|
+
def _ensureIsInstance(self, instance: Any) -> None:
|
109
|
+
"""
|
110
|
+
Ensure that the given instance is a valid object.
|
111
|
+
|
112
|
+
Parameters
|
113
|
+
----------
|
114
|
+
instance : Any
|
115
|
+
The instance to check.
|
116
|
+
|
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
|
+
)
|
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.
|
134
|
+
Args:
|
135
|
+
concrete (Callable[..., Any]): The callable to be bound to the container.
|
136
|
+
Returns:
|
137
|
+
str: The unique key generated for the callable.
|
138
|
+
"""
|
139
|
+
self._ensureNotMain(concrete)
|
140
|
+
self._ensureUniqueService(concrete)
|
141
|
+
self._ensureIsCallable(concrete)
|
142
|
+
|
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
|
+
}
|
150
|
+
|
151
|
+
return key
|
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.
|
157
|
+
Args:
|
158
|
+
concrete (Callable[..., Any]): The callable that defines the service.
|
159
|
+
Returns:
|
160
|
+
str: The unique key generated for the callable.
|
161
|
+
"""
|
162
|
+
self._ensureNotMain(concrete)
|
163
|
+
self._ensureUniqueService(concrete)
|
164
|
+
self._ensureIsCallable(concrete)
|
165
|
+
|
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
|
+
}
|
173
|
+
|
174
|
+
return key
|
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.
|
182
|
+
Args:
|
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.
|
186
|
+
"""
|
187
|
+
self._ensureNotMain(concrete)
|
188
|
+
self._ensureUniqueService(concrete)
|
189
|
+
self._ensureIsCallable(concrete)
|
190
|
+
|
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
|
+
}
|
198
|
+
|
199
|
+
return key
|
200
|
+
|
201
|
+
def scoped(self, concrete: Callable[..., Any]) -> str:
|
202
|
+
"""
|
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.
|
207
|
+
Args:
|
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.
|
211
|
+
"""
|
212
|
+
self._ensureNotMain(concrete)
|
213
|
+
self._ensureUniqueService(concrete)
|
214
|
+
self._ensureIsCallable(concrete)
|
215
|
+
|
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
|
+
}
|
223
|
+
|
224
|
+
return key
|
225
|
+
|
226
|
+
def instance(self, instance: Any) -> str:
|
227
|
+
"""
|
228
|
+
Registers an instance as a singleton in the container.
|
229
|
+
Args:
|
230
|
+
instance (Any): The instance to be registered as a singleton.
|
231
|
+
Returns:
|
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.
|
255
|
+
Raises:
|
256
|
+
OrionisContainerException: If the concrete instance is not a valid object or if the alias is a primitive type.
|
257
|
+
"""
|
258
|
+
if not callable(concrete) and not isinstance(concrete, object):
|
259
|
+
raise OrionisContainerException(f"The instance '{str(concrete)}' must be a valid object.")
|
260
|
+
|
261
|
+
if self._instance._validate_types.isPrimitive(alias):
|
262
|
+
raise OrionisContainerException(f"Cannot use primitive type '{alias}' as an alias.")
|
263
|
+
|
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__}"
|
269
|
+
|
270
|
+
self._aliases[alias] = current_key
|
271
|
+
|
272
|
+
def has(self, obj: Any) -> bool:
|
273
|
+
"""
|
274
|
+
Checks if a service is registered in the container.
|
275
|
+
|
276
|
+
Parameters
|
277
|
+
----------
|
278
|
+
obj : Any
|
279
|
+
The service class, instance, or alias to check.
|
280
|
+
|
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
|
+
)
|
290
|
+
|
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
|
294
|
+
|
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
|
+
)
|
300
|
+
|
301
|
+
return False
|
302
|
+
|
303
|
+
def make(self, abstract: Any) -> Any:
|
304
|
+
"""
|
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)
|
323
|
+
|
324
|
+
if key in self._instances:
|
325
|
+
return self._instances[key]['instance']
|
326
|
+
|
327
|
+
if key in self._singletons:
|
328
|
+
self._instances[key] = {'instance': self._resolve(self._singletons[key]['callback'])}
|
329
|
+
return self._instances[key]['instance']
|
330
|
+
|
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]
|
335
|
+
|
336
|
+
if key in self._transients:
|
337
|
+
return self._resolve(self._transients[key]['callback'])
|
338
|
+
|
339
|
+
if key in self._bindings:
|
340
|
+
return self._resolve(self._bindings[key]['callback'])
|
341
|
+
|
342
|
+
raise OrionisContainerException(f"Service '{abstract}' is not registered in the container.")
|
343
|
+
|
344
|
+
def _resolve(self, concrete: Callable[..., Any]) -> Any:
|
345
|
+
"""
|
346
|
+
Resolve and instantiate a given service class or function.
|
347
|
+
|
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
|
+
"""
|
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:
|
376
|
+
param_type = param.annotation
|
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__}")
|
382
|
+
else:
|
383
|
+
resolved_dependencies[param_name] = self._resolve_dependency(param_type)
|
384
|
+
else:
|
385
|
+
resolved_dependencies[param_name] = param_type # It's a primitive, use as-is
|
386
|
+
|
387
|
+
# Resolve parameters with default values (without annotations)
|
388
|
+
elif param.default is not param.empty:
|
389
|
+
resolved_dependencies[param_name] = param.default
|
390
|
+
|
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)
|
396
|
+
|
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)}")
|
402
|
+
|
403
|
+
def _resolve_dependency(self, dep_type: Any) -> Any:
|
404
|
+
"""
|
405
|
+
Resolves a dependency based on the provided type.
|
406
|
+
|
407
|
+
This method looks for the type in the container and returns the instance,
|
408
|
+
respecting the lifecycle of the service (transient, singleton, etc.).
|
409
|
+
"""
|
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]}"
|
@@ -0,0 +1,222 @@
|
|
1
|
+
from abc import ABC, abstractmethod
|
2
|
+
from typing import Any, Callable
|
3
|
+
|
4
|
+
class IContainer(ABC):
|
5
|
+
|
6
|
+
@abstractmethod
|
7
|
+
def _newRequest(self) -> None:
|
8
|
+
"""
|
9
|
+
Reset scoped instances at the beginning of a new request.
|
10
|
+
"""
|
11
|
+
pass
|
12
|
+
|
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.
|
32
|
+
"""
|
33
|
+
pass
|
34
|
+
|
35
|
+
@abstractmethod
|
36
|
+
def _ensureUniqueService(self, obj: Any) -> None:
|
37
|
+
"""
|
38
|
+
Ensure that a service is not already registered.
|
39
|
+
|
40
|
+
Parameters
|
41
|
+
----------
|
42
|
+
obj : Any
|
43
|
+
The service to check.
|
44
|
+
|
45
|
+
Raises
|
46
|
+
------
|
47
|
+
OrionisContainerValueError
|
48
|
+
If the service is already registered.
|
49
|
+
"""
|
50
|
+
pass
|
51
|
+
|
52
|
+
@abstractmethod
|
53
|
+
def _ensureIsCallable(self, concrete: Callable[..., Any]) -> None:
|
54
|
+
"""
|
55
|
+
Ensure that the given implementation is callable or instantiable.
|
56
|
+
|
57
|
+
Parameters
|
58
|
+
----------
|
59
|
+
concrete : Callable[..., Any]
|
60
|
+
The implementation to check.
|
61
|
+
|
62
|
+
Raises
|
63
|
+
------
|
64
|
+
OrionisContainerTypeError
|
65
|
+
If the implementation is not callable.
|
66
|
+
"""
|
67
|
+
pass
|
68
|
+
|
69
|
+
@abstractmethod
|
70
|
+
def _ensureIsInstance(self, instance: Any) -> None:
|
71
|
+
"""
|
72
|
+
Ensure that the given instance is a valid object.
|
73
|
+
|
74
|
+
Parameters
|
75
|
+
----------
|
76
|
+
instance : Any
|
77
|
+
The instance to check.
|
78
|
+
|
79
|
+
Raises
|
80
|
+
------
|
81
|
+
OrionisContainerValueError
|
82
|
+
If the instance is not a valid object.
|
83
|
+
"""
|
84
|
+
pass
|
85
|
+
|
86
|
+
@abstractmethod
|
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.
|
94
|
+
Args:
|
95
|
+
concrete (Callable[..., Any]): The callable to be bound to the container.
|
96
|
+
Returns:
|
97
|
+
str: The unique key generated for the callable.
|
98
|
+
"""
|
99
|
+
pass
|
100
|
+
|
101
|
+
@abstractmethod
|
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.
|
106
|
+
Args:
|
107
|
+
concrete (Callable[..., Any]): The callable that defines the service.
|
108
|
+
Returns:
|
109
|
+
str: The unique key generated for the callable.
|
110
|
+
"""
|
111
|
+
pass
|
112
|
+
|
113
|
+
@abstractmethod
|
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.
|
120
|
+
Args:
|
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.
|
124
|
+
"""
|
125
|
+
pass
|
126
|
+
|
127
|
+
@abstractmethod
|
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.
|
134
|
+
Args:
|
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
|
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.
|
147
|
+
Returns:
|
148
|
+
str: The key under which the instance is registered in the container.
|
149
|
+
"""
|
150
|
+
pass
|
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.
|
159
|
+
Raises:
|
160
|
+
OrionisContainerException: If the concrete instance is not a valid object or if the alias is a primitive type.
|
161
|
+
"""
|
162
|
+
pass
|
163
|
+
|
164
|
+
@abstractmethod
|
165
|
+
def has(self, obj: Any) -> bool:
|
166
|
+
"""
|
167
|
+
Checks if a service is registered in the container.
|
168
|
+
|
169
|
+
Parameters
|
170
|
+
----------
|
171
|
+
obj : Any
|
172
|
+
The service class, instance, or alias to check.
|
173
|
+
|
174
|
+
Returns
|
175
|
+
-------
|
176
|
+
bool
|
177
|
+
True if the service is registered, False otherwise.
|
178
|
+
"""
|
179
|
+
pass
|
180
|
+
|
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.
|
200
|
+
"""
|
201
|
+
pass
|
202
|
+
|
203
|
+
@abstractmethod
|
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.
|
218
|
+
|
219
|
+
This method looks for the type in the container and returns the instance,
|
220
|
+
respecting the lifecycle of the service (transient, singleton, etc.).
|
221
|
+
"""
|
222
|
+
pass
|