orionis 0.447.0__py3-none-any.whl → 0.449.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.
Files changed (32) hide show
  1. orionis/console/args/argument.py +174 -43
  2. orionis/console/args/parser.py +37 -3
  3. orionis/console/base/command.py +103 -48
  4. orionis/console/base/contracts/command.py +97 -40
  5. orionis/console/core/reactor.py +412 -16
  6. orionis/console/output/contracts/executor.py +93 -0
  7. orionis/console/output/executor.py +153 -0
  8. orionis/foundation/application.py +6 -2
  9. orionis/foundation/providers/console_provider.py +35 -10
  10. orionis/foundation/providers/dumper_provider.py +42 -14
  11. orionis/foundation/providers/executor_provider.py +80 -0
  12. orionis/foundation/providers/inspirational_provider.py +43 -23
  13. orionis/foundation/providers/logger_provider.py +47 -8
  14. orionis/foundation/providers/progress_bar_provider.py +55 -10
  15. orionis/foundation/providers/testing_provider.py +75 -31
  16. orionis/foundation/providers/workers_provider.py +69 -11
  17. orionis/metadata/framework.py +1 -1
  18. orionis/support/facades/console.py +11 -3
  19. orionis/support/facades/dumper.py +10 -3
  20. orionis/support/facades/executor.py +24 -0
  21. orionis/support/facades/inspire.py +9 -6
  22. orionis/support/facades/logger.py +11 -4
  23. orionis/support/facades/progress_bar.py +9 -3
  24. orionis/support/facades/testing.py +10 -4
  25. orionis/support/facades/workers.py +8 -4
  26. orionis/test/kernel.py +2 -2
  27. {orionis-0.447.0.dist-info → orionis-0.449.0.dist-info}/METADATA +1 -1
  28. {orionis-0.447.0.dist-info → orionis-0.449.0.dist-info}/RECORD +32 -28
  29. {orionis-0.447.0.dist-info → orionis-0.449.0.dist-info}/WHEEL +0 -0
  30. {orionis-0.447.0.dist-info → orionis-0.449.0.dist-info}/licenses/LICENCE +0 -0
  31. {orionis-0.447.0.dist-info → orionis-0.449.0.dist-info}/top_level.txt +0 -0
  32. {orionis-0.447.0.dist-info → orionis-0.449.0.dist-info}/zip-safe +0 -0
@@ -45,69 +45,69 @@ class CLIArgument:
45
45
  """
46
46
 
47
47
  # Required fields
48
- flags: List[str] = None
49
- type: Type = None
50
- help: str = None
48
+ flags: List[str]
49
+ type: Type
50
+ help: Optional[str] = None
51
51
 
52
52
  default: Any = field(
53
- default_factory = None,
54
- metadata = {
53
+ default=None,
54
+ metadata={
55
55
  "description": "Default value for the argument.",
56
56
  "default": None
57
57
  }
58
58
  )
59
59
 
60
60
  choices: Optional[List[Any]] = field(
61
- default_factory = None,
62
- metadata = {
61
+ default=None,
62
+ metadata={
63
63
  "description": "List of valid choices for the argument.",
64
64
  "default": None
65
65
  }
66
66
  )
67
67
 
68
68
  required: bool = field(
69
- default_factory = False,
70
- metadata = {
69
+ default=False,
70
+ metadata={
71
71
  "description": "Indicates if the argument is required.",
72
72
  "default": False
73
73
  }
74
74
  )
75
75
 
76
76
  metavar: Optional[str] = field(
77
- default_factory = None,
78
- metadata = {
77
+ default=None,
78
+ metadata={
79
79
  "description": "Metavar for displaying in help messages.",
80
80
  "default": None
81
81
  }
82
82
  )
83
83
 
84
84
  dest: Optional[str] = field(
85
- default_factory = None,
86
- metadata = {
85
+ default=None,
86
+ metadata={
87
87
  "description": "Destination name for the argument in the namespace.",
88
88
  "default": None
89
89
  }
90
90
  )
91
91
 
92
92
  action: Union[str, ArgumentAction] = field(
93
- default_factory = ArgumentAction.STORE,
94
- metadata = {
93
+ default=ArgumentAction.STORE,
94
+ metadata={
95
95
  "description": "Action to perform with the argument.",
96
96
  "default": ArgumentAction.STORE.value
97
97
  }
98
98
  )
99
99
 
100
100
  nargs: Optional[Union[int, str]] = field(
101
- default_factory = None,
102
- metadata = {
101
+ default=None,
102
+ metadata={
103
103
  "description": "Number of arguments expected (e.g., 1, 2, '+', '*').",
104
104
  "default": None
105
105
  }
106
106
  )
107
107
 
108
108
  const: Any = field(
109
- default_factory = None,
110
- metadata = {
109
+ default=None,
110
+ metadata={
111
111
  "description": "Constant value for store_const or append_const actions.",
112
112
  "default": None
113
113
  }
@@ -159,7 +159,8 @@ class CLIArgument:
159
159
 
160
160
  # Auto-generate help if not provided
161
161
  if self.help is None:
162
- object.__setattr__(self, 'help', f"Argument for {primary_flag}")
162
+ clean_flag = primary_flag.lstrip('-').replace('-', ' ').title()
163
+ object.__setattr__(self, 'help', f"{clean_flag} argument")
163
164
 
164
165
  # Ensure help is a string
165
166
  if not isinstance(self.help, str):
@@ -204,9 +205,7 @@ class CLIArgument:
204
205
  raise CLIOrionisValueError(f"Destination '{self.dest}' is not a valid Python identifier")
205
206
 
206
207
  # Normalize action value
207
- if self.action is None:
208
- object.__setattr__(self, 'action', ArgumentAction.STORE.value)
209
- elif isinstance(self.action, str):
208
+ if isinstance(self.action, str):
210
209
  try:
211
210
  action_enum = ArgumentAction(self.action)
212
211
  object.__setattr__(self, 'action', action_enum.value)
@@ -217,22 +216,74 @@ class CLIArgument:
217
216
  else:
218
217
  raise CLIOrionisValueError("Action must be a string or an ArgumentAction enum value")
219
218
 
219
+ # Determine if this is an optional argument (starts with dash)
220
+ is_optional = any(flag.startswith('-') for flag in self.flags)
221
+
220
222
  # Special handling for boolean types
221
223
  if self.type is bool:
224
+ # Auto-configure action based on default value and whether it's optional
225
+ if is_optional:
226
+ action = ArgumentAction.STORE_FALSE.value if self.default else ArgumentAction.STORE_TRUE.value
227
+ object.__setattr__(self, 'action', action)
228
+ # argparse ignores type with store_true/false actions
229
+ object.__setattr__(self, 'type', None)
230
+ else:
231
+ # For positional boolean arguments, keep type as bool
232
+ pass
222
233
 
223
- # Auto-configure action based on default value
224
- action = ArgumentAction.STORE_TRUE.value if not self.default else ArgumentAction.STORE_FALSE.value
225
- object.__setattr__(self, 'action', action)
226
-
227
- # argparse ignores type with store_true/false actions
234
+ # Special handling for list types
235
+ elif self.type is list:
236
+ if self.nargs is None:
237
+ # Auto-configure for accepting multiple values
238
+ object.__setattr__(self, 'nargs', '+' if is_optional else '*')
239
+ # Keep type as list for proper conversion
240
+ object.__setattr__(self, 'type', str) # argparse expects element type, not list
241
+
242
+ # Handle count action - typically used for verbosity flags
243
+ elif self.action == ArgumentAction.COUNT.value:
244
+ object.__setattr__(self, 'type', None) # count action doesn't use type
245
+ if self.default is None:
246
+ object.__setattr__(self, 'default', 0)
247
+
248
+ # Handle const actions
249
+ if self.action in (ArgumentAction.STORE_CONST.value, ArgumentAction.APPEND_CONST.value):
250
+ if self.const is None:
251
+ # Auto-set const based on type or use True as default
252
+ if self.type is bool:
253
+ object.__setattr__(self, 'const', True)
254
+ elif self.type is int:
255
+ object.__setattr__(self, 'const', 1)
256
+ elif self.type is str:
257
+ object.__setattr__(self, 'const', self.dest)
258
+ else:
259
+ object.__setattr__(self, 'const', True)
260
+ object.__setattr__(self, 'type', None) # const actions don't use type
261
+
262
+ # Handle nargs '?' - optional single argument
263
+ elif self.nargs == '?':
264
+ if self.const is None and is_optional:
265
+ # For optional arguments with nargs='?', set a reasonable const
266
+ object.__setattr__(self, 'const', True if self.type is bool else self.dest)
267
+
268
+ # Validate nargs compatibility
269
+ if self.nargs is not None:
270
+ valid_nargs = ['?', '*', '+'] + [str(i) for i in range(0, 10)]
271
+ if isinstance(self.nargs, int):
272
+ if self.nargs < 0:
273
+ raise CLIOrionisValueError("nargs cannot be negative")
274
+ elif self.nargs not in valid_nargs:
275
+ raise CLIOrionisValueError(f"Invalid nargs value: {self.nargs}")
276
+
277
+ # Handle version action
278
+ if self.action == ArgumentAction.VERSION.value:
228
279
  object.__setattr__(self, 'type', None)
280
+ if 'version' not in self.dest:
281
+ object.__setattr__(self, 'dest', 'version')
229
282
 
230
- # Special handling for list types
231
- if self.type is list and self.nargs is None:
283
+ # Handle help action
284
+ if self.action == ArgumentAction.HELP.value:
285
+ object.__setattr__(self, 'type', None)
232
286
 
233
- # Auto-configure for accepting multiple values
234
- object.__setattr__(self, 'nargs', '+')
235
- object.__setattr__(self, 'type', str)
236
287
 
237
288
  def addToParser(self, parser: argparse.ArgumentParser) -> None:
238
289
  """
@@ -327,17 +378,97 @@ class CLIArgument:
327
378
  "action": self.action, # Action to take when argument is encountered
328
379
  "nargs": self.nargs, # Number of command-line arguments expected
329
380
  "type": self.type, # Type to convert the argument to
330
- "const": self.const # Constant value for certain actions
331
381
  }
332
382
 
333
- # Filter out None values to prevent passing invalid parameters to argparse
334
- # argparse will raise errors if None values are explicitly passed for certain parameters
335
- kwargs = {k: v for k, v in kwargs.items() if v is not None}
336
-
337
- # Remove 'required' parameter for positional arguments since it's not supported
338
- # Positional arguments are inherently required by argparse's design
339
- if is_positional and 'required' in kwargs:
340
- del kwargs['required']
383
+ # Handle const parameter for specific actions
384
+ const_actions = [
385
+ ArgumentAction.STORE_CONST.value,
386
+ ArgumentAction.APPEND_CONST.value
387
+ ]
388
+
389
+ # Add const parameter when it's needed
390
+ if self.action in const_actions or (self.nargs == '?' and self.const is not None):
391
+ kwargs["const"] = self.const
392
+
393
+ # Special handling for version action
394
+ if self.action == ArgumentAction.VERSION.value and hasattr(self, 'version'):
395
+ kwargs["version"] = getattr(self, 'version', None)
396
+
397
+ # Define actions that don't accept certain parameters
398
+ type_ignored_actions = [
399
+ ArgumentAction.STORE_TRUE.value,
400
+ ArgumentAction.STORE_FALSE.value,
401
+ ArgumentAction.STORE_CONST.value,
402
+ ArgumentAction.APPEND_CONST.value,
403
+ ArgumentAction.COUNT.value,
404
+ ArgumentAction.HELP.value,
405
+ ArgumentAction.VERSION.value
406
+ ]
407
+
408
+ # Define actions that don't accept metavar or default parameters
409
+ metavar_ignored_actions = [
410
+ ArgumentAction.STORE_TRUE.value,
411
+ ArgumentAction.STORE_FALSE.value,
412
+ ArgumentAction.COUNT.value,
413
+ ArgumentAction.HELP.value,
414
+ ArgumentAction.VERSION.value
415
+ ]
416
+
417
+ # Define actions that don't accept default parameters
418
+ default_ignored_actions = [
419
+ ArgumentAction.STORE_TRUE.value,
420
+ ArgumentAction.STORE_FALSE.value,
421
+ ArgumentAction.STORE_CONST.value,
422
+ ArgumentAction.APPEND_CONST.value,
423
+ ArgumentAction.HELP.value,
424
+ ArgumentAction.VERSION.value
425
+ ]
426
+
427
+ # Filter out None values and incompatible parameters
428
+ filtered_kwargs = {}
429
+ for k, v in kwargs.items():
430
+ if v is not None:
431
+
432
+ # Skip parameters that are not compatible with certain actions
433
+ if k == "type" and self.action in type_ignored_actions:
434
+ continue
435
+
436
+ # Skip metavar for actions that don't accept it
437
+ if k == "metavar" and self.action in metavar_ignored_actions:
438
+ continue
439
+
440
+ # Skip default for actions that don't accept it
441
+ if k == "default" and self.action in default_ignored_actions:
442
+ continue
443
+
444
+ # Special case: don't include empty strings for metavar in positional args
445
+ if k == "metavar" and is_positional and v == "":
446
+ continue
447
+
448
+ # Add the parameter to the filtered kwargs
449
+ filtered_kwargs[k] = v
450
+
451
+ # Remove parameters that are not compatible with positional arguments
452
+ if is_positional:
453
+
454
+ # Remove 'required' parameter for positional arguments since it's not supported
455
+ if 'required' in filtered_kwargs:
456
+ del filtered_kwargs['required']
457
+
458
+ # Remove 'dest' parameter for positional arguments as argparse calculates it automatically
459
+ if 'dest' in filtered_kwargs:
460
+ del filtered_kwargs['dest']
461
+
462
+ # Remove 'metavar' if it's the same as the flag name (redundant)
463
+ if 'metavar' in filtered_kwargs and len(self.flags) == 1:
464
+ flag_upper = self.flags[0].upper()
465
+ if filtered_kwargs['metavar'] == flag_upper:
466
+ del filtered_kwargs['metavar']
467
+
468
+ # For count action, ensure default is an integer
469
+ if self.action == ArgumentAction.COUNT.value and 'default' in filtered_kwargs:
470
+ if not isinstance(filtered_kwargs['default'], int):
471
+ filtered_kwargs['default'] = 0
341
472
 
342
473
  # Return the cleaned and validated kwargs dictionary
343
- return kwargs
474
+ return filtered_kwargs
@@ -1,6 +1,40 @@
1
+ import argparse
2
+
1
3
  class ArgumentPaser():
2
4
 
3
- def __init__(self, args: dict):
4
- self.__args = args
5
+ def __init__(self):
6
+ self.__parser = argparse.ArgumentParser()
7
+
8
+ def addArgument(self, name: str, **kwargs):
9
+ """
10
+ Add an argument to the parser.
11
+
12
+ Parameters
13
+ ----------
14
+ name : str
15
+ The name of the argument to add.
16
+ kwargs : dict
17
+ Additional keyword arguments for the argument configuration.
18
+
19
+ Returns
20
+ -------
21
+ None
22
+ This method does not return any value.
23
+ """
24
+ self.__parser.add_argument(name, **kwargs)
25
+
26
+ def parse(self, args=None):
27
+ """
28
+ Parse the command-line arguments.
29
+
30
+ Parameters
31
+ ----------
32
+ args : list, optional
33
+ A list of arguments to parse. If None, uses sys.argv by default.
5
34
 
6
- def
35
+ Returns
36
+ -------
37
+ Namespace
38
+ An object containing the parsed arguments as attributes.
39
+ """
40
+ return self.__parser.parse_args(args)
@@ -1,92 +1,147 @@
1
- from typing import Any, Dict
1
+ from typing import Any, Dict, List
2
+ from orionis.console.args.argument import CLIArgument
2
3
  from orionis.console.dynamic.progress_bar import ProgressBar
3
4
  from orionis.console.output.console import Console
4
5
  from orionis.console.base.contracts.command import IBaseCommand
5
6
 
6
7
  class BaseCommand(Console, ProgressBar, IBaseCommand):
7
8
  """
8
- Abstract base class for console commands in Orionis.
9
+ Abstract base class for implementing console commands in the Orionis framework.
9
10
 
10
- Inherits from Console and ProgressBar, allowing commands to:
11
- - Display messages, errors, and formatted text in the console.
12
- - Manage progress bars for long-running tasks.
13
- - Access and manipulate parsed arguments from the command line.
11
+ This class provides a foundation for creating command-line interface commands with
12
+ built-in console output capabilities, progress bar functionality, and argument handling.
13
+ It inherits from Console and ProgressBar to provide rich terminal interaction features
14
+ while implementing the IBaseCommand interface contract.
15
+
16
+ The BaseCommand class serves as a template that enforces a consistent structure for
17
+ all command implementations in the framework, requiring subclasses to implement the
18
+ core command logic while providing common utilities for argument access and console
19
+ operations.
20
+
21
+ This is an abstract base class and should not be instantiated directly.
22
+ All concrete command implementations must inherit from this class and
23
+ provide their own handle() method implementation.
24
+
25
+ The class integrates with the framework's console and progress bar systems,
26
+ allowing commands to provide rich user feedback during execution.
14
27
 
15
28
  Attributes
16
29
  ----------
17
- args : dict
18
- Dictionary containing the parsed arguments for the command. Set via the setArgs method.
30
+ timestamps : bool, default True
31
+ Controls whether timestamps are included in console output messages.
32
+ When True, all console output will be prefixed with timestamp information.
33
+ signature : str
34
+ Defines the command signature string used for command registration and
35
+ automatic help text generation. This should follow the framework's
36
+ signature format conventions.
37
+ description : str
38
+ Human-readable description of the command's purpose and functionality.
39
+ Used in help documentation and command listing interfaces.
40
+ _args : Dict[str, Any], default {}
41
+ Dictionary containing parsed command-line arguments and options.
42
+ Populated automatically by the command parser before handle() execution.
43
+ arguments : List[CLIArgument], default []
44
+ List of CLIArgument instances defining the command's accepted arguments
45
+ and options. Used for argument parsing and validation.
19
46
 
20
47
  Methods
21
48
  -------
22
49
  handle()
23
- Must be implemented by each subclass to define the main logic of the command.
24
- argument(key)
25
- Retrieves the value of a specific argument by key.
26
- arguments()
27
- Returns all parsed arguments as a dictionary.
50
+ Abstract method that must be implemented by subclasses to define the
51
+ main command execution logic.
52
+ argument(key: str)
53
+ Safely retrieves argument values from the parsed arguments dictionary
54
+ with type validation and error handling.
28
55
  """
29
56
 
30
- args: Dict[str, Any] = {}
57
+ # Enable timestamps in console output by default
58
+ timestamps: bool = True
59
+
60
+ # Command signature string for registration and help text generation
61
+ signature: str
62
+
63
+ # Human-readable description for documentation and help display
64
+ description: str
65
+
66
+ # Dictionary to store parsed command-line arguments and options
67
+ _args: Dict[str, Any] = {}
68
+
69
+ # List of CLIArgument instances defining command arguments
70
+ arguments: List[CLIArgument] = []
31
71
 
32
72
  def handle(self):
33
73
  """
34
- Main entry point for command execution.
74
+ Execute the main command logic.
35
75
 
36
- This method must be overridden in each subclass to define the specific logic of the command.
37
- Access parsed arguments via self.args and use console and progress bar methods as needed.
76
+ This abstract method defines the entry point for command execution and must be
77
+ implemented by all concrete command subclasses. It serves as the primary interface
78
+ for running the command's core functionality after argument parsing and validation.
38
79
 
39
- Example:
40
- def handle(self):
41
- self.write("Processing...")
42
- value = self.argument("key")
43
- # custom logic
80
+ Returns
81
+ -------
82
+ None
83
+ This method does not return any value. All command output should be handled
84
+ through the inherited console methods or other side effects.
44
85
 
45
86
  Raises
46
87
  ------
47
88
  NotImplementedError
48
- Always raised in the base class. Subclasses must implement this method.
89
+ Always raised when called on the base class, indicating that subclasses
90
+ must provide their own implementation of this method.
91
+
92
+ Notes
93
+ -----
94
+ Subclasses should override this method to implement their specific command
95
+ behavior. The method will be called after all command-line arguments have
96
+ been parsed and stored in the _args dictionary.
49
97
  """
98
+
99
+ # Raise an error to enforce implementation in subclasses
50
100
  raise NotImplementedError("The 'handle' method must be implemented in the subclass.")
51
101
 
52
- def argument(self, key: str):
102
+ def argument(self, key: str, default: Any = None) -> Any:
53
103
  """
54
- Retrieves the value of a specific argument by its key.
104
+ Retrieve the value of a specific command-line argument by key with optional default fallback.
105
+
106
+ This method provides safe and validated access to command-line arguments stored in the
107
+ internal arguments dictionary. It performs type checking on both the key parameter and
108
+ the internal _args attribute to ensure data integrity before attempting retrieval.
109
+
110
+ The method follows a fail-safe approach by returning a default value when the requested
111
+ argument key is not found, preventing KeyError exceptions during command execution.
55
112
 
56
113
  Parameters
57
114
  ----------
58
115
  key : str
59
- Name of the argument to retrieve.
116
+ The string identifier used to locate the desired argument in the arguments
117
+ dictionary. Must be a non-empty string that corresponds to a valid argument name.
118
+ default : Any, optional
119
+ The fallback value to return if the specified key is not found in the arguments
120
+ dictionary. Defaults to None if not provided.
60
121
 
61
122
  Returns
62
123
  -------
63
- any or None
64
- Value associated with the key, or None if it does not exist or arguments are not set.
124
+ Any
125
+ The value associated with the specified key if it exists in the arguments
126
+ dictionary. If the key is not found, returns the provided default value
127
+ or None if no default was specified.
65
128
 
66
129
  Raises
67
130
  ------
68
131
  ValueError
69
- If the key is not a string or if arguments are not a dictionary.
70
-
71
- Example:
72
- value = self.argument("user")
132
+ If the provided key parameter is not of string type.
133
+ ValueError
134
+ If the internal _args attribute is not of dictionary type, indicating
135
+ a corrupted or improperly initialized command state.
73
136
  """
74
- if not isinstance(key, str):
75
- raise ValueError("Argument key must be a string.")
76
- if not isinstance(self.args, dict):
77
- raise ValueError("Arguments must be a dictionary.")
78
- return self.args.get(key)
79
137
 
80
- def arguments(self) -> dict:
81
- """
82
- Returns all parsed arguments as a dictionary.
138
+ # Validate that the key parameter is a string type
139
+ if not isinstance(key, str):
140
+ raise ValueError(f"Argument key must be a string, got '{type(key).__name__}' instead.")
83
141
 
84
- Returns
85
- -------
86
- dict
87
- Dictionary containing all arguments received by the command. If no arguments, returns an empty dictionary.
142
+ # Ensure the internal args attribute is a valid dictionary
143
+ if not isinstance(self._args, dict):
144
+ raise ValueError(f"Arguments must be a dictionary, got '{type(self._args).__name__}' instead.")
88
145
 
89
- Example:
90
- args = self.arguments()
91
- """
92
- return dict(self.args) if isinstance(self.args, dict) else {}
146
+ # Safely retrieve the argument value with optional default fallback
147
+ return self._args.get(key, default)