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
orionis/console/core/reactor.py
CHANGED
|
@@ -5,13 +5,15 @@ import re
|
|
|
5
5
|
from typing import Any, List, Optional
|
|
6
6
|
from orionis.console.args.argument import CLIArgument
|
|
7
7
|
from orionis.console.base.command import BaseCommand
|
|
8
|
-
from orionis.console.contracts.
|
|
8
|
+
from orionis.console.contracts.base_command import IBaseCommand
|
|
9
|
+
from orionis.console.contracts.cli_request import ICLIRequest
|
|
10
|
+
from orionis.console.contracts.command import ICommand
|
|
9
11
|
from orionis.console.contracts.reactor import IReactor
|
|
10
|
-
from orionis.console.
|
|
12
|
+
from orionis.console.entities.command import Command
|
|
11
13
|
from orionis.console.exceptions import CLIOrionisValueError
|
|
12
14
|
from orionis.console.exceptions.cli_runtime_error import CLIOrionisRuntimeError
|
|
13
|
-
from orionis.console.
|
|
14
|
-
from orionis.console.
|
|
15
|
+
from orionis.console.contracts.executor import IExecutor
|
|
16
|
+
from orionis.console.request.cli_request import CLIRequest
|
|
15
17
|
from orionis.foundation.contracts.application import IApplication
|
|
16
18
|
from orionis.services.introspection.modules.reflection import ReflectionModule
|
|
17
19
|
from orionis.services.log.contracts.log_service import ILogger
|
|
@@ -58,31 +60,181 @@ class Reactor(IReactor):
|
|
|
58
60
|
# Initialize the application instance, using provided app or creating new Orionis instance
|
|
59
61
|
self.__app = app
|
|
60
62
|
|
|
61
|
-
# Set the project root directory to current working directory for module path resolution
|
|
62
|
-
self.__root = self.__app.path('root')
|
|
63
|
-
|
|
64
63
|
# Initialize the internal command registry as an empty dictionary
|
|
65
64
|
self.__commands: dict[str, Command] = {}
|
|
66
65
|
|
|
67
|
-
# Automatically discover and load command classes from the console commands directory
|
|
68
|
-
cli_commands_path = (Path(self.__app.path('console')) / 'commands').resolve()
|
|
69
|
-
self.__loadCommands(cli_commands_path, self.__root)
|
|
70
|
-
|
|
71
|
-
# Load core command classes provided by the Orionis framework
|
|
72
|
-
self.__loadCoreCommands()
|
|
73
|
-
|
|
74
66
|
# Initialize the executor for command output management
|
|
75
67
|
self.__executer: IExecutor = self.__app.make('x-orionis.console.output.executor')
|
|
76
68
|
|
|
77
|
-
# Initialize the console for command output
|
|
78
|
-
self.__console: IConsole = self.__app.make('x-orionis.console.output.console')
|
|
79
|
-
|
|
80
69
|
# Initialize the logger service for logging command execution details
|
|
81
70
|
self.__logger: ILogger = self.__app.make('x-orionis.services.log.log_service')
|
|
82
71
|
|
|
83
72
|
# Initialize the performance counter for measuring command execution time
|
|
84
73
|
self.__performance_counter: IPerformanceCounter = self.__app.make('x-orionis.support.performance.counter')
|
|
85
74
|
|
|
75
|
+
# List to hold fluent command definitions
|
|
76
|
+
self.__fluent_commands: List[ICommand] = []
|
|
77
|
+
|
|
78
|
+
# Flag to indicate whether commands have been loaded
|
|
79
|
+
self.__load_commands: bool = False
|
|
80
|
+
|
|
81
|
+
def __loadCommands(self) -> None:
|
|
82
|
+
"""
|
|
83
|
+
Loads all available commands for the console application.
|
|
84
|
+
|
|
85
|
+
This method orchestrates the loading of both custom user-defined commands and
|
|
86
|
+
core framework commands into the reactor's internal command registry. It implements
|
|
87
|
+
a lazy loading pattern using an internal flag to ensure commands are loaded only
|
|
88
|
+
once during the reactor instance's lifetime, preventing duplicate registrations
|
|
89
|
+
and improving performance on subsequent calls.
|
|
90
|
+
|
|
91
|
+
The loading process follows a specific order: custom commands are loaded first,
|
|
92
|
+
followed by core framework commands. This ensures that custom commands can
|
|
93
|
+
potentially override core commands if they share the same signature, giving
|
|
94
|
+
users the flexibility to customize framework behavior.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
None
|
|
99
|
+
This method does not return any value. All discovered commands are
|
|
100
|
+
registered internally in the reactor's command registry and become
|
|
101
|
+
available for execution through the `call` and `callAsync` methods.
|
|
102
|
+
|
|
103
|
+
Notes
|
|
104
|
+
-----
|
|
105
|
+
- Commands are loaded only once per reactor instance to prevent duplicates
|
|
106
|
+
- Custom commands are loaded before core commands to allow potential overrides
|
|
107
|
+
- The internal `__load_commands` flag tracks whether commands have been loaded
|
|
108
|
+
- Both loading methods handle their own error handling and validation
|
|
109
|
+
"""
|
|
110
|
+
# Check if commands have already been loaded to prevent duplicate loading
|
|
111
|
+
if not self.__load_commands:
|
|
112
|
+
|
|
113
|
+
# Load custom user-defined commands from the project's commands directory
|
|
114
|
+
self.__loadCustomCommands()
|
|
115
|
+
|
|
116
|
+
# Load core framework commands that are bundled with Orionis
|
|
117
|
+
self.__loadCoreCommands()
|
|
118
|
+
|
|
119
|
+
# Load commands defined using the fluent interface
|
|
120
|
+
self.__loadFluentCommands()
|
|
121
|
+
|
|
122
|
+
# Set the flag to indicate that commands have been successfully loaded
|
|
123
|
+
self.__load_commands = True
|
|
124
|
+
|
|
125
|
+
def __loadFluentCommands(self) -> None:
|
|
126
|
+
"""
|
|
127
|
+
Loads and registers commands defined using the fluent interface.
|
|
128
|
+
|
|
129
|
+
This method iterates through all commands that have been defined using the
|
|
130
|
+
fluent interface pattern and registers them in the reactor's internal command
|
|
131
|
+
registry. Each fluent command is validated for structure and metadata before
|
|
132
|
+
being added to ensure proper integration and discoverability.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
None
|
|
137
|
+
This method does not return any value. All fluent commands are
|
|
138
|
+
registered internally in the reactor's command registry for later lookup
|
|
139
|
+
and execution.
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
# Import library for dynamic module importing
|
|
143
|
+
import importlib
|
|
144
|
+
|
|
145
|
+
# Get the routes directory path from the application instance
|
|
146
|
+
routes_path = self.__app.path('routes')
|
|
147
|
+
|
|
148
|
+
# Check if routes directory exists
|
|
149
|
+
if not os.path.exists(routes_path):
|
|
150
|
+
return
|
|
151
|
+
|
|
152
|
+
# Get the project root directory for module path resolution
|
|
153
|
+
root_path = self.__app.getBasePath()
|
|
154
|
+
|
|
155
|
+
# List all .py files in the routes directory and subdirectories
|
|
156
|
+
for current_directory, _, files in os.walk(routes_path):
|
|
157
|
+
for file in files:
|
|
158
|
+
|
|
159
|
+
# Only process Python files
|
|
160
|
+
if file.endswith('.py'):
|
|
161
|
+
file_path = os.path.join(current_directory, file)
|
|
162
|
+
|
|
163
|
+
# Read file content to check for 'Reactor.command'
|
|
164
|
+
try:
|
|
165
|
+
|
|
166
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
167
|
+
content = f.read()
|
|
168
|
+
|
|
169
|
+
# Check if the file contains 'Reactor.command'
|
|
170
|
+
if 'Reactor.command' in content:
|
|
171
|
+
|
|
172
|
+
# Sanitize the module path for import
|
|
173
|
+
pre_module = current_directory.replace(root_path, '')\
|
|
174
|
+
.replace(os.sep, '.')\
|
|
175
|
+
.lstrip('.')
|
|
176
|
+
|
|
177
|
+
# Remove virtual environment paths
|
|
178
|
+
pre_module = re.sub(r'[^.]*\.(?:Lib|lib)\.(?:python[^.]*\.)?site-packages\.?', '', pre_module)
|
|
179
|
+
pre_module = re.sub(r'\.?v?env\.?', '', pre_module)
|
|
180
|
+
pre_module = re.sub(r'\.+', '.', pre_module).strip('.')
|
|
181
|
+
|
|
182
|
+
# Skip if module name is empty after cleaning
|
|
183
|
+
if not pre_module:
|
|
184
|
+
continue
|
|
185
|
+
|
|
186
|
+
# Create the reflection module path
|
|
187
|
+
module_name = f"{pre_module}.{file[:-3]}"
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
|
|
191
|
+
# Import the module natively using importlib
|
|
192
|
+
importlib.import_module(module_name)
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
|
|
196
|
+
# Raise a runtime error if module import fails
|
|
197
|
+
raise CLIOrionisRuntimeError(
|
|
198
|
+
f"Failed to import module '{module_name}' from file '{file_path}': {e}"
|
|
199
|
+
) from e
|
|
200
|
+
|
|
201
|
+
except Exception as e:
|
|
202
|
+
|
|
203
|
+
# Raise a runtime error if file reading fails
|
|
204
|
+
raise CLIOrionisRuntimeError(
|
|
205
|
+
f"Failed to read file '{file_path}' for fluent command loading: {e}"
|
|
206
|
+
) from e
|
|
207
|
+
|
|
208
|
+
# Iterate through all fluent command definitions
|
|
209
|
+
for f_command in self.__fluent_commands:
|
|
210
|
+
|
|
211
|
+
signature, command_entity = f_command.get()
|
|
212
|
+
|
|
213
|
+
# Build the arguments dictionary from the CLIArgument instances
|
|
214
|
+
required_args: List[CLIArgument] = command_entity.args
|
|
215
|
+
|
|
216
|
+
# Create an ArgumentParser instance to handle the command arguments
|
|
217
|
+
arg_parser = argparse.ArgumentParser(
|
|
218
|
+
usage=f"python -B reactor {signature} [options]",
|
|
219
|
+
description=f"Command [{signature}] : {command_entity.description}",
|
|
220
|
+
formatter_class=argparse.RawTextHelpFormatter,
|
|
221
|
+
add_help=True,
|
|
222
|
+
allow_abbrev=False,
|
|
223
|
+
exit_on_error=True,
|
|
224
|
+
prog=signature
|
|
225
|
+
)
|
|
226
|
+
for arg in required_args:
|
|
227
|
+
arg.addToParser(arg_parser)
|
|
228
|
+
|
|
229
|
+
self.__commands[signature] = Command(
|
|
230
|
+
obj=command_entity.obj,
|
|
231
|
+
method=command_entity.method,
|
|
232
|
+
timestamps=command_entity.timestamps,
|
|
233
|
+
signature=signature,
|
|
234
|
+
description=command_entity.description,
|
|
235
|
+
args=arg_parser
|
|
236
|
+
)
|
|
237
|
+
|
|
86
238
|
def __loadCoreCommands(self) -> None:
|
|
87
239
|
"""
|
|
88
240
|
Loads and registers core command classes provided by the Orionis framework.
|
|
@@ -141,13 +293,14 @@ class Reactor(IReactor):
|
|
|
141
293
|
# Register the command in the internal registry with all its metadata
|
|
142
294
|
self.__commands[signature] = Command(
|
|
143
295
|
obj=obj,
|
|
296
|
+
method='handle',
|
|
144
297
|
timestamps=timestamp,
|
|
145
298
|
signature=signature,
|
|
146
299
|
description=description,
|
|
147
300
|
args=args
|
|
148
301
|
)
|
|
149
302
|
|
|
150
|
-
def
|
|
303
|
+
def __loadCustomCommands(self) -> None:
|
|
151
304
|
"""
|
|
152
305
|
Loads command classes from Python files in the specified commands directory.
|
|
153
306
|
|
|
@@ -156,13 +309,6 @@ class Reactor(IReactor):
|
|
|
156
309
|
sanitization to handle virtual environment paths and validates command structure
|
|
157
310
|
before registration.
|
|
158
311
|
|
|
159
|
-
Parameters
|
|
160
|
-
----------
|
|
161
|
-
commands_path : str
|
|
162
|
-
The absolute path to the directory containing command modules to load.
|
|
163
|
-
root_path : str
|
|
164
|
-
The root path of the project, used for module path normalization.
|
|
165
|
-
|
|
166
312
|
Returns
|
|
167
313
|
-------
|
|
168
314
|
None
|
|
@@ -177,6 +323,10 @@ class Reactor(IReactor):
|
|
|
177
323
|
- Each discovered command class undergoes structure validation via __ensureStructure
|
|
178
324
|
"""
|
|
179
325
|
|
|
326
|
+
# Ensure the provided commands_path is a valid directory
|
|
327
|
+
commands_path = (Path(self.__app.path('console')) / 'commands').resolve()
|
|
328
|
+
root_path = self.__app.getBasePath()
|
|
329
|
+
|
|
180
330
|
# Iterate through the command path and load command modules
|
|
181
331
|
for current_directory, _, files in os.walk(commands_path):
|
|
182
332
|
for file in files:
|
|
@@ -222,6 +372,7 @@ class Reactor(IReactor):
|
|
|
222
372
|
# Add the command to the internal registry
|
|
223
373
|
self.__commands[signature] = Command(
|
|
224
374
|
obj=obj,
|
|
375
|
+
method='handle',
|
|
225
376
|
timestamps=timestamp,
|
|
226
377
|
signature=signature,
|
|
227
378
|
description=description,
|
|
@@ -477,6 +628,60 @@ class Reactor(IReactor):
|
|
|
477
628
|
# Return an empty dictionary if no arguments were parsed
|
|
478
629
|
return {}
|
|
479
630
|
|
|
631
|
+
def command(
|
|
632
|
+
self,
|
|
633
|
+
signature: str,
|
|
634
|
+
handler: Any
|
|
635
|
+
) -> ICommand:
|
|
636
|
+
"""
|
|
637
|
+
Define a new command using a fluent interface.
|
|
638
|
+
|
|
639
|
+
This method allows defining a new command with a specified signature and handler
|
|
640
|
+
function using a fluent interface pattern. The command can be further configured
|
|
641
|
+
by chaining additional method calls to set properties such as timestamps,
|
|
642
|
+
description, and arguments.
|
|
643
|
+
|
|
644
|
+
Parameters
|
|
645
|
+
----------
|
|
646
|
+
signature : str
|
|
647
|
+
The unique signature identifier for the command. Must follow naming conventions
|
|
648
|
+
(alphanumeric characters, underscores, colons, cannot start/end with underscore
|
|
649
|
+
or colon, cannot start with a number).
|
|
650
|
+
handler : Any
|
|
651
|
+
The function or callable that will be executed when the command is invoked.
|
|
652
|
+
This should be a valid function that accepts parameters matching the command's
|
|
653
|
+
defined arguments.
|
|
654
|
+
|
|
655
|
+
Returns
|
|
656
|
+
-------
|
|
657
|
+
ICommand
|
|
658
|
+
Returns an instance of ICommand that allows further configuration of the command
|
|
659
|
+
through method chaining.
|
|
660
|
+
|
|
661
|
+
Raises
|
|
662
|
+
------
|
|
663
|
+
TypeError
|
|
664
|
+
If the signature is not a string or if the handler is not callable.
|
|
665
|
+
ValueError
|
|
666
|
+
If the signature does not meet the required naming conventions.
|
|
667
|
+
"""
|
|
668
|
+
|
|
669
|
+
# Import the FluentCommand class here to avoid circular imports
|
|
670
|
+
from orionis.console.fluent.command import Command as FluentCommand
|
|
671
|
+
|
|
672
|
+
# Create a new FluentCommand instance with the provided signature and handler
|
|
673
|
+
f_command = FluentCommand(
|
|
674
|
+
signature=signature,
|
|
675
|
+
concrete=handler[0],
|
|
676
|
+
method=handler[1] if len(handler) > 1 else 'handle'
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
# Append the new command to the internal list of fluent commands
|
|
680
|
+
self.__fluent_commands.append(f_command)
|
|
681
|
+
|
|
682
|
+
# Return the newly created command for further configuration
|
|
683
|
+
return self.__fluent_commands[-1]
|
|
684
|
+
|
|
480
685
|
def info(self) -> List[dict]:
|
|
481
686
|
"""
|
|
482
687
|
Retrieves a list of all registered commands with their metadata.
|
|
@@ -493,6 +698,9 @@ class Reactor(IReactor):
|
|
|
493
698
|
contains the command's signature, description, and timestamps status.
|
|
494
699
|
"""
|
|
495
700
|
|
|
701
|
+
# Ensure commands are loaded before retrieving information
|
|
702
|
+
self.__loadCommands()
|
|
703
|
+
|
|
496
704
|
# Prepare a list to hold command information
|
|
497
705
|
commands_info = []
|
|
498
706
|
|
|
@@ -558,51 +766,64 @@ class Reactor(IReactor):
|
|
|
558
766
|
- All exceptions are logged and displayed in the console.
|
|
559
767
|
"""
|
|
560
768
|
|
|
561
|
-
#
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
769
|
+
# Scope Request instances to this command execution context
|
|
770
|
+
with self.__app.createContext():
|
|
771
|
+
|
|
772
|
+
# Ensure commands are loaded before attempting to execute
|
|
773
|
+
self.__loadCommands()
|
|
565
774
|
|
|
566
|
-
|
|
567
|
-
|
|
775
|
+
# Retrieve the command from the registry by its signature
|
|
776
|
+
command: Command = self.__commands.get(signature)
|
|
777
|
+
if command is None:
|
|
778
|
+
raise CLIOrionisValueError(f"Command '{signature}' not found.")
|
|
568
779
|
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
self.__executer.running(program=signature)
|
|
780
|
+
# Start execution timer for performance measurement
|
|
781
|
+
self.__performance_counter.start()
|
|
572
782
|
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
783
|
+
# Log the command execution start with RUNNING state if timestamps are enabled
|
|
784
|
+
if command.timestamps:
|
|
785
|
+
self.__executer.running(program=signature)
|
|
576
786
|
|
|
577
|
-
|
|
578
|
-
|
|
787
|
+
try:
|
|
788
|
+
# Instantiate the command class using the application container
|
|
789
|
+
command_instance: IBaseCommand = self.__app.make(command.obj)
|
|
579
790
|
|
|
580
|
-
|
|
581
|
-
|
|
791
|
+
# Inject parsed arguments into the command instance
|
|
792
|
+
_args = self.__parseArgs(command, args)
|
|
793
|
+
command_instance._args = _args.copy()
|
|
582
794
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
795
|
+
# Inject a scoped CLIRequest instance into the application container for the command's context
|
|
796
|
+
self.__app.scopedInstance(ICLIRequest, CLIRequest(
|
|
797
|
+
command=signature,
|
|
798
|
+
args=_args.copy()
|
|
799
|
+
))
|
|
587
800
|
|
|
588
|
-
|
|
589
|
-
|
|
801
|
+
# Execute the command's handle method and capture its output
|
|
802
|
+
output = self.__app.call(command_instance, command.method)
|
|
590
803
|
|
|
591
|
-
|
|
592
|
-
|
|
804
|
+
# Calculate elapsed time and log completion with DONE state if command.timestamps are enabled
|
|
805
|
+
elapsed_time = round(self.__performance_counter.stop(), 2)
|
|
806
|
+
if command.timestamps:
|
|
807
|
+
self.__executer.done(program=signature, time=f"{elapsed_time}s")
|
|
593
808
|
|
|
594
|
-
|
|
809
|
+
# Log successful execution in the logger service
|
|
810
|
+
self.__logger.info(f"Command '{signature}' executed successfully in ({elapsed_time}) seconds.")
|
|
595
811
|
|
|
596
|
-
|
|
597
|
-
|
|
812
|
+
# Return the output produced by the command, if any
|
|
813
|
+
return output
|
|
598
814
|
|
|
599
|
-
|
|
600
|
-
elapsed_time = round(self.__performance_counter.stop(), 2)
|
|
601
|
-
if command.timestamps:
|
|
602
|
-
self.__executer.fail(program=signature, time=f"{elapsed_time}s")
|
|
815
|
+
except Exception as e:
|
|
603
816
|
|
|
604
|
-
|
|
605
|
-
|
|
817
|
+
# Log the error in the logger service
|
|
818
|
+
self.__logger.error(f"Command '{signature}' execution failed: {e}")
|
|
819
|
+
|
|
820
|
+
# Calculate elapsed time and log failure with ERROR state if command.timestamps are enabled
|
|
821
|
+
elapsed_time = round(self.__performance_counter.stop(), 2)
|
|
822
|
+
if command.timestamps:
|
|
823
|
+
self.__executer.fail(program=signature, time=f"{elapsed_time}s")
|
|
824
|
+
|
|
825
|
+
# Propagate the exception after logging
|
|
826
|
+
raise
|
|
606
827
|
|
|
607
828
|
async def callAsync(
|
|
608
829
|
self,
|
|
@@ -646,48 +867,61 @@ class Reactor(IReactor):
|
|
|
646
867
|
- All exceptions are logged and displayed in the console.
|
|
647
868
|
"""
|
|
648
869
|
|
|
649
|
-
#
|
|
650
|
-
|
|
651
|
-
if command is None:
|
|
652
|
-
raise CLIOrionisValueError(f"Command '{signature}' not found.")
|
|
870
|
+
# Scope Request instances to this command execution context
|
|
871
|
+
with self.__app.createContext():
|
|
653
872
|
|
|
654
|
-
|
|
655
|
-
|
|
873
|
+
# Ensure commands are loaded before attempting to execute
|
|
874
|
+
self.__loadCommands()
|
|
656
875
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
876
|
+
# Retrieve the command from the registry by its signature
|
|
877
|
+
command: Command = self.__commands.get(signature)
|
|
878
|
+
if command is None:
|
|
879
|
+
raise CLIOrionisValueError(f"Command '{signature}' not found.")
|
|
660
880
|
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
command_instance: IBaseCommand = self.__app.make(command.obj)
|
|
881
|
+
# Start execution timer for performance measurement
|
|
882
|
+
self.__performance_counter.start()
|
|
664
883
|
|
|
665
|
-
#
|
|
666
|
-
|
|
884
|
+
# Log the command execution start with RUNNING state if timestamps are enabled
|
|
885
|
+
if command.timestamps:
|
|
886
|
+
self.__executer.running(program=signature)
|
|
667
887
|
|
|
668
|
-
|
|
669
|
-
|
|
888
|
+
try:
|
|
889
|
+
# Instantiate the command class using the application container
|
|
890
|
+
command_instance: IBaseCommand = self.__app.make(command.obj)
|
|
670
891
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
self.__executer.done(program=signature, time=f"{elapsed_time}s")
|
|
892
|
+
# Inject parsed arguments into the command instance
|
|
893
|
+
_args = self.__parseArgs(command, args)
|
|
894
|
+
command_instance._args = _args.copy()
|
|
675
895
|
|
|
676
|
-
|
|
677
|
-
|
|
896
|
+
# Inject a scoped CLIRequest instance into the application container for the command's context
|
|
897
|
+
self.__app.scopedInstance(ICLIRequest, CLIRequest(
|
|
898
|
+
command=signature,
|
|
899
|
+
args=_args.copy()
|
|
900
|
+
))
|
|
678
901
|
|
|
679
|
-
|
|
680
|
-
|
|
902
|
+
# Execute the command's handle method asynchronously and capture its output
|
|
903
|
+
output = await self.__app.callAsync(command_instance, 'handle')
|
|
681
904
|
|
|
682
|
-
|
|
905
|
+
# Calculate elapsed time and log completion with DONE state if command.timestamps are enabled
|
|
906
|
+
elapsed_time = round(self.__performance_counter.stop(), 2)
|
|
907
|
+
if command.timestamps:
|
|
908
|
+
self.__executer.done(program=signature, time=f"{elapsed_time}s")
|
|
683
909
|
|
|
684
|
-
|
|
685
|
-
|
|
910
|
+
# Log successful execution in the logger service
|
|
911
|
+
self.__logger.info(f"Command '{signature}' executed successfully in ({elapsed_time}) seconds.")
|
|
686
912
|
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
913
|
+
# Return the output produced by the command, if any
|
|
914
|
+
return output
|
|
915
|
+
|
|
916
|
+
except Exception as e:
|
|
917
|
+
|
|
918
|
+
# Log the error in the logger service
|
|
919
|
+
self.__logger.error(f"Command '{signature}' execution failed: {e}")
|
|
920
|
+
|
|
921
|
+
# Calculate elapsed time and log failure with ERROR state if command.timestamps are enabled
|
|
922
|
+
elapsed_time = round(self.__performance_counter.stop(), 2)
|
|
923
|
+
if command.timestamps:
|
|
924
|
+
self.__executer.fail(program=signature, time=f"{elapsed_time}s")
|
|
691
925
|
|
|
692
|
-
|
|
693
|
-
|
|
926
|
+
# Propagate the exception after logging
|
|
927
|
+
raise
|
orionis/console/dumper/dump.py
CHANGED
|
@@ -9,7 +9,7 @@ from rich.panel import Panel
|
|
|
9
9
|
from rich.syntax import Syntax
|
|
10
10
|
from rich.table import Table
|
|
11
11
|
from rich.traceback import install
|
|
12
|
-
from orionis.console.
|
|
12
|
+
from orionis.console.contracts.dump import IDebug
|
|
13
13
|
|
|
14
14
|
class Debug(IDebug):
|
|
15
15
|
|
|
@@ -31,7 +31,7 @@ class Event:
|
|
|
31
31
|
details : Optional[str]
|
|
32
32
|
Additional details or metadata about the event. Can be None if not specified.
|
|
33
33
|
listener : Optional[IScheduleEventListener]
|
|
34
|
-
An optional listener object that implements the IScheduleEventListener interface.
|
|
34
|
+
An optional listener object that implements the IScheduleEventListener interface.
|
|
35
35
|
This listener can handle event-specific logic. Defaults to None.
|
|
36
36
|
"""
|
|
37
37
|
|