argenta 1.1.1rc0__py3-none-any.whl → 1.1.2__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.
- argenta/__init__.py +6 -0
- argenta/app/__init__.py +10 -1
- argenta/app/autocompleter/entity.py +18 -19
- argenta/app/defaults.py +0 -1
- argenta/app/dividing_line/models.py +5 -5
- argenta/app/models.py +226 -140
- argenta/app/protocols.py +22 -0
- argenta/app/registered_routers/entity.py +7 -14
- argenta/command/__init__.py +11 -2
- argenta/command/exceptions.py +20 -13
- argenta/command/flag/__init__.py +5 -11
- argenta/command/flag/defaults.py +19 -24
- argenta/command/flag/flags/__init__.py +2 -8
- argenta/command/flag/flags/models.py +65 -49
- argenta/command/flag/models.py +78 -93
- argenta/command/models.py +100 -144
- argenta/di/__init__.py +2 -0
- argenta/di/integration.py +45 -0
- argenta/di/providers.py +14 -0
- argenta/metrics/main.py +2 -2
- argenta/orchestrator/__init__.py +2 -2
- argenta/orchestrator/argparser/__init__.py +6 -1
- argenta/orchestrator/argparser/arguments/__init__.py +3 -3
- argenta/orchestrator/argparser/arguments/models.py +61 -35
- argenta/orchestrator/argparser/entity.py +56 -37
- argenta/orchestrator/entity.py +20 -18
- argenta/py.typed +0 -0
- argenta/response/__init__.py +2 -2
- argenta/response/entity.py +17 -18
- argenta/response/status.py +12 -1
- argenta/router/__init__.py +2 -2
- argenta/router/command_handler/entity.py +9 -27
- argenta/router/entity.py +133 -160
- argenta/router/exceptions.py +9 -12
- {argenta-1.1.1rc0.dist-info → argenta-1.1.2.dist-info}/METADATA +12 -5
- argenta-1.1.2.dist-info/RECORD +44 -0
- argenta-1.1.1rc0.dist-info/RECORD +0 -39
- {argenta-1.1.1rc0.dist-info → argenta-1.1.2.dist-info}/WHEEL +0 -0
- {argenta-1.1.1rc0.dist-info → argenta-1.1.2.dist-info}/licenses/LICENSE +0 -0
argenta/orchestrator/entity.py
CHANGED
@@ -1,35 +1,37 @@
|
|
1
|
-
from argparse import Namespace
|
2
|
-
|
3
1
|
from argenta.app import App
|
2
|
+
from argenta.response import Response
|
3
|
+
|
4
4
|
from argenta.orchestrator.argparser import ArgParser
|
5
|
+
from argenta.di.integration import setup_dishka
|
6
|
+
from argenta.di.providers import SystemProvider
|
7
|
+
|
8
|
+
from dishka import Provider, make_container
|
9
|
+
|
10
|
+
|
11
|
+
DEFAULT_ARGPARSER: ArgParser = ArgParser(processed_args=[])
|
5
12
|
|
6
13
|
|
7
14
|
class Orchestrator:
|
8
|
-
def __init__(self, arg_parser: ArgParser
|
15
|
+
def __init__(self, arg_parser: ArgParser = DEFAULT_ARGPARSER,
|
16
|
+
custom_providers: list[Provider] = [],
|
17
|
+
auto_inject_handlers: bool = True):
|
9
18
|
"""
|
10
19
|
Public. An orchestrator and configurator that defines the behavior of an integrated system, one level higher than the App
|
11
20
|
:param arg_parser: Cmd argument parser and configurator at startup
|
12
21
|
:return: None
|
13
22
|
"""
|
14
|
-
self.
|
15
|
-
|
16
|
-
|
23
|
+
self._arg_parser: ArgParser = arg_parser
|
24
|
+
self._custom_providers: list[Provider] = custom_providers
|
25
|
+
self._auto_inject_handlers: bool = auto_inject_handlers
|
17
26
|
|
18
|
-
|
19
|
-
def start_polling(app: App) -> None:
|
27
|
+
def start_polling(self, app: App) -> None:
|
20
28
|
"""
|
21
29
|
Public. Starting the user input processing cycle
|
22
30
|
:param app: a running application
|
23
31
|
:return: None
|
24
32
|
"""
|
25
|
-
|
33
|
+
container = make_container(SystemProvider(self._arg_parser), *self._custom_providers)
|
34
|
+
Response.patch_by_container(container)
|
35
|
+
setup_dishka(app, auto_inject=self._auto_inject_handlers)
|
26
36
|
|
27
|
-
|
28
|
-
"""
|
29
|
-
Public. Returns the arguments parsed
|
30
|
-
:return: None
|
31
|
-
"""
|
32
|
-
if self.arg_parser:
|
33
|
-
return self.arg_parser.entity.parse_args()
|
34
|
-
else:
|
35
|
-
return None
|
37
|
+
app.run_polling()
|
argenta/py.typed
ADDED
File without changes
|
argenta/response/__init__.py
CHANGED
argenta/response/entity.py
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
from
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
from dishka import Container
|
2
|
+
|
3
|
+
from argenta.command.flag.flags.models import InputFlags
|
4
|
+
from argenta.response.status import ResponseStatus
|
5
|
+
|
6
|
+
|
7
|
+
EMPTY_INPUT_FLAGS: InputFlags = InputFlags()
|
7
8
|
|
8
9
|
|
9
10
|
class Response:
|
10
|
-
|
11
|
+
_dishka_container: Container
|
11
12
|
|
12
13
|
def __init__(
|
13
14
|
self,
|
14
|
-
status:
|
15
|
-
|
16
|
-
undefined_flags: UndefinedInputFlags = UndefinedInputFlags(),
|
17
|
-
invalid_value_flags: InvalidValueInputFlags = InvalidValueInputFlags(),
|
15
|
+
status: ResponseStatus,
|
16
|
+
input_flags: InputFlags = EMPTY_INPUT_FLAGS,
|
18
17
|
):
|
19
18
|
"""
|
20
19
|
Public. The entity of the user input sent to the handler
|
21
20
|
:param status: the status of the response
|
22
|
-
:param
|
23
|
-
:param undefined_flags: undefined input flags
|
24
|
-
:param invalid_value_flags: input flags with invalid values
|
21
|
+
:param input_flags: all input flags
|
25
22
|
"""
|
26
|
-
self.status = status
|
27
|
-
self.
|
28
|
-
|
29
|
-
|
23
|
+
self.status: ResponseStatus = status
|
24
|
+
self.input_flags: InputFlags = input_flags
|
25
|
+
|
26
|
+
@classmethod
|
27
|
+
def patch_by_container(cls, container: Container) -> None:
|
28
|
+
cls._dishka_container = container
|
argenta/response/status.py
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
from enum import Enum
|
2
2
|
|
3
3
|
|
4
|
-
class
|
4
|
+
class ResponseStatus(Enum):
|
5
5
|
ALL_FLAGS_VALID = "ALL_FLAGS_VALID"
|
6
6
|
UNDEFINED_FLAGS = "UNDEFINED_FLAGS"
|
7
7
|
INVALID_VALUE_FLAGS = "INVALID_VALUE_FLAGS"
|
8
8
|
UNDEFINED_AND_INVALID_FLAGS = "UNDEFINED_AND_INVALID_FLAGS"
|
9
|
+
|
10
|
+
@classmethod
|
11
|
+
def from_flags(cls, *, has_invalid_value_flags: bool, has_undefined_flags: bool) -> 'ResponseStatus':
|
12
|
+
key = (has_invalid_value_flags, has_undefined_flags)
|
13
|
+
status_map: dict[tuple[bool, bool], ResponseStatus] = {
|
14
|
+
(True, True): cls.UNDEFINED_AND_INVALID_FLAGS,
|
15
|
+
(True, False): cls.INVALID_VALUE_FLAGS,
|
16
|
+
(False, True): cls.UNDEFINED_FLAGS,
|
17
|
+
(False, False): cls.ALL_FLAGS_VALID,
|
18
|
+
}
|
19
|
+
return status_map[key]
|
argenta/router/__init__.py
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
-
from
|
1
|
+
from collections.abc import Iterator
|
2
|
+
from typing import Callable
|
2
3
|
|
3
4
|
from argenta.command import Command
|
4
5
|
from argenta.response import Response
|
5
6
|
|
6
7
|
|
7
8
|
class CommandHandler:
|
8
|
-
def __init__(self,
|
9
|
+
def __init__(self, handler_as_func: Callable[..., None], handled_command: Command):
|
9
10
|
"""
|
10
11
|
Private. Entity of the model linking the handler and the command being processed
|
11
12
|
:param handler: the handler being called
|
12
13
|
:param handled_command: the command being processed
|
13
14
|
"""
|
14
|
-
self.
|
15
|
-
self.
|
15
|
+
self.handler_as_func: Callable[..., None] = handler_as_func
|
16
|
+
self.handled_command: Command = handled_command
|
16
17
|
|
17
18
|
def handling(self, response: Response) -> None:
|
18
19
|
"""
|
@@ -20,21 +21,7 @@ class CommandHandler:
|
|
20
21
|
:param response: the entity of response: various groups of flags and status of response
|
21
22
|
:return: None
|
22
23
|
"""
|
23
|
-
self.
|
24
|
-
|
25
|
-
def get_handler(self) -> Callable[[Response], None]:
|
26
|
-
"""
|
27
|
-
Private. Returns the handler being called
|
28
|
-
:return: the handler being called as Callable[[Response], None]
|
29
|
-
"""
|
30
|
-
return self._handler
|
31
|
-
|
32
|
-
def get_handled_command(self) -> Command:
|
33
|
-
"""
|
34
|
-
Private. Returns the command being processed
|
35
|
-
:return: the command being processed as Command
|
36
|
-
"""
|
37
|
-
return self._handled_command
|
24
|
+
self.handler_as_func(response)
|
38
25
|
|
39
26
|
|
40
27
|
class CommandHandlers:
|
@@ -43,14 +30,9 @@ class CommandHandlers:
|
|
43
30
|
Private. The model that unites all CommandHandler of the routers
|
44
31
|
:param command_handlers: list of CommandHandlers for register
|
45
32
|
"""
|
46
|
-
self.command_handlers =
|
47
|
-
|
48
|
-
|
49
|
-
"""
|
50
|
-
Private. Returns the list of CommandHandlers
|
51
|
-
:return: the list of CommandHandlers as list[CommandHandler]
|
52
|
-
"""
|
53
|
-
return self.command_handlers
|
33
|
+
self.command_handlers: list[CommandHandler] = (
|
34
|
+
command_handlers if command_handlers else []
|
35
|
+
)
|
54
36
|
|
55
37
|
def add_handler(self, command_handler: CommandHandler) -> None:
|
56
38
|
"""
|
argenta/router/entity.py
CHANGED
@@ -1,29 +1,28 @@
|
|
1
|
-
from typing import Callable,
|
1
|
+
from typing import Callable, TypeAlias
|
2
2
|
from inspect import getfullargspec, get_annotations, getsourcefile, getsourcelines
|
3
3
|
from rich.console import Console
|
4
4
|
|
5
|
-
from argenta.command import Command
|
6
|
-
from argenta.command.
|
7
|
-
from argenta.response import Response,
|
5
|
+
from argenta.command import Command, InputCommand
|
6
|
+
from argenta.command.flag import ValidationStatus
|
7
|
+
from argenta.response import Response, ResponseStatus
|
8
8
|
from argenta.router.command_handler.entity import CommandHandlers, CommandHandler
|
9
|
-
from argenta.command.flag.flags import
|
10
|
-
Flags,
|
11
|
-
InputFlags,
|
12
|
-
UndefinedInputFlags,
|
13
|
-
ValidInputFlags,
|
14
|
-
InvalidValueInputFlags,
|
15
|
-
)
|
9
|
+
from argenta.command.flag.flags import Flags, InputFlags
|
16
10
|
from argenta.router.exceptions import (
|
17
11
|
RepeatedFlagNameException,
|
18
|
-
TooManyTransferredArgsException,
|
19
12
|
RequiredArgumentNotPassedException,
|
20
13
|
TriggerContainSpacesException,
|
21
14
|
)
|
22
15
|
|
23
16
|
|
17
|
+
HandlerFunc: TypeAlias = Callable[..., None]
|
18
|
+
|
19
|
+
|
24
20
|
class Router:
|
25
21
|
def __init__(
|
26
|
-
self,
|
22
|
+
self,
|
23
|
+
*,
|
24
|
+
title: str | None = "Default title",
|
25
|
+
disable_redirect_stdout: bool = False,
|
27
26
|
):
|
28
27
|
"""
|
29
28
|
Public. Directly configures and manages handlers
|
@@ -35,13 +34,13 @@ class Router:
|
|
35
34
|
which is ambiguous behavior and can lead to unexpected work
|
36
35
|
:return: None
|
37
36
|
"""
|
38
|
-
self.title = title
|
39
|
-
self.disable_redirect_stdout = disable_redirect_stdout
|
37
|
+
self.title: str | None = title
|
38
|
+
self.disable_redirect_stdout: bool = disable_redirect_stdout
|
40
39
|
|
41
|
-
self.
|
42
|
-
self.
|
40
|
+
self.command_handlers: CommandHandlers = CommandHandlers()
|
41
|
+
self.command_register_ignore: bool = False
|
43
42
|
|
44
|
-
def command(self, command: Command | str) -> Callable:
|
43
|
+
def command(self, command: Command | str) -> Callable[[HandlerFunc], HandlerFunc]:
|
45
44
|
"""
|
46
45
|
Public. Registers handler
|
47
46
|
:param command: Registered command
|
@@ -51,18 +50,15 @@ class Router:
|
|
51
50
|
redefined_command = Command(command)
|
52
51
|
else:
|
53
52
|
redefined_command = command
|
54
|
-
self._validate_command(redefined_command)
|
55
|
-
|
56
|
-
def command_decorator(func):
|
57
|
-
Router._validate_func_args(func)
|
58
|
-
self._command_handlers.add_handler(CommandHandler(func, redefined_command))
|
59
53
|
|
60
|
-
|
61
|
-
return func(*args, **kwargs)
|
54
|
+
_validate_command(redefined_command)
|
62
55
|
|
63
|
-
|
56
|
+
def decorator(func: HandlerFunc) -> HandlerFunc:
|
57
|
+
_validate_func_args(func)
|
58
|
+
self.command_handlers.add_handler(CommandHandler(func, redefined_command))
|
59
|
+
return func
|
64
60
|
|
65
|
-
return
|
61
|
+
return decorator
|
66
62
|
|
67
63
|
def finds_appropriate_handler(self, input_command: InputCommand) -> None:
|
68
64
|
"""
|
@@ -70,14 +66,14 @@ class Router:
|
|
70
66
|
:param input_command: input command as InputCommand
|
71
67
|
:return: None
|
72
68
|
"""
|
73
|
-
input_command_name: str = input_command.
|
74
|
-
input_command_flags: InputFlags = input_command.
|
69
|
+
input_command_name: str = input_command.trigger
|
70
|
+
input_command_flags: InputFlags = input_command.input_flags
|
75
71
|
|
76
|
-
for command_handler in self.
|
77
|
-
handle_command = command_handler.
|
78
|
-
if input_command_name.lower() == handle_command.
|
72
|
+
for command_handler in self.command_handlers:
|
73
|
+
handle_command = command_handler.handled_command
|
74
|
+
if input_command_name.lower() == handle_command.trigger.lower():
|
79
75
|
self.process_input_command(input_command_flags, command_handler)
|
80
|
-
if input_command_name.lower() in handle_command.
|
76
|
+
if input_command_name.lower() in handle_command.aliases:
|
81
77
|
self.process_input_command(input_command_flags, command_handler)
|
82
78
|
|
83
79
|
def process_input_command(
|
@@ -89,152 +85,129 @@ class Router:
|
|
89
85
|
:param command_handler: command handler for input command as CommandHandler
|
90
86
|
:return: None
|
91
87
|
"""
|
92
|
-
handle_command = command_handler.
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
88
|
+
handle_command = command_handler.handled_command
|
89
|
+
if handle_command.registered_flags.flags:
|
90
|
+
if input_command_flags.flags:
|
91
|
+
response: Response = _structuring_input_flags(
|
92
|
+
handle_command, input_command_flags
|
93
|
+
)
|
97
94
|
command_handler.handling(response)
|
98
95
|
else:
|
99
|
-
response
|
96
|
+
response = Response(ResponseStatus.ALL_FLAGS_VALID)
|
100
97
|
command_handler.handling(response)
|
101
98
|
else:
|
102
|
-
if input_command_flags.
|
103
|
-
|
104
|
-
|
105
|
-
|
99
|
+
if input_command_flags.flags:
|
100
|
+
undefined_flags = InputFlags()
|
101
|
+
for input_flag in input_command_flags:
|
102
|
+
input_flag.status = ValidationStatus.UNDEFINED
|
103
|
+
undefined_flags.add_flag(input_flag)
|
104
|
+
response = Response(
|
105
|
+
ResponseStatus.UNDEFINED_FLAGS, input_flags=undefined_flags
|
106
|
+
)
|
106
107
|
command_handler.handling(response)
|
107
108
|
else:
|
108
|
-
response
|
109
|
+
response = Response(ResponseStatus.ALL_FLAGS_VALID)
|
109
110
|
command_handler.handling(response)
|
110
111
|
|
111
|
-
@
|
112
|
-
def
|
113
|
-
handled_command: Command, input_flags: InputFlags
|
114
|
-
) -> Response:
|
115
|
-
"""
|
116
|
-
Private. Validates flags of input command
|
117
|
-
:param handled_command: entity of the handled command
|
118
|
-
:param input_flags:
|
119
|
-
:return: entity of response as Response
|
120
|
-
"""
|
121
|
-
valid_input_flags: ValidInputFlags = ValidInputFlags()
|
122
|
-
invalid_value_input_flags: InvalidValueInputFlags = InvalidValueInputFlags()
|
123
|
-
undefined_input_flags: UndefinedInputFlags = UndefinedInputFlags()
|
124
|
-
for flag in input_flags:
|
125
|
-
flag_status: Literal["Undefined", "Valid", "Invalid"] = (
|
126
|
-
handled_command.validate_input_flag(flag)
|
127
|
-
)
|
128
|
-
if flag_status == "Valid":
|
129
|
-
valid_input_flags.add_flag(flag)
|
130
|
-
elif flag_status == "Undefined":
|
131
|
-
undefined_input_flags.add_flag(flag)
|
132
|
-
elif flag_status == "Invalid":
|
133
|
-
invalid_value_input_flags.add_flag(flag)
|
134
|
-
|
135
|
-
if (
|
136
|
-
not invalid_value_input_flags.get_flags()
|
137
|
-
and not undefined_input_flags.get_flags()
|
138
|
-
):
|
139
|
-
status = Status.ALL_FLAGS_VALID
|
140
|
-
elif (
|
141
|
-
invalid_value_input_flags.get_flags()
|
142
|
-
and not undefined_input_flags.get_flags()
|
143
|
-
):
|
144
|
-
status = Status.INVALID_VALUE_FLAGS
|
145
|
-
elif (
|
146
|
-
not invalid_value_input_flags.get_flags()
|
147
|
-
and undefined_input_flags.get_flags()
|
148
|
-
):
|
149
|
-
status = Status.UNDEFINED_FLAGS
|
150
|
-
else:
|
151
|
-
status = Status.UNDEFINED_AND_INVALID_FLAGS
|
152
|
-
|
153
|
-
return Response(
|
154
|
-
invalid_value_flags=invalid_value_input_flags,
|
155
|
-
valid_flags=valid_input_flags,
|
156
|
-
status=status,
|
157
|
-
undefined_flags=undefined_input_flags,
|
158
|
-
)
|
159
|
-
|
160
|
-
@staticmethod
|
161
|
-
def _validate_command(command: Command) -> None:
|
162
|
-
"""
|
163
|
-
Private. Validates the command registered in handler
|
164
|
-
:param command: validated command
|
165
|
-
:return: None if command is valid else raise exception
|
166
|
-
"""
|
167
|
-
command_name: str = command.get_trigger()
|
168
|
-
if command_name.find(" ") != -1:
|
169
|
-
raise TriggerContainSpacesException()
|
170
|
-
flags: Flags = command.get_registered_flags()
|
171
|
-
if flags:
|
172
|
-
flags_name: list = [x.get_string_entity().lower() for x in flags]
|
173
|
-
if len(set(flags_name)) < len(flags_name):
|
174
|
-
raise RepeatedFlagNameException()
|
175
|
-
|
176
|
-
@staticmethod
|
177
|
-
def _validate_func_args(func: Callable) -> None:
|
178
|
-
"""
|
179
|
-
Private. Validates the arguments of the handler
|
180
|
-
:param func: entity of the handler func
|
181
|
-
:return: None if func is valid else raise exception
|
182
|
-
"""
|
183
|
-
transferred_args = getfullargspec(func).args
|
184
|
-
if len(transferred_args) > 1:
|
185
|
-
raise TooManyTransferredArgsException()
|
186
|
-
elif len(transferred_args) == 0:
|
187
|
-
raise RequiredArgumentNotPassedException()
|
188
|
-
|
189
|
-
transferred_arg: str = transferred_args[0]
|
190
|
-
func_annotations: dict[str, Type] = get_annotations(func)
|
191
|
-
|
192
|
-
if arg_annotation := func_annotations.get(transferred_arg):
|
193
|
-
if arg_annotation is Response:
|
194
|
-
pass
|
195
|
-
else:
|
196
|
-
file_path: str | None = getsourcefile(func)
|
197
|
-
source_line: int = getsourcelines(func)[1]
|
198
|
-
fprint = Console().print
|
199
|
-
fprint(
|
200
|
-
f'\nFile "{file_path}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint '
|
201
|
-
f"of argument([green]{transferred_arg}[/green]) passed to the handler is [/i][bold blue]{Response}[/bold blue],"
|
202
|
-
f" [i]but[/i] [bold blue]{arg_annotation}[/bold blue] [i]is specified[/i]",
|
203
|
-
highlight=False,
|
204
|
-
)
|
205
|
-
|
206
|
-
def set_command_register_ignore(self, _: bool) -> None:
|
207
|
-
"""
|
208
|
-
Private. Sets the router behavior on the input commands register
|
209
|
-
:param _: is command register ignore
|
210
|
-
:return: None
|
211
|
-
"""
|
212
|
-
self._ignore_command_register = _
|
213
|
-
|
214
|
-
def get_triggers(self) -> list[str]:
|
112
|
+
@property
|
113
|
+
def triggers(self) -> list[str]:
|
215
114
|
"""
|
216
115
|
Public. Gets registered triggers
|
217
116
|
:return: registered in router triggers as list[str]
|
218
117
|
"""
|
219
118
|
all_triggers: list[str] = []
|
220
|
-
for command_handler in self.
|
221
|
-
all_triggers.append(command_handler.
|
119
|
+
for command_handler in self.command_handlers:
|
120
|
+
all_triggers.append(command_handler.handled_command.trigger)
|
222
121
|
return all_triggers
|
223
122
|
|
224
|
-
|
123
|
+
@property
|
124
|
+
def aliases(self) -> list[str]:
|
225
125
|
"""
|
226
126
|
Public. Gets registered aliases
|
227
127
|
:return: registered in router aliases as list[str]
|
228
128
|
"""
|
229
129
|
all_aliases: list[str] = []
|
230
|
-
for command_handler in self.
|
231
|
-
if command_handler.
|
232
|
-
all_aliases.extend(command_handler.
|
130
|
+
for command_handler in self.command_handlers:
|
131
|
+
if command_handler.handled_command.aliases:
|
132
|
+
all_aliases.extend(command_handler.handled_command.aliases)
|
233
133
|
return all_aliases
|
234
134
|
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
:
|
239
|
-
|
240
|
-
|
135
|
+
|
136
|
+
class CommandDecorator:
|
137
|
+
def __init__(self, router_instance: Router, command: Command):
|
138
|
+
self.router: Router = router_instance
|
139
|
+
self.command: Command = command
|
140
|
+
|
141
|
+
def __call__(self, handler_func: Callable[..., None]) -> Callable[..., None]:
|
142
|
+
_validate_func_args(handler_func)
|
143
|
+
self.router.command_handlers.add_handler(
|
144
|
+
CommandHandler(handler_func, self.command)
|
145
|
+
)
|
146
|
+
return handler_func
|
147
|
+
|
148
|
+
|
149
|
+
def _structuring_input_flags(
|
150
|
+
handled_command: Command, input_flags: InputFlags
|
151
|
+
) -> Response:
|
152
|
+
"""
|
153
|
+
Private. Validates flags of input command
|
154
|
+
:param handled_command: entity of the handled command
|
155
|
+
:param input_flags:
|
156
|
+
:return: entity of response as Response
|
157
|
+
"""
|
158
|
+
invalid_value_flags, undefined_flags = False, False
|
159
|
+
|
160
|
+
for flag in input_flags:
|
161
|
+
flag_status: ValidationStatus = handled_command.validate_input_flag(flag)
|
162
|
+
flag.status = flag_status
|
163
|
+
if flag_status == ValidationStatus.INVALID:
|
164
|
+
invalid_value_flags = True
|
165
|
+
elif flag_status == ValidationStatus.UNDEFINED:
|
166
|
+
undefined_flags = True
|
167
|
+
|
168
|
+
status = ResponseStatus.from_flags(
|
169
|
+
has_invalid_value_flags=invalid_value_flags, has_undefined_flags=undefined_flags
|
170
|
+
)
|
171
|
+
|
172
|
+
return Response(status=status, input_flags=input_flags)
|
173
|
+
|
174
|
+
|
175
|
+
def _validate_func_args(func: Callable[..., None]) -> None:
|
176
|
+
"""
|
177
|
+
Private. Validates the arguments of the handler
|
178
|
+
:param func: entity of the handler func
|
179
|
+
:return: None if func is valid else raise exception
|
180
|
+
"""
|
181
|
+
transferred_args = getfullargspec(func).args
|
182
|
+
if len(transferred_args) == 0:
|
183
|
+
raise RequiredArgumentNotPassedException()
|
184
|
+
|
185
|
+
response_arg: str = transferred_args[0]
|
186
|
+
func_annotations: dict[str, None] = get_annotations(func)
|
187
|
+
|
188
|
+
response_arg_annotation = func_annotations.get(response_arg)
|
189
|
+
|
190
|
+
if response_arg_annotation is not None:
|
191
|
+
if response_arg_annotation is not Response:
|
192
|
+
source_line: int = getsourcelines(func)[1]
|
193
|
+
Console().print(
|
194
|
+
f'\nFile "{getsourcefile(func)}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint '
|
195
|
+
+ f"of argument([green]{response_arg}[/green]) passed to the handler must be [/i][bold blue]{Response}[/bold blue],"
|
196
|
+
+ f" [i]but[/i] [bold blue]{response_arg_annotation}[/bold blue] [i]is specified[/i]",
|
197
|
+
highlight=False,
|
198
|
+
)
|
199
|
+
|
200
|
+
|
201
|
+
def _validate_command(command: Command) -> None:
|
202
|
+
"""
|
203
|
+
Private. Validates the command registered in handler
|
204
|
+
:param command: validated command
|
205
|
+
:return: None if command is valid else raise exception
|
206
|
+
"""
|
207
|
+
command_name: str = command.trigger
|
208
|
+
if command_name.find(" ") != -1:
|
209
|
+
raise TriggerContainSpacesException()
|
210
|
+
flags: Flags = command.registered_flags
|
211
|
+
flags_name: list[str] = [flag.string_entity.lower() for flag in flags]
|
212
|
+
if len(set(flags_name)) < len(flags_name):
|
213
|
+
raise RepeatedFlagNameException()
|
argenta/router/exceptions.py
CHANGED
@@ -1,27 +1,23 @@
|
|
1
|
+
from typing import override
|
2
|
+
|
3
|
+
|
1
4
|
class RepeatedFlagNameException(Exception):
|
2
5
|
"""
|
3
6
|
Private. Raised when a repeated flag name is registered
|
4
7
|
"""
|
5
8
|
|
6
|
-
|
9
|
+
@override
|
10
|
+
def __str__(self) -> str:
|
7
11
|
return "Repeated registered flag names in register command"
|
8
12
|
|
9
13
|
|
10
|
-
class TooManyTransferredArgsException(Exception):
|
11
|
-
"""
|
12
|
-
Private. Raised when too many arguments are passed
|
13
|
-
"""
|
14
|
-
|
15
|
-
def __str__(self):
|
16
|
-
return "Too many transferred arguments"
|
17
|
-
|
18
|
-
|
19
14
|
class RequiredArgumentNotPassedException(Exception):
|
20
15
|
"""
|
21
16
|
Private. Raised when a required argument is not passed
|
22
17
|
"""
|
23
18
|
|
24
|
-
|
19
|
+
@override
|
20
|
+
def __str__(self) -> str:
|
25
21
|
return "Required argument not passed"
|
26
22
|
|
27
23
|
|
@@ -30,5 +26,6 @@ class TriggerContainSpacesException(Exception):
|
|
30
26
|
Private. Raised when there is a space in the trigger being registered
|
31
27
|
"""
|
32
28
|
|
33
|
-
|
29
|
+
@override
|
30
|
+
def __str__(self) -> str:
|
34
31
|
return "Command trigger cannot contain spaces"
|
@@ -1,13 +1,14 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: argenta
|
3
|
-
Version: 1.1.
|
3
|
+
Version: 1.1.2
|
4
4
|
Summary: Python library for building modular CLI applications
|
5
5
|
Author-email: kolo <kolo.is.main@gmail.com>
|
6
6
|
License: MIT
|
7
7
|
License-File: LICENSE
|
8
|
-
Requires-Python: >=3.
|
8
|
+
Requires-Python: >=3.11
|
9
9
|
Requires-Dist: art<7.0,>=6.4
|
10
|
-
Requires-Dist:
|
10
|
+
Requires-Dist: dishka>=1.7.2
|
11
|
+
Requires-Dist: pyreadline3>=3.5.4; sys_platform == 'win32'
|
11
12
|
Requires-Dist: rich<15.0.0,>=14.0.0
|
12
13
|
Description-Content-Type: text/markdown
|
13
14
|
|
@@ -15,9 +16,15 @@ Description-Content-Type: text/markdown
|
|
15
16
|
|
16
17
|
### Library for creating modular CLI applications
|
17
18
|
|
18
|
-
#### RU - [README.ru.md](https://github.com/koloideal/Argenta/blob/
|
19
|
+
#### RU - [README.ru.md](https://github.com/koloideal/Argenta/blob/main/README.ru.md) • DE - [README.de.md](https://github.com/koloideal/Argenta/blob/main/README.de.md)
|
19
20
|
|
20
|
-
|
21
|
+
---
|
22
|
+
|
23
|
+
Argenta allows you to encapsulate CLI functionality in isolated, abstracted environments. Eg: you are creating a utility similar to the Metasploit Framework, where the user first logs into a specific scope (for example, selects a module to scan), and then gets access to a set of commands specific only to that context. Argenta provides a simple and concise way to build such an architecture.
|
24
|
+
|
25
|
+
---
|
26
|
+
|
27
|
+

|
21
28
|
|
22
29
|
---
|
23
30
|
|