orionis 0.539.0__py3-none-any.whl → 0.541.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/console/base/command.py +1 -1
- orionis/console/base/scheduler.py +1 -1
- orionis/console/contracts/base_command.py +123 -0
- orionis/console/contracts/cli_request.py +85 -0
- orionis/console/contracts/command.py +90 -108
- orionis/console/contracts/event.py +1 -1
- orionis/console/contracts/reactor.py +40 -0
- orionis/console/core/reactor.py +325 -91
- orionis/console/dumper/dump.py +1 -1
- orionis/console/dynamic/progress_bar.py +1 -1
- orionis/console/{enums → entities}/command.py +3 -0
- orionis/console/{enums → entities}/event.py +1 -1
- orionis/console/fluent/command.py +216 -0
- orionis/console/{tasks → fluent}/event.py +1 -1
- orionis/console/kernel.py +28 -11
- orionis/console/output/console.py +1 -1
- orionis/console/output/executor.py +1 -1
- orionis/console/request/cli_request.py +113 -31
- orionis/console/tasks/schedule.py +4 -4
- orionis/container/container.py +102 -1
- orionis/container/contracts/container.py +46 -0
- orionis/failure/base/handler.py +6 -6
- orionis/failure/catch.py +1 -1
- orionis/failure/contracts/handler.py +3 -3
- orionis/foundation/application.py +1 -1
- orionis/foundation/providers/console_provider.py +1 -1
- orionis/foundation/providers/dumper_provider.py +1 -1
- orionis/foundation/providers/executor_provider.py +1 -1
- orionis/foundation/providers/progress_bar_provider.py +1 -1
- orionis/metadata/framework.py +1 -1
- orionis/support/facades/application.pyi +5 -0
- orionis/support/facades/console.pyi +4 -0
- orionis/support/facades/directory.py +0 -1
- orionis/support/facades/directory.pyi +4 -0
- orionis/support/facades/dumper.pyi +4 -0
- orionis/support/facades/executor.pyi +4 -0
- orionis/support/facades/inspire.pyi +4 -0
- orionis/support/facades/logger.pyi +4 -0
- orionis/support/facades/performance_counter.pyi +4 -0
- orionis/support/facades/progress_bar.pyi +4 -0
- orionis/support/facades/reactor.pyi +97 -0
- orionis/support/facades/testing.pyi +4 -0
- orionis/support/facades/workers.pyi +4 -0
- orionis/test/kernel.py +1 -1
- {orionis-0.539.0.dist-info → orionis-0.541.0.dist-info}/METADATA +1 -1
- {orionis-0.539.0.dist-info → orionis-0.541.0.dist-info}/RECORD +57 -44
- orionis/console/entities/request.py +0 -37
- orionis/console/output/contracts/__init__.py +0 -0
- /orionis/console/contracts/{scheduler.py → base_scheduler.py} +0 -0
- /orionis/console/{output/contracts → contracts}/console.py +0 -0
- /orionis/console/{dumper/contracts → contracts}/dump.py +0 -0
- /orionis/console/{output/contracts → contracts}/executor.py +0 -0
- /orionis/console/{dynamic/contracts → contracts}/progress_bar.py +0 -0
- /orionis/console/{dumper/contracts → fluent}/__init__.py +0 -0
- /orionis/console/{dynamic/contracts → request}/__init__.py +0 -0
- {orionis-0.539.0.dist-info → orionis-0.541.0.dist-info}/WHEEL +0 -0
- {orionis-0.539.0.dist-info → orionis-0.541.0.dist-info}/licenses/LICENCE +0 -0
- {orionis-0.539.0.dist-info → orionis-0.541.0.dist-info}/top_level.txt +0 -0
- {orionis-0.539.0.dist-info → orionis-0.541.0.dist-info}/zip-safe +0 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from typing import Any, Callable
|
|
2
|
+
from orionis.console.args.argument import CLIArgument
|
|
3
|
+
from orionis.console.entities.command import Command as CommandEntity
|
|
4
|
+
from orionis.services.introspection.concretes.reflection import ReflectionConcrete
|
|
5
|
+
|
|
6
|
+
class Command:
|
|
7
|
+
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
signature: str,
|
|
11
|
+
concrete: Callable[..., Any],
|
|
12
|
+
method: str = 'handle'
|
|
13
|
+
) -> None:
|
|
14
|
+
"""
|
|
15
|
+
Initialize a new Command instance.
|
|
16
|
+
|
|
17
|
+
This constructor creates a Command object that encapsulates a command signature,
|
|
18
|
+
the concrete class that implements the command logic, and the method to be called
|
|
19
|
+
when executing the command. It validates that the provided concrete is a valid
|
|
20
|
+
class with the specified callable method.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
signature : str
|
|
25
|
+
The command signature string that defines how the command should be invoked
|
|
26
|
+
from the command line interface.
|
|
27
|
+
concrete : Callable[..., Any]
|
|
28
|
+
The concrete class that contains the implementation logic for the command.
|
|
29
|
+
Must be a valid class (not an instance) that will be instantiated when
|
|
30
|
+
the command is executed.
|
|
31
|
+
method : str, default='handle'
|
|
32
|
+
The name of the method within the concrete class that will be called
|
|
33
|
+
when executing the command. The method must exist and be callable.
|
|
34
|
+
|
|
35
|
+
Returns
|
|
36
|
+
-------
|
|
37
|
+
None
|
|
38
|
+
This is a constructor method and does not return a value.
|
|
39
|
+
|
|
40
|
+
Raises
|
|
41
|
+
------
|
|
42
|
+
TypeError
|
|
43
|
+
If the provided concrete is not a class, or if the method parameter
|
|
44
|
+
is not a string value.
|
|
45
|
+
AttributeError
|
|
46
|
+
If the specified method does not exist in the concrete class or
|
|
47
|
+
is not callable.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
# Validate that the concrete parameter is actually a class
|
|
51
|
+
if not ReflectionConcrete.isConcreteClass(concrete):
|
|
52
|
+
raise TypeError("The provided concrete must be a class.")
|
|
53
|
+
|
|
54
|
+
# Validate that the method parameter is a string
|
|
55
|
+
if not isinstance(method, str):
|
|
56
|
+
raise TypeError("The method name must be a string.")
|
|
57
|
+
|
|
58
|
+
# Validate that the specified method exists in the concrete class and is callable
|
|
59
|
+
if not hasattr(concrete, method) or not callable(getattr(concrete, method)):
|
|
60
|
+
raise AttributeError(f"The method '{method}' does not exist or is not callable in the provided concrete class.")
|
|
61
|
+
|
|
62
|
+
# Store the command signature for later use during command parsing
|
|
63
|
+
self.__signature = signature
|
|
64
|
+
|
|
65
|
+
# Store the concrete class reference for instantiation during execution
|
|
66
|
+
self.__concrete = concrete
|
|
67
|
+
|
|
68
|
+
# Store the method name to be called on the concrete instance
|
|
69
|
+
self.__method = method
|
|
70
|
+
|
|
71
|
+
# Initialize timestamp display as enabled by default
|
|
72
|
+
self.__timestamp = True
|
|
73
|
+
|
|
74
|
+
# Set default description for commands that don't provide one
|
|
75
|
+
self.__description = "No description provided."
|
|
76
|
+
|
|
77
|
+
# Initialize empty arguments list to be populated later
|
|
78
|
+
self.__arguments = []
|
|
79
|
+
|
|
80
|
+
def timestamp(self, enabled: bool = True) -> 'Command':
|
|
81
|
+
"""
|
|
82
|
+
Configure whether timestamps should be included in command output.
|
|
83
|
+
|
|
84
|
+
This method allows enabling or disabling timestamp display for the command.
|
|
85
|
+
When enabled, timestamps will be shown alongside command execution results.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
enabled : bool, default=True
|
|
90
|
+
Flag to enable or disable timestamp display. True enables timestamps,
|
|
91
|
+
False disables them.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
Command
|
|
96
|
+
Returns the current Command instance to allow method chaining.
|
|
97
|
+
|
|
98
|
+
Raises
|
|
99
|
+
------
|
|
100
|
+
TypeError
|
|
101
|
+
If the enabled parameter is not a boolean value.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
# Validate that the enabled parameter is a boolean
|
|
105
|
+
if not isinstance(enabled, bool):
|
|
106
|
+
raise TypeError("The timestamp flag must be a boolean value.")
|
|
107
|
+
|
|
108
|
+
# Set the internal timestamp flag
|
|
109
|
+
self.__timestamp = enabled
|
|
110
|
+
|
|
111
|
+
# Return self to enable method chaining
|
|
112
|
+
return self
|
|
113
|
+
|
|
114
|
+
def description(self, desc: str) -> 'Command':
|
|
115
|
+
"""
|
|
116
|
+
Set the description for the command.
|
|
117
|
+
|
|
118
|
+
This method allows setting a descriptive text that explains what the command
|
|
119
|
+
does. The description is used for help text and documentation purposes when
|
|
120
|
+
displaying command information to users.
|
|
121
|
+
|
|
122
|
+
Parameters
|
|
123
|
+
----------
|
|
124
|
+
desc : str
|
|
125
|
+
The description text for the command. Must be a non-empty string that
|
|
126
|
+
describes the command's purpose and functionality.
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
-------
|
|
130
|
+
Command
|
|
131
|
+
Returns the current Command instance to allow method chaining.
|
|
132
|
+
|
|
133
|
+
Raises
|
|
134
|
+
------
|
|
135
|
+
TypeError
|
|
136
|
+
If the desc parameter is not a string value.
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
# Validate that the description parameter is a string
|
|
140
|
+
if not isinstance(desc, str):
|
|
141
|
+
raise TypeError("The description must be a string.")
|
|
142
|
+
|
|
143
|
+
# Set the internal description attribute
|
|
144
|
+
self.__description = desc
|
|
145
|
+
|
|
146
|
+
# Return self to enable method chaining
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
def arguments(self, args: list) -> 'Command':
|
|
150
|
+
"""
|
|
151
|
+
Set the list of CLI arguments for the command.
|
|
152
|
+
|
|
153
|
+
This method configures the command-line arguments that the command will accept.
|
|
154
|
+
Each argument must be a properly configured CLIArgument instance that defines
|
|
155
|
+
the argument's name, type, validation rules, and other properties. The arguments
|
|
156
|
+
are used during command parsing to validate and process user input.
|
|
157
|
+
|
|
158
|
+
Parameters
|
|
159
|
+
----------
|
|
160
|
+
args : list
|
|
161
|
+
A list of CLIArgument instances that define the command's accepted arguments.
|
|
162
|
+
Each element in the list must be a valid CLIArgument object with proper
|
|
163
|
+
configuration for argument parsing and validation.
|
|
164
|
+
|
|
165
|
+
Returns
|
|
166
|
+
-------
|
|
167
|
+
Command
|
|
168
|
+
Returns the current Command instance to allow method chaining and enable
|
|
169
|
+
fluent interface pattern for command configuration.
|
|
170
|
+
|
|
171
|
+
Raises
|
|
172
|
+
------
|
|
173
|
+
TypeError
|
|
174
|
+
If the args parameter is not a list, or if any element in the list
|
|
175
|
+
is not an instance of CLIArgument.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
# Validate that the arguments parameter is a list
|
|
179
|
+
if not isinstance(args, list):
|
|
180
|
+
raise TypeError("Arguments must be provided as a list.")
|
|
181
|
+
|
|
182
|
+
# Validate that each argument in the list is a CLIArgument instance
|
|
183
|
+
for arg in args:
|
|
184
|
+
if not isinstance(arg, CLIArgument):
|
|
185
|
+
raise TypeError("All arguments must be instances of CLIArgument.")
|
|
186
|
+
|
|
187
|
+
# Set the internal arguments list with the validated arguments
|
|
188
|
+
self.__arguments = args
|
|
189
|
+
|
|
190
|
+
# Return self to enable method chaining
|
|
191
|
+
return self
|
|
192
|
+
|
|
193
|
+
def get(self) -> tuple[str, CommandEntity]:
|
|
194
|
+
"""
|
|
195
|
+
Retrieve the configured Command entity.
|
|
196
|
+
|
|
197
|
+
This method constructs and returns a Command entity object that encapsulates
|
|
198
|
+
all the configuration details of the command, including its signature, concrete
|
|
199
|
+
class, method, description, arguments, and timestamp setting. The returned
|
|
200
|
+
Command entity can be used for command execution and management.
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
CommandEntity
|
|
205
|
+
A Command entity object containing all the command's configuration details.
|
|
206
|
+
"""
|
|
207
|
+
|
|
208
|
+
# Create and return a Command entity with all the configured properties
|
|
209
|
+
return self.__signature, CommandEntity(
|
|
210
|
+
obj=self.__concrete,
|
|
211
|
+
method=self.__method,
|
|
212
|
+
timestamps=self.__timestamp,
|
|
213
|
+
signature=self.__signature,
|
|
214
|
+
description=self.__description,
|
|
215
|
+
args=self.__arguments
|
|
216
|
+
)
|
|
@@ -6,7 +6,7 @@ from apscheduler.triggers.date import DateTrigger
|
|
|
6
6
|
from apscheduler.triggers.interval import IntervalTrigger
|
|
7
7
|
from orionis.console.contracts.event import IEvent
|
|
8
8
|
from orionis.console.contracts.schedule_event_listener import IScheduleEventListener
|
|
9
|
-
from orionis.console.
|
|
9
|
+
from orionis.console.entities.event import Event as EventEntity
|
|
10
10
|
from orionis.console.exceptions import CLIOrionisValueError
|
|
11
11
|
|
|
12
12
|
class Event(IEvent):
|
orionis/console/kernel.py
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
from typing import List
|
|
2
2
|
from orionis.console.contracts.kernel import IKernelCLI
|
|
3
3
|
from orionis.console.contracts.reactor import IReactor
|
|
4
|
-
from orionis.console.
|
|
5
|
-
from orionis.console.request.cli_request import Request
|
|
4
|
+
from orionis.console.request.cli_request import CLIRequest
|
|
6
5
|
from orionis.failure.contracts.catch import ICatch
|
|
7
6
|
from orionis.foundation.contracts.application import IApplication
|
|
8
7
|
from orionis.console.exceptions import CLIOrionisValueError
|
|
@@ -38,6 +37,9 @@ class KernelCLI(IKernelCLI):
|
|
|
38
37
|
f"Failed to initialize TestKernel: expected IApplication, got {type(app).__module__}.{type(app).__name__}."
|
|
39
38
|
)
|
|
40
39
|
|
|
40
|
+
# Store the application container as a private attribute for internal use
|
|
41
|
+
self.__app: IApplication = app
|
|
42
|
+
|
|
41
43
|
# Retrieve and initialize the reactor instance from the application container.
|
|
42
44
|
# The reactor is responsible for dispatching CLI commands.
|
|
43
45
|
self.__reactor: IReactor = app.make('x-orionis.console.core.reactor')
|
|
@@ -64,21 +66,36 @@ class KernelCLI(IKernelCLI):
|
|
|
64
66
|
SystemExit
|
|
65
67
|
If invalid arguments are provided or no command is specified, this method may terminate the process with an error message.
|
|
66
68
|
"""
|
|
69
|
+
try:
|
|
67
70
|
|
|
68
|
-
|
|
71
|
+
# Ensure the arguments are provided as a list
|
|
72
|
+
if not isinstance(args, list):
|
|
73
|
+
raise CLIOrionisValueError(
|
|
74
|
+
f"Failed to handle command line arguments: expected list, got {type(args).__module__}.{type(args).__name__}."
|
|
75
|
+
)
|
|
69
76
|
|
|
70
|
-
|
|
77
|
+
# If no arguments or only the script name is provided, show the default help command
|
|
78
|
+
if not args or len(args) <= 1:
|
|
79
|
+
return self.__reactor.call('help')
|
|
80
|
+
|
|
81
|
+
# Remove the first argument (script name) to process only the command and its parameters
|
|
82
|
+
args = args[1:]
|
|
71
83
|
|
|
72
|
-
#
|
|
73
|
-
|
|
84
|
+
# If no command is provided after removing the script name, exit with an error
|
|
85
|
+
if len(args) == 0:
|
|
86
|
+
raise CLIOrionisValueError("No command provided to execute.")
|
|
87
|
+
|
|
88
|
+
# If only the command is provided, call it without additional arguments
|
|
89
|
+
if len(args) == 1:
|
|
90
|
+
return self.__reactor.call(args[0])
|
|
74
91
|
|
|
75
92
|
# If command and arguments are provided, call the command with its arguments
|
|
76
|
-
return self.__reactor.call(
|
|
77
|
-
request.command,
|
|
78
|
-
request.args
|
|
79
|
-
)
|
|
93
|
+
return self.__reactor.call(args[0], args[1:])
|
|
80
94
|
|
|
81
95
|
except BaseException as e:
|
|
82
96
|
|
|
97
|
+
# If an exception occurs, prepare a CLIRequest with the command (if available)
|
|
98
|
+
command = args[0] if args else "__unknown__"
|
|
99
|
+
|
|
83
100
|
# Catch any exceptions that occur during command handling
|
|
84
|
-
self.__catch.exception(self,
|
|
101
|
+
self.__catch.exception(self, CLIRequest(command, {}), e)
|
|
@@ -1,45 +1,127 @@
|
|
|
1
|
-
from typing import
|
|
2
|
-
from orionis.console.
|
|
1
|
+
from typing import Any
|
|
2
|
+
from orionis.console.contracts.cli_request import ICLIRequest
|
|
3
3
|
from orionis.console.exceptions.cli_orionis_value_error import CLIOrionisValueError
|
|
4
4
|
|
|
5
|
-
class CLIRequest:
|
|
6
|
-
|
|
5
|
+
class CLIRequest(ICLIRequest):
|
|
6
|
+
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
command: str,
|
|
10
|
+
args: dict
|
|
11
|
+
):
|
|
7
12
|
"""
|
|
8
|
-
|
|
13
|
+
Initialize a CLI request object with command line arguments.
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
Args:
|
|
16
|
+
args (dict, optional): Dictionary containing command line arguments and their values.
|
|
17
|
+
Defaults to an empty dictionary.
|
|
18
|
+
|
|
19
|
+
Raises:
|
|
20
|
+
CLIOrionisValueError: If the provided args parameter is not a dictionary.
|
|
21
|
+
|
|
22
|
+
Note:
|
|
23
|
+
The args dictionary is stored privately and used to manage CLI request parameters
|
|
24
|
+
throughout the lifecycle of the CLI request object.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
# Validate that args is a dictionary
|
|
28
|
+
if not isinstance(args, dict):
|
|
29
|
+
raise CLIOrionisValueError("Args must be a dictionary")
|
|
30
|
+
|
|
31
|
+
# Validate that command is a string
|
|
32
|
+
if not isinstance(command, str):
|
|
33
|
+
raise CLIOrionisValueError("Command must be a string")
|
|
34
|
+
|
|
35
|
+
# Store the args dictionary as a private attribute
|
|
36
|
+
self.__command = command
|
|
37
|
+
self.__args = args if args is not None else {}
|
|
38
|
+
|
|
39
|
+
def command(self) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Retrieve the command name associated with this CLI request.
|
|
42
|
+
|
|
43
|
+
This method provides access to the command string that was specified during
|
|
44
|
+
the initialization of the CLIRequest object. The command represents the
|
|
45
|
+
primary action or operation that should be executed based on the CLI input.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
str
|
|
50
|
+
The command name stored as a string. This is the exact command value
|
|
51
|
+
that was passed to the constructor during object initialization.
|
|
52
|
+
|
|
53
|
+
Notes
|
|
54
|
+
-----
|
|
55
|
+
The returned command string is immutable and represents the core action
|
|
56
|
+
identifier for this CLI request. This value is essential for determining
|
|
57
|
+
which operation should be performed by the CLI handler.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
# Return the command name stored in the private attribute
|
|
61
|
+
# This provides access to the command specified during initialization
|
|
62
|
+
return self.__command
|
|
63
|
+
|
|
64
|
+
def all(self) -> dict:
|
|
65
|
+
"""
|
|
66
|
+
Retrieve all command line arguments as a complete dictionary.
|
|
67
|
+
|
|
68
|
+
This method provides access to the entire collection of command line arguments
|
|
69
|
+
that were passed during the initialization of the CLIRequest object. It returns
|
|
70
|
+
a reference to the internal arguments dictionary, allowing for comprehensive
|
|
71
|
+
access to all parsed CLI parameters.
|
|
14
72
|
|
|
15
73
|
Returns
|
|
16
74
|
-------
|
|
17
|
-
|
|
18
|
-
|
|
75
|
+
dict
|
|
76
|
+
A dictionary containing all the parsed command line arguments as key-value
|
|
77
|
+
pairs, where keys are argument names (str) and values are the corresponding
|
|
78
|
+
argument values of any type. If no arguments were provided during
|
|
79
|
+
initialization, returns an empty dictionary.
|
|
19
80
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
81
|
+
Notes
|
|
82
|
+
-----
|
|
83
|
+
This method returns a reference to the internal arguments dictionary rather
|
|
84
|
+
than a copy. Modifications to the returned dictionary will affect the
|
|
85
|
+
internal state of the CLIRequest object.
|
|
24
86
|
"""
|
|
25
|
-
if not isinstance(args, list):
|
|
26
|
-
raise CLIOrionisValueError(
|
|
27
|
-
f"Failed to handle command line arguments: expected list, got {type(args).__module__}.{type(args).__name__}."
|
|
28
|
-
)
|
|
29
87
|
|
|
30
|
-
|
|
31
|
-
|
|
88
|
+
# Return the complete arguments dictionary containing all CLI parameters
|
|
89
|
+
return self.__args
|
|
90
|
+
|
|
91
|
+
def argument(self, name: str, default: Any = None):
|
|
92
|
+
"""
|
|
93
|
+
Retrieve the value of a specific command line argument by its name.
|
|
32
94
|
|
|
33
|
-
|
|
34
|
-
|
|
95
|
+
This method provides access to individual command line arguments that were
|
|
96
|
+
passed during initialization. It safely retrieves argument values without
|
|
97
|
+
raising exceptions if the argument doesn't exist.
|
|
35
98
|
|
|
36
|
-
|
|
37
|
-
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
name : str
|
|
102
|
+
The name of the command line argument to retrieve. This should match
|
|
103
|
+
the key used when the argument was originally parsed and stored.
|
|
104
|
+
default : Any, optional
|
|
105
|
+
The default value to return if the specified argument name does not
|
|
106
|
+
exist in the arguments dictionary. Defaults to None.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
Any or None
|
|
111
|
+
The value associated with the specified argument name if it exists
|
|
112
|
+
in the arguments dictionary. Returns None if the argument name is
|
|
113
|
+
not found or was not provided during CLI execution.
|
|
114
|
+
|
|
115
|
+
Notes
|
|
116
|
+
-----
|
|
117
|
+
This method uses the dictionary's get() method to safely access values,
|
|
118
|
+
ensuring that missing arguments return None rather than raising a KeyError.
|
|
119
|
+
"""
|
|
38
120
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
121
|
+
# Safely retrieve the argument value using dict.get() to avoid KeyError
|
|
122
|
+
# Returns None if the argument name doesn't exist in the dictionary
|
|
123
|
+
if name not in self.__args or self.__args[name] is None:
|
|
124
|
+
return default
|
|
43
125
|
|
|
44
|
-
#
|
|
45
|
-
|
|
126
|
+
# Return the value associated with the specified argument name
|
|
127
|
+
return self.__args.get(name, default)
|
|
@@ -25,16 +25,16 @@ from orionis.console.contracts.reactor import IReactor
|
|
|
25
25
|
from orionis.console.contracts.schedule import ISchedule
|
|
26
26
|
from orionis.console.contracts.schedule_event_listener import IScheduleEventListener
|
|
27
27
|
from orionis.console.entities.event_job import EventJob
|
|
28
|
-
from orionis.console.entities.request import CLIRequest
|
|
29
28
|
from orionis.console.entities.scheduler_error import SchedulerError
|
|
30
29
|
from orionis.console.entities.scheduler_paused import SchedulerPaused
|
|
31
30
|
from orionis.console.entities.scheduler_resumed import SchedulerResumed
|
|
32
31
|
from orionis.console.entities.scheduler_shutdown import SchedulerShutdown
|
|
33
32
|
from orionis.console.entities.scheduler_started import SchedulerStarted
|
|
34
|
-
from orionis.console.
|
|
33
|
+
from orionis.console.entities.event import Event as EventEntity
|
|
35
34
|
from orionis.console.enums.listener import ListeningEvent
|
|
36
35
|
from orionis.console.exceptions import CLIOrionisRuntimeError
|
|
37
36
|
from orionis.console.exceptions.cli_orionis_value_error import CLIOrionisValueError
|
|
37
|
+
from orionis.console.request.cli_request import CLIRequest
|
|
38
38
|
from orionis.failure.contracts.catch import ICatch
|
|
39
39
|
from orionis.foundation.contracts.application import IApplication
|
|
40
40
|
from orionis.services.log.contracts.log_service import ILogger
|
|
@@ -282,7 +282,7 @@ class Schedule(ISchedule):
|
|
|
282
282
|
raise CLIOrionisValueError(f"The command '{signature}' is not available or does not exist.")
|
|
283
283
|
|
|
284
284
|
# Import Event here to avoid circular dependency issues
|
|
285
|
-
from orionis.console.
|
|
285
|
+
from orionis.console.fluent.event import Event
|
|
286
286
|
|
|
287
287
|
# Store the command and its arguments for scheduling
|
|
288
288
|
self.__events[signature] = Event(
|
|
@@ -981,7 +981,7 @@ class Schedule(ISchedule):
|
|
|
981
981
|
|
|
982
982
|
# Delegate exception handling to the application's error catching mechanism
|
|
983
983
|
# This ensures consistent error handling across the entire application
|
|
984
|
-
self.__catch.exception(self, CLIRequest(command="schedule:work", args=
|
|
984
|
+
self.__catch.exception(self, CLIRequest(command="schedule:work", args={}), exception)
|
|
985
985
|
|
|
986
986
|
def setListener(
|
|
987
987
|
self,
|
orionis/container/container.py
CHANGED
|
@@ -18,7 +18,6 @@ from orionis.services.introspection.concretes.reflection import ReflectionConcre
|
|
|
18
18
|
from orionis.services.introspection.dependencies.entities.argument import Argument
|
|
19
19
|
from orionis.services.introspection.dependencies.entities.resolve_argument import ResolveArguments
|
|
20
20
|
from orionis.services.introspection.dependencies.reflection import ReflectDependencies
|
|
21
|
-
from orionis.services.introspection.instances.reflection import ReflectionInstance
|
|
22
21
|
|
|
23
22
|
class Container(IContainer):
|
|
24
23
|
|
|
@@ -541,6 +540,101 @@ class Container(IContainer):
|
|
|
541
540
|
# Return True to indicate successful registration
|
|
542
541
|
return True
|
|
543
542
|
|
|
543
|
+
def scopedInstance(
|
|
544
|
+
self,
|
|
545
|
+
abstract: Callable[..., Any],
|
|
546
|
+
instance: Any,
|
|
547
|
+
*,
|
|
548
|
+
alias: str = None,
|
|
549
|
+
enforce_decoupling: bool = False
|
|
550
|
+
) -> Optional[bool]:
|
|
551
|
+
"""
|
|
552
|
+
Registers an instance of a class or interface in the container with scoped lifetime.
|
|
553
|
+
|
|
554
|
+
Parameters
|
|
555
|
+
----------
|
|
556
|
+
abstract : Callable[..., Any]
|
|
557
|
+
The abstract class or interface to associate with the instance.
|
|
558
|
+
instance : Any
|
|
559
|
+
The concrete instance to register.
|
|
560
|
+
alias : str, optional
|
|
561
|
+
An optional alias to register the instance under. If not provided,
|
|
562
|
+
the abstract's `__name__` attribute will be used as the alias if available.
|
|
563
|
+
enforce_decoupling : bool, optional
|
|
564
|
+
Whether to enforce decoupling between abstract and concrete types.
|
|
565
|
+
|
|
566
|
+
Returns
|
|
567
|
+
-------
|
|
568
|
+
bool
|
|
569
|
+
True if the instance was successfully registered.
|
|
570
|
+
|
|
571
|
+
Raises
|
|
572
|
+
------
|
|
573
|
+
TypeError
|
|
574
|
+
If `abstract` is not an abstract class or if `alias` is not a valid string.
|
|
575
|
+
ValueError
|
|
576
|
+
If `instance` is not a valid instance of `abstract`.
|
|
577
|
+
OrionisContainerException
|
|
578
|
+
If no active scope is found.
|
|
579
|
+
|
|
580
|
+
Notes
|
|
581
|
+
-----
|
|
582
|
+
This method registers the instance with scoped lifetime, meaning it will be
|
|
583
|
+
available only within the current active scope. If no scope is active,
|
|
584
|
+
an exception will be raised.
|
|
585
|
+
"""
|
|
586
|
+
|
|
587
|
+
# Ensure that the abstract is an abstract class
|
|
588
|
+
IsAbstractClass(abstract, f"Instance {Lifetime.SCOPED}")
|
|
589
|
+
|
|
590
|
+
# Ensure that the instance is a valid instance
|
|
591
|
+
IsInstance(instance)
|
|
592
|
+
|
|
593
|
+
# Ensure that instance is NOT a subclass of abstract
|
|
594
|
+
if enforce_decoupling:
|
|
595
|
+
IsNotSubclass(abstract, instance.__class__)
|
|
596
|
+
else:
|
|
597
|
+
# Validate that instance is a subclass of abstract
|
|
598
|
+
IsSubclass(abstract, instance.__class__)
|
|
599
|
+
|
|
600
|
+
# Ensure implementation
|
|
601
|
+
ImplementsAbstractMethods(
|
|
602
|
+
abstract=abstract,
|
|
603
|
+
instance=instance
|
|
604
|
+
)
|
|
605
|
+
|
|
606
|
+
# Ensure that the alias is a valid string if provided
|
|
607
|
+
if alias:
|
|
608
|
+
IsValidAlias(alias)
|
|
609
|
+
else:
|
|
610
|
+
rf_asbtract = ReflectionAbstract(abstract)
|
|
611
|
+
alias = rf_asbtract.getModuleWithClassName()
|
|
612
|
+
|
|
613
|
+
# If the service is already registered in container bindings, drop it
|
|
614
|
+
self.drop(abstract, alias)
|
|
615
|
+
|
|
616
|
+
# Register the binding in the container for future scope resolutions
|
|
617
|
+
self.__bindings[abstract] = Binding(
|
|
618
|
+
contract=abstract,
|
|
619
|
+
instance=instance,
|
|
620
|
+
lifetime=Lifetime.SCOPED,
|
|
621
|
+
enforce_decoupling=enforce_decoupling,
|
|
622
|
+
alias=alias
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
# Register the alias
|
|
626
|
+
self.__aliases[alias] = self.__bindings[abstract]
|
|
627
|
+
|
|
628
|
+
# Store the instance directly in the current scope
|
|
629
|
+
scope = ScopedContext.getCurrentScope()
|
|
630
|
+
if scope:
|
|
631
|
+
scope[abstract] = instance
|
|
632
|
+
if alias != abstract:
|
|
633
|
+
scope[alias] = instance
|
|
634
|
+
|
|
635
|
+
# Return True to indicate successful registration
|
|
636
|
+
return True
|
|
637
|
+
|
|
544
638
|
def instance(
|
|
545
639
|
self,
|
|
546
640
|
abstract: Callable[..., Any],
|
|
@@ -1760,6 +1854,11 @@ class Container(IContainer):
|
|
|
1760
1854
|
If the dependency cannot be resolved through any available method.
|
|
1761
1855
|
"""
|
|
1762
1856
|
|
|
1857
|
+
# Check if the dependency is already in the current scope (for SCOPED lifetime)
|
|
1858
|
+
scoped = ScopedContext.getCurrentScope()
|
|
1859
|
+
if scoped and (dependency.type in scoped or dependency.full_class_path in scoped):
|
|
1860
|
+
return scoped[dependency.type] if dependency.type in scoped else scoped[dependency.full_class_path]
|
|
1861
|
+
|
|
1763
1862
|
# If the dependency has a default value, use it
|
|
1764
1863
|
if dependency.default is not None:
|
|
1765
1864
|
return dependency.default
|
|
@@ -1775,10 +1874,12 @@ class Container(IContainer):
|
|
|
1775
1874
|
)
|
|
1776
1875
|
|
|
1777
1876
|
# Try to resolve from container using type (Abstract or Interface)
|
|
1877
|
+
# This will automatically handle SCOPED lifetime through resolve() -> __resolveScoped()
|
|
1778
1878
|
if self.bound(dependency.type):
|
|
1779
1879
|
return self.resolve(self.getBinding(dependency.type))
|
|
1780
1880
|
|
|
1781
1881
|
# Try to resolve from container using full class path
|
|
1882
|
+
# This will also handle SCOPED lifetime appropriately
|
|
1782
1883
|
if self.bound(dependency.full_class_path):
|
|
1783
1884
|
return self.resolve(self.getBinding(dependency.full_class_path))
|
|
1784
1885
|
|