argenta 0.4.9__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {argenta-0.4.9 → argenta-0.5.0}/PKG-INFO +33 -13
- {argenta-0.4.9 → argenta-0.5.0}/README.md +32 -12
- argenta-0.5.0/argenta/app/__init__.py +3 -0
- argenta-0.5.0/argenta/app/autocompleter/__init__.py +4 -0
- argenta-0.5.0/argenta/app/autocompleter/entity.py +47 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/app/defaults.py +1 -0
- argenta-0.5.0/argenta/app/exceptions.py +10 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/app/models.py +130 -99
- argenta-0.5.0/argenta/command/__init__.py +3 -0
- argenta-0.5.0/argenta/command/flag/__init__.py +4 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/command/models.py +7 -2
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/command_handler/entity.py +6 -6
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/entity.py +44 -21
- {argenta-0.4.9 → argenta-0.5.0}/pyproject.toml +1 -1
- argenta-0.4.9/argenta/app/__init__.py +0 -3
- argenta-0.4.9/argenta/app/exceptions.py +0 -25
- argenta-0.4.9/argenta/command/__init__.py +0 -3
- argenta-0.4.9/argenta/command/flag/__init__.py +0 -4
- {argenta-0.4.9 → argenta-0.5.0}/LICENSE +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/__init__.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/app/dividing_line/__init__.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/app/dividing_line/models.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/app/registered_routers/__init__.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/app/registered_routers/entity.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/command/exceptions.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/command/flag/defaults.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/command/flag/models.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/__init__.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/command_handler/__init__.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/command_handlers/__init__.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/command_handlers/entity.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/defaults.py +0 -0
- {argenta-0.4.9 → argenta-0.5.0}/argenta/router/exceptions.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: argenta
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.5.0
|
4
4
|
Summary: Python library for creating TUI
|
5
5
|
License: MIT
|
6
6
|
Author: kolo
|
@@ -22,7 +22,7 @@ Description-Content-Type: text/markdown
|
|
22
22
|
## Описание
|
23
23
|
**Argenta** — Python library for creating TUI
|
24
24
|
|
25
|
-

|
26
26
|
Пример внешнего вида TUI, написанного с помощью Argenta
|
27
27
|
|
28
28
|
---
|
@@ -112,16 +112,16 @@ def handler_with_flags(flags: InputFlags):
|
|
112
112
|
|
113
113
|
### Конструктор
|
114
114
|
```python
|
115
|
-
App(prompt: str = 'What do you want to do?\n',
|
116
|
-
initial_message: str = '
|
117
|
-
farewell_message: str = '
|
118
|
-
exit_command:
|
119
|
-
|
120
|
-
system_points_title: str = 'System points:',
|
115
|
+
App(prompt: str = '[italic dim bold]What do you want to do?\n',
|
116
|
+
initial_message: str = '\nArgenta\n',
|
117
|
+
farewell_message: str = '\nSee you\n',
|
118
|
+
exit_command: Command = Command('Q', 'Exit command'),
|
119
|
+
system_points_title: str | None = 'System points:',
|
121
120
|
ignore_command_register: bool = True,
|
122
121
|
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
|
123
122
|
repeat_command_groups: bool = True,
|
124
|
-
|
123
|
+
override_system_messages: bool = False,
|
124
|
+
autocompleter: AutoCompleter = AutoCompleter(),
|
125
125
|
print_func: Callable[[str], None] = Console().print)
|
126
126
|
```
|
127
127
|
**Аргументы:**
|
@@ -129,13 +129,13 @@ App(prompt: str = 'What do you want to do?\n',
|
|
129
129
|
- `prompt` (`str`): Сообщение перед вводом команды.
|
130
130
|
- `initial_message` (`str`): Приветственное сообщение при запуске.
|
131
131
|
- `farewell_message` (`str`): Сообщение при выходе.
|
132
|
-
- `exit_command` (`
|
133
|
-
- `exit_command_description` (`str`): Описание команды выхода.
|
132
|
+
- `exit_command` (`Command`): Сущность команды, которая будет отвечать за завершение работы.
|
134
133
|
- `system_points_title` (`str`): Заголовок перед списком системных команд.
|
135
134
|
- `ignore_command_register` (`bool`): Игнорировать регистр всех команд.
|
136
135
|
- `dividing_line` (`StaticDividingLine | DynamicDividingLine`): Разделительная строка.
|
137
136
|
- `repeat_command_groups` (`bool`): Повторять описание команд перед вводом.
|
138
|
-
- `
|
137
|
+
- `override_system_messages` (`bool`): Переопределить ли дефолтное оформление сообщений ([подробнее см.](#override_defaults))
|
138
|
+
- `autocompleter` (`AutoCompleter`): Сущность автодополнителя ввода.
|
139
139
|
- `print_func` (`Callable[[str], None]`): Функция вывода текста в терминал.
|
140
140
|
|
141
141
|
---
|
@@ -260,6 +260,26 @@ App(prompt: str = 'What do you want to do?\n',
|
|
260
260
|
|
261
261
|
---
|
262
262
|
|
263
|
+
## *class* :: `AutoCompleter`
|
264
|
+
Класс, экземпляр которого представляет собой автодополнитель ввода
|
265
|
+
|
266
|
+
### Конструктор
|
267
|
+
```python
|
268
|
+
AutoCompleter(history_filename: str = False,
|
269
|
+
autocomplete_button: str = 'tab')
|
270
|
+
```
|
271
|
+
|
272
|
+
**Аргументы:**
|
273
|
+
- **name : mean**
|
274
|
+
- `history_filename` (`str` | `False`): Путь к файлу, который будет являться или является
|
275
|
+
историй пользовательского ввода, в последующем эти команды будут автодополняться, файл
|
276
|
+
может не существовать при инициализации, тогда он будет создан, при значении аргумента `False`
|
277
|
+
история пользовательского ввода будет существовать только в пределах сессии и не сохраняться в файл
|
278
|
+
- `autocomplete_button` (`str`): Строковое обозначение кнопки на клавиатуре, которая будет
|
279
|
+
использоваться для автодополнения при вводе, по умолчанию `tab`
|
280
|
+
|
281
|
+
---
|
282
|
+
|
263
283
|
## *class* :: `StaticDivideLine`
|
264
284
|
Класс, экземпляр которого представляет собой строковый разделитель фиксированной длины
|
265
285
|
|
@@ -295,7 +315,7 @@ DinamicDivideLine(unit_part: str = '-')
|
|
295
315
|
|
296
316
|
### Конструктор
|
297
317
|
```python
|
298
|
-
Router(title: str
|
318
|
+
Router(title: str | None = None,
|
299
319
|
name: str = 'Default')
|
300
320
|
```
|
301
321
|
|
@@ -5,7 +5,7 @@
|
|
5
5
|
## Описание
|
6
6
|
**Argenta** — Python library for creating TUI
|
7
7
|
|
8
|
-

|
9
9
|
Пример внешнего вида TUI, написанного с помощью Argenta
|
10
10
|
|
11
11
|
---
|
@@ -95,16 +95,16 @@ def handler_with_flags(flags: InputFlags):
|
|
95
95
|
|
96
96
|
### Конструктор
|
97
97
|
```python
|
98
|
-
App(prompt: str = 'What do you want to do?\n',
|
99
|
-
initial_message: str = '
|
100
|
-
farewell_message: str = '
|
101
|
-
exit_command:
|
102
|
-
|
103
|
-
system_points_title: str = 'System points:',
|
98
|
+
App(prompt: str = '[italic dim bold]What do you want to do?\n',
|
99
|
+
initial_message: str = '\nArgenta\n',
|
100
|
+
farewell_message: str = '\nSee you\n',
|
101
|
+
exit_command: Command = Command('Q', 'Exit command'),
|
102
|
+
system_points_title: str | None = 'System points:',
|
104
103
|
ignore_command_register: bool = True,
|
105
104
|
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
|
106
105
|
repeat_command_groups: bool = True,
|
107
|
-
|
106
|
+
override_system_messages: bool = False,
|
107
|
+
autocompleter: AutoCompleter = AutoCompleter(),
|
108
108
|
print_func: Callable[[str], None] = Console().print)
|
109
109
|
```
|
110
110
|
**Аргументы:**
|
@@ -112,13 +112,13 @@ App(prompt: str = 'What do you want to do?\n',
|
|
112
112
|
- `prompt` (`str`): Сообщение перед вводом команды.
|
113
113
|
- `initial_message` (`str`): Приветственное сообщение при запуске.
|
114
114
|
- `farewell_message` (`str`): Сообщение при выходе.
|
115
|
-
- `exit_command` (`
|
116
|
-
- `exit_command_description` (`str`): Описание команды выхода.
|
115
|
+
- `exit_command` (`Command`): Сущность команды, которая будет отвечать за завершение работы.
|
117
116
|
- `system_points_title` (`str`): Заголовок перед списком системных команд.
|
118
117
|
- `ignore_command_register` (`bool`): Игнорировать регистр всех команд.
|
119
118
|
- `dividing_line` (`StaticDividingLine | DynamicDividingLine`): Разделительная строка.
|
120
119
|
- `repeat_command_groups` (`bool`): Повторять описание команд перед вводом.
|
121
|
-
- `
|
120
|
+
- `override_system_messages` (`bool`): Переопределить ли дефолтное оформление сообщений ([подробнее см.](#override_defaults))
|
121
|
+
- `autocompleter` (`AutoCompleter`): Сущность автодополнителя ввода.
|
122
122
|
- `print_func` (`Callable[[str], None]`): Функция вывода текста в терминал.
|
123
123
|
|
124
124
|
---
|
@@ -243,6 +243,26 @@ App(prompt: str = 'What do you want to do?\n',
|
|
243
243
|
|
244
244
|
---
|
245
245
|
|
246
|
+
## *class* :: `AutoCompleter`
|
247
|
+
Класс, экземпляр которого представляет собой автодополнитель ввода
|
248
|
+
|
249
|
+
### Конструктор
|
250
|
+
```python
|
251
|
+
AutoCompleter(history_filename: str = False,
|
252
|
+
autocomplete_button: str = 'tab')
|
253
|
+
```
|
254
|
+
|
255
|
+
**Аргументы:**
|
256
|
+
- **name : mean**
|
257
|
+
- `history_filename` (`str` | `False`): Путь к файлу, который будет являться или является
|
258
|
+
историй пользовательского ввода, в последующем эти команды будут автодополняться, файл
|
259
|
+
может не существовать при инициализации, тогда он будет создан, при значении аргумента `False`
|
260
|
+
история пользовательского ввода будет существовать только в пределах сессии и не сохраняться в файл
|
261
|
+
- `autocomplete_button` (`str`): Строковое обозначение кнопки на клавиатуре, которая будет
|
262
|
+
использоваться для автодополнения при вводе, по умолчанию `tab`
|
263
|
+
|
264
|
+
---
|
265
|
+
|
246
266
|
## *class* :: `StaticDivideLine`
|
247
267
|
Класс, экземпляр которого представляет собой строковый разделитель фиксированной длины
|
248
268
|
|
@@ -278,7 +298,7 @@ DinamicDivideLine(unit_part: str = '-')
|
|
278
298
|
|
279
299
|
### Конструктор
|
280
300
|
```python
|
281
|
-
Router(title: str
|
301
|
+
Router(title: str | None = None,
|
282
302
|
name: str = 'Default')
|
283
303
|
```
|
284
304
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import os
|
2
|
+
import readline
|
3
|
+
|
4
|
+
|
5
|
+
class AutoCompleter:
|
6
|
+
def __init__(self, history_filename: str = False, autocomplete_button: str = 'tab'):
|
7
|
+
self.history_filename = history_filename
|
8
|
+
self.autocomplete_button = autocomplete_button
|
9
|
+
self.matches = []
|
10
|
+
|
11
|
+
def complete(self, text, state):
|
12
|
+
matches = sorted(cmd for cmd in self.get_history_items() if cmd.startswith(text))
|
13
|
+
if len(matches) > 1:
|
14
|
+
common_prefix = matches[0]
|
15
|
+
for match in matches[1:]:
|
16
|
+
i = 0
|
17
|
+
while i < len(common_prefix) and i < len(match) and common_prefix[i] == match[i]:
|
18
|
+
i += 1
|
19
|
+
common_prefix = common_prefix[:i]
|
20
|
+
if state == 0:
|
21
|
+
readline.insert_text(common_prefix[len(text):])
|
22
|
+
readline.redisplay()
|
23
|
+
return None
|
24
|
+
elif len(matches) == 1:
|
25
|
+
return matches[0] if state == 0 else None
|
26
|
+
else:
|
27
|
+
return None
|
28
|
+
|
29
|
+
def initial_setup(self, all_commands: list[str]):
|
30
|
+
if self.history_filename:
|
31
|
+
if os.path.exists(self.history_filename):
|
32
|
+
readline.read_history_file(self.history_filename)
|
33
|
+
else:
|
34
|
+
for line in all_commands:
|
35
|
+
readline.add_history(line)
|
36
|
+
|
37
|
+
readline.set_completer(self.complete)
|
38
|
+
readline.set_completer_delims(readline.get_completer_delims().replace(' ', ''))
|
39
|
+
readline.parse_and_bind(f'{self.autocomplete_button}: complete')
|
40
|
+
|
41
|
+
def exit_setup(self):
|
42
|
+
if self.history_filename:
|
43
|
+
readline.write_history_file(self.history_filename)
|
44
|
+
|
45
|
+
@staticmethod
|
46
|
+
def get_history_items():
|
47
|
+
return [readline.get_history_item(i) for i in range(1, readline.get_current_history_length() + 1)]
|
@@ -5,4 +5,5 @@ from dataclasses import dataclass
|
|
5
5
|
class PredeterminedMessages:
|
6
6
|
USAGE = '[b dim]Usage[/b dim]: [i]<command> <[green]flags[/green]>[/i]'
|
7
7
|
HELP = '[b dim]Help[/b dim]: [i]<command>[/i] [b red]--help[/b red]'
|
8
|
+
AUTOCOMPLETE = '[b dim]Autocomplete[/b dim]: [i]<part>[/i] [bold]<tab>'
|
8
9
|
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class NoRegisteredRoutersException(Exception):
|
2
|
+
def __str__(self):
|
3
|
+
return "No Registered Router Found"
|
4
|
+
|
5
|
+
|
6
|
+
class NoRegisteredHandlersException(Exception):
|
7
|
+
def __init__(self, router_name):
|
8
|
+
self.router_name = router_name
|
9
|
+
def __str__(self):
|
10
|
+
return f"No Registered Handlers Found For '{self.router_name}'"
|
@@ -1,5 +1,6 @@
|
|
1
1
|
from typing import Callable
|
2
2
|
from rich.console import Console
|
3
|
+
from rich.markup import escape
|
3
4
|
from art import text2art
|
4
5
|
from contextlib import redirect_stdout
|
5
6
|
import io
|
@@ -8,82 +9,118 @@ import re
|
|
8
9
|
from argenta.command.models import Command, InputCommand
|
9
10
|
from argenta.router import Router
|
10
11
|
from argenta.router.defaults import system_router
|
12
|
+
from argenta.app.autocompleter import AutoCompleter
|
11
13
|
from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine
|
12
14
|
from argenta.command.exceptions import (UnprocessedInputFlagException,
|
13
15
|
RepeatedInputFlagsException,
|
14
16
|
EmptyInputCommandException,
|
15
17
|
BaseInputCommandException)
|
16
|
-
from argenta.app.exceptions import (
|
17
|
-
InvalidDescriptionMessagePatternException,
|
18
|
-
NoRegisteredRoutersException,
|
18
|
+
from argenta.app.exceptions import (NoRegisteredRoutersException,
|
19
19
|
NoRegisteredHandlersException)
|
20
20
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
21
21
|
|
22
22
|
|
23
23
|
|
24
|
-
class
|
24
|
+
class AppInit:
|
25
25
|
def __init__(self,
|
26
26
|
prompt: str = '[italic dim bold]What do you want to do?\n',
|
27
27
|
initial_message: str = '\nArgenta\n',
|
28
28
|
farewell_message: str = '\nSee you\n',
|
29
|
-
exit_command:
|
30
|
-
|
31
|
-
system_points_title: str = 'System points:',
|
29
|
+
exit_command: Command = Command('Q', 'Exit command'),
|
30
|
+
system_points_title: str | None = 'System points:',
|
32
31
|
ignore_command_register: bool = True,
|
33
32
|
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
|
34
33
|
repeat_command_groups: bool = True,
|
35
|
-
|
34
|
+
override_system_messages: bool = False,
|
35
|
+
autocompleter: AutoCompleter = AutoCompleter(),
|
36
36
|
print_func: Callable[[str], None] = Console().print) -> None:
|
37
37
|
self._prompt = prompt
|
38
38
|
self._print_func = print_func
|
39
39
|
self._exit_command = exit_command
|
40
|
-
self._exit_command_description = exit_command_description
|
41
40
|
self._system_points_title = system_points_title
|
42
41
|
self._dividing_line = dividing_line
|
43
42
|
self._ignore_command_register = ignore_command_register
|
44
43
|
self._repeat_command_groups_description = repeat_command_groups
|
45
|
-
self.
|
44
|
+
self._override_system_messages = override_system_messages
|
45
|
+
self._autocompleter = autocompleter
|
46
46
|
|
47
|
-
self.
|
48
|
-
self.
|
47
|
+
self._farewell_message = farewell_message
|
48
|
+
self._initial_message = initial_message
|
49
49
|
|
50
|
-
|
50
|
+
|
51
|
+
self._description_message_gen: Callable[[str, str], str] = lambda command, description: f'[bold red]{escape('['+command+']')}[/bold red] [blue dim]*=*=*[/blue dim] [bold yellow italic]{escape(description)}'
|
51
52
|
self._registered_routers: RegisteredRouters = RegisteredRouters()
|
52
53
|
self._messages_on_startup = []
|
53
54
|
|
54
|
-
self.
|
55
|
-
self.
|
56
|
-
self.
|
57
|
-
self.
|
58
|
-
self.
|
55
|
+
self._invalid_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'[red bold]Incorrect flag syntax: {escape(raw_command)}')
|
56
|
+
self._repeated_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'[red bold]Repeated input flags: {escape(raw_command)}')
|
57
|
+
self._empty_input_command_handler: Callable[[], None] = lambda: print_func('[red bold]Empty input command')
|
58
|
+
self._unknown_command_handler: Callable[[InputCommand], None] = lambda command: print_func(f"[red bold]Unknown command: {escape(command.get_trigger())}")
|
59
|
+
self._exit_command_handler: Callable[[], None] = lambda: print_func(self._farewell_message)
|
59
60
|
|
60
|
-
self._setup_default_view()
|
61
61
|
|
62
|
+
class AppSetters(AppInit):
|
63
|
+
def set_description_message_pattern(self, pattern: Callable[[str, str], str]) -> None:
|
64
|
+
self._description_message_gen: Callable[[str, str], str] = pattern
|
62
65
|
|
63
|
-
def _setup_default_view(self):
|
64
|
-
if not self._full_override_system_messages:
|
65
|
-
self.initial_message = f'\n[bold red]{text2art(self.initial_message, font='tarty1')}\n\n'
|
66
|
-
self.farewell_message = (f'[bold red]\n{text2art(f'\n{self.farewell_message}\n', font='chanky')}[/bold red]\n'
|
67
|
-
f'[red i]github.com/koloideal/Argenta[/red i] | [red bold i]made by kolo[/red bold i]\n')
|
68
66
|
|
67
|
+
def set_invalid_input_flags_handler(self, handler: Callable[[str], None]) -> None:
|
68
|
+
self._invalid_input_flags_handler = handler
|
69
69
|
|
70
|
-
def _validate_number_of_routers(self) -> None:
|
71
|
-
if not self._registered_routers:
|
72
|
-
raise NoRegisteredRoutersException()
|
73
70
|
|
71
|
+
def set_repeated_input_flags_handler(self, handler: Callable[[str], None]) -> None:
|
72
|
+
self._repeated_input_flags_handler = handler
|
73
|
+
|
74
|
+
|
75
|
+
def set_unknown_command_handler(self, handler: Callable[[str], None]) -> None:
|
76
|
+
self._unknown_command_handler = handler
|
74
77
|
|
75
|
-
def _validate_included_routers(self) -> None:
|
76
|
-
for router in self._registered_routers:
|
77
|
-
if not router.get_command_handlers():
|
78
|
-
raise NoRegisteredHandlersException(router.get_name())
|
79
78
|
|
79
|
+
def set_empty_command_handler(self, handler: Callable[[], None]) -> None:
|
80
|
+
self._empty_input_command_handler = handler
|
80
81
|
|
82
|
+
|
83
|
+
def set_exit_command_handler(self, handler: Callable[[], None]) -> None:
|
84
|
+
self._exit_command_handler = handler
|
85
|
+
|
86
|
+
|
87
|
+
class AppPrinters(AppInit):
|
88
|
+
def _print_command_group_description(self):
|
89
|
+
for registered_router in self._registered_routers:
|
90
|
+
if registered_router.get_title():
|
91
|
+
self._print_func(registered_router.get_title())
|
92
|
+
for command_handler in registered_router.get_command_handlers():
|
93
|
+
self._print_func(self._description_message_gen(
|
94
|
+
command_handler.get_handled_command().get_trigger(),
|
95
|
+
command_handler.get_handled_command().get_description()))
|
96
|
+
self._print_func('')
|
97
|
+
|
98
|
+
|
99
|
+
def _print_framed_text_with_dynamic_line(self, text: str):
|
100
|
+
clear_text = re.sub(r'\u001b\[[0-9;]*m', '', text)
|
101
|
+
max_length_line = max([len(line) for line in clear_text.split('\n')])
|
102
|
+
max_length_line = max_length_line if 10 <= max_length_line <= 80 else 80 if max_length_line > 80 else 10
|
103
|
+
self._print_func(self._dividing_line.get_full_line(max_length_line))
|
104
|
+
print(text.strip('\n'))
|
105
|
+
self._print_func(self._dividing_line.get_full_line(max_length_line))
|
106
|
+
|
107
|
+
|
108
|
+
def _print_framed_text(self, text: str):
|
109
|
+
if isinstance(self._dividing_line, StaticDividingLine):
|
110
|
+
self._print_func(self._dividing_line.get_full_line())
|
111
|
+
self._print_func(text)
|
112
|
+
self._print_func(self._dividing_line.get_full_line())
|
113
|
+
elif isinstance(self._dividing_line, DynamicDividingLine):
|
114
|
+
self._print_framed_text_with_dynamic_line(text)
|
115
|
+
|
116
|
+
|
117
|
+
class AppNonStandardHandlers(AppPrinters):
|
81
118
|
def _is_exit_command(self, command: InputCommand):
|
82
|
-
if command.get_trigger().lower() == self._exit_command.lower():
|
119
|
+
if command.get_trigger().lower() == self._exit_command.get_trigger().lower():
|
83
120
|
if self._ignore_command_register:
|
84
121
|
system_router.input_command_handler(command)
|
85
122
|
return True
|
86
|
-
elif command.get_trigger() == self._exit_command:
|
123
|
+
elif command.get_trigger() == self._exit_command.get_trigger():
|
87
124
|
system_router.input_command_handler(command)
|
88
125
|
return True
|
89
126
|
return False
|
@@ -93,66 +130,94 @@ class BaseApp:
|
|
93
130
|
for router_entity in self._registered_routers:
|
94
131
|
for command_handler in router_entity.get_command_handlers():
|
95
132
|
handled_command_trigger = command_handler.get_handled_command().get_trigger()
|
133
|
+
handled_command_aliases = command_handler.get_handled_command().get_aliases()
|
96
134
|
if handled_command_trigger.lower() == command.get_trigger().lower() and self._ignore_command_register:
|
97
135
|
return False
|
98
136
|
elif handled_command_trigger == command.get_trigger():
|
99
137
|
return False
|
138
|
+
elif handled_command_aliases:
|
139
|
+
if (command.get_trigger().lower() in [x.lower() for x in handled_command_aliases]) and self._ignore_command_register:
|
140
|
+
return False
|
141
|
+
elif command.get_trigger() in handled_command_trigger:
|
142
|
+
return False
|
100
143
|
if isinstance(self._dividing_line, StaticDividingLine):
|
101
144
|
self._print_func(self._dividing_line.get_full_line())
|
102
|
-
self.
|
145
|
+
self._unknown_command_handler(command)
|
103
146
|
self._print_func(self._dividing_line.get_full_line())
|
104
147
|
elif isinstance(self._dividing_line, DynamicDividingLine):
|
105
148
|
with redirect_stdout(io.StringIO()) as f:
|
106
|
-
self.
|
149
|
+
self._unknown_command_handler(command)
|
107
150
|
res: str = f.getvalue()
|
108
151
|
self._print_framed_text_with_dynamic_line(res)
|
109
152
|
return True
|
110
153
|
|
111
154
|
|
112
|
-
def _print_command_group_description(self):
|
113
|
-
for registered_router in self._registered_routers:
|
114
|
-
self._print_func(registered_router.get_title())
|
115
|
-
for command_handler in registered_router.get_command_handlers():
|
116
|
-
self._print_func(self._description_message_pattern.format(
|
117
|
-
command=command_handler.get_handled_command().get_trigger(),
|
118
|
-
description=command_handler.get_handled_command().get_description()))
|
119
|
-
self._print_func('')
|
120
|
-
|
121
|
-
|
122
155
|
def _error_handler(self, error: BaseInputCommandException, raw_command: str) -> None:
|
123
156
|
match error:
|
124
157
|
case UnprocessedInputFlagException():
|
125
|
-
self.
|
158
|
+
self._invalid_input_flags_handler(raw_command)
|
126
159
|
case RepeatedInputFlagsException():
|
127
|
-
self.
|
160
|
+
self._repeated_input_flags_handler(raw_command)
|
128
161
|
case EmptyInputCommandException():
|
129
|
-
self.
|
162
|
+
self._empty_input_command_handler()
|
130
163
|
|
131
164
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
self._print_func(self._dividing_line.get_full_line(max_length_line))
|
137
|
-
print(text.strip('\n'))
|
138
|
-
self._print_func(self._dividing_line.get_full_line(max_length_line))
|
165
|
+
class AppValidators(AppInit):
|
166
|
+
def _validate_number_of_routers(self) -> None:
|
167
|
+
if not self._registered_routers:
|
168
|
+
raise NoRegisteredRoutersException()
|
139
169
|
|
140
170
|
|
171
|
+
def _validate_included_routers(self) -> None:
|
172
|
+
for router in self._registered_routers:
|
173
|
+
if not router.get_command_handlers():
|
174
|
+
raise NoRegisteredHandlersException(router.get_name())
|
141
175
|
|
142
|
-
|
143
|
-
|
176
|
+
|
177
|
+
class AppSetups(AppValidators, AppPrinters):
|
178
|
+
def _setup_system_router(self):
|
179
|
+
system_router.set_title(self._system_points_title)
|
180
|
+
|
181
|
+
@system_router.command(self._exit_command)
|
182
|
+
def exit_command():
|
183
|
+
self._exit_command_handler()
|
184
|
+
|
185
|
+
if system_router not in self._registered_routers.get_registered_routers():
|
186
|
+
system_router.set_ignore_command_register(self._ignore_command_register)
|
187
|
+
self._registered_routers.add_registered_router(system_router)
|
188
|
+
|
189
|
+
def _setup_default_view(self):
|
190
|
+
if not self._override_system_messages:
|
191
|
+
self._initial_message = f'\n[bold red]{text2art(self._initial_message, font='tarty1')}\n\n'
|
192
|
+
self._farewell_message = (
|
193
|
+
f'[bold red]\n{text2art(f'\n{self._farewell_message}\n', font='chanky')}[/bold red]\n'
|
194
|
+
f'[red i]github.com/koloideal/Argenta[/red i] | [red bold i]made by kolo[/red bold i]\n')
|
195
|
+
|
196
|
+
def _pre_cycle_setup(self):
|
197
|
+
self._setup_default_view()
|
144
198
|
self._setup_system_router()
|
145
199
|
self._validate_number_of_routers()
|
146
200
|
self._validate_included_routers()
|
147
201
|
|
148
|
-
|
202
|
+
all_triggers: list[str] = []
|
203
|
+
for router_entity in self._registered_routers:
|
204
|
+
all_triggers.extend(router_entity.get_triggers())
|
205
|
+
all_triggers.extend(router_entity.get_aliases())
|
206
|
+
self._autocompleter.initial_setup(all_triggers)
|
207
|
+
|
208
|
+
self._print_func(self._initial_message)
|
149
209
|
|
150
210
|
for message in self._messages_on_startup:
|
151
211
|
self._print_func(message)
|
212
|
+
print('\n\n')
|
152
213
|
|
153
214
|
if not self._repeat_command_groups_description:
|
154
215
|
self._print_command_group_description()
|
155
216
|
|
217
|
+
|
218
|
+
class App(AppSetters, AppNonStandardHandlers, AppSetups):
|
219
|
+
def start_polling(self) -> None:
|
220
|
+
self._pre_cycle_setup()
|
156
221
|
while True:
|
157
222
|
if self._repeat_command_groups_description:
|
158
223
|
self._print_command_group_description()
|
@@ -162,43 +227,30 @@ class App(BaseApp):
|
|
162
227
|
try:
|
163
228
|
input_command: InputCommand = InputCommand.parse(raw_command=raw_command)
|
164
229
|
except BaseInputCommandException as error:
|
165
|
-
|
166
|
-
self._print_func(self._dividing_line.get_full_line())
|
230
|
+
with redirect_stdout(io.StringIO()) as f:
|
167
231
|
self._error_handler(error, raw_command)
|
168
|
-
|
169
|
-
|
170
|
-
with redirect_stdout(io.StringIO()) as f:
|
171
|
-
self._error_handler(error, raw_command)
|
172
|
-
res: str = f.getvalue()
|
173
|
-
self._print_framed_text_with_dynamic_line(res)
|
232
|
+
res: str = f.getvalue()
|
233
|
+
self._print_framed_text(res)
|
174
234
|
continue
|
175
235
|
|
176
236
|
if self._is_exit_command(input_command):
|
237
|
+
self._autocompleter.exit_setup()
|
177
238
|
return
|
178
239
|
|
179
240
|
if self._is_unknown_command(input_command):
|
180
241
|
continue
|
181
242
|
|
182
|
-
|
183
|
-
self._print_func(self._dividing_line.get_full_line())
|
243
|
+
with redirect_stdout(io.StringIO()) as f:
|
184
244
|
for registered_router in self._registered_routers:
|
185
245
|
registered_router.input_command_handler(input_command)
|
186
|
-
|
187
|
-
|
188
|
-
with redirect_stdout(io.StringIO()) as f:
|
189
|
-
for registered_router in self._registered_routers:
|
190
|
-
registered_router.input_command_handler(input_command)
|
191
|
-
res: str = f.getvalue()
|
192
|
-
self._print_framed_text_with_dynamic_line(res)
|
246
|
+
res: str = f.getvalue()
|
247
|
+
self._print_framed_text(res)
|
193
248
|
|
194
249
|
if not self._repeat_command_groups_description:
|
195
250
|
self._print_func(self._prompt)
|
196
251
|
|
197
252
|
|
198
253
|
def include_router(self, router: Router) -> None:
|
199
|
-
if not isinstance(router, Router):
|
200
|
-
raise InvalidRouterInstanceException()
|
201
|
-
|
202
254
|
router.set_ignore_command_register(self._ignore_command_register)
|
203
255
|
self._registered_routers.add_registered_router(router)
|
204
256
|
|
@@ -211,24 +263,3 @@ class App(BaseApp):
|
|
211
263
|
def add_message_on_startup(self, message: str) -> None:
|
212
264
|
self._messages_on_startup.append(message)
|
213
265
|
|
214
|
-
|
215
|
-
def set_description_message_pattern(self, pattern: str) -> None:
|
216
|
-
first_check = re.match(r'.*{command}.*', pattern)
|
217
|
-
second_check = re.match(r'.*{description}.*', pattern)
|
218
|
-
|
219
|
-
if bool(first_check) and bool(second_check):
|
220
|
-
self._description_message_pattern: str = pattern
|
221
|
-
else:
|
222
|
-
raise InvalidDescriptionMessagePatternException(pattern)
|
223
|
-
|
224
|
-
|
225
|
-
def _setup_system_router(self):
|
226
|
-
system_router.set_title(self._system_points_title)
|
227
|
-
@system_router.command(Command(self._exit_command, self._exit_command_description))
|
228
|
-
def exit_command():
|
229
|
-
self.exit_command_handler()
|
230
|
-
|
231
|
-
if system_router not in self._registered_routers.get_registered_routers():
|
232
|
-
self.include_router(system_router)
|
233
|
-
|
234
|
-
|
@@ -19,14 +19,19 @@ class BaseCommand:
|
|
19
19
|
class Command(BaseCommand):
|
20
20
|
def __init__(self, trigger: str,
|
21
21
|
description: str = None,
|
22
|
-
flags: Flag | Flags = None
|
22
|
+
flags: Flag | Flags = None,
|
23
|
+
aliases: list[str] = None):
|
23
24
|
super().__init__(trigger)
|
24
25
|
self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags()
|
25
|
-
self._description = f'
|
26
|
+
self._description = f'Description for "{self._trigger}" command' if not description else description
|
27
|
+
self._aliases = aliases
|
26
28
|
|
27
29
|
def get_registered_flags(self) -> Flags:
|
28
30
|
return self._registered_flags
|
29
31
|
|
32
|
+
def get_aliases(self) -> list[str] | None:
|
33
|
+
return self._aliases
|
34
|
+
|
30
35
|
def validate_input_flag(self, flag: InputFlag):
|
31
36
|
registered_flags: Flags | None = self.get_registered_flags()
|
32
37
|
if registered_flags:
|
@@ -5,17 +5,17 @@ from argenta.command.flag.models import InputFlags
|
|
5
5
|
|
6
6
|
class CommandHandler:
|
7
7
|
def __init__(self, handler: Callable[[], None] | Callable[[InputFlags], None], handled_command: Command):
|
8
|
-
self.
|
9
|
-
self.
|
8
|
+
self._handler = handler
|
9
|
+
self._handled_command = handled_command
|
10
10
|
|
11
11
|
def handling(self, input_flags: InputFlags = None):
|
12
12
|
if input_flags is not None:
|
13
|
-
self.
|
13
|
+
self._handler(input_flags)
|
14
14
|
else:
|
15
|
-
self.
|
15
|
+
self._handler()
|
16
16
|
|
17
17
|
def get_handler(self):
|
18
|
-
return self.
|
18
|
+
return self._handler
|
19
19
|
|
20
20
|
def get_handled_command(self):
|
21
|
-
return self.
|
21
|
+
return self._handled_command
|
@@ -7,15 +7,15 @@ from argenta.router.command_handlers.entity import CommandHandlers
|
|
7
7
|
from argenta.router.command_handler.entity import CommandHandler
|
8
8
|
from argenta.command.flag.models import Flag, Flags, InputFlags
|
9
9
|
from argenta.router.exceptions import (RepeatedFlagNameException,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
TooManyTransferredArgsException,
|
11
|
+
RequiredArgumentNotPassedException,
|
12
|
+
IncorrectNumberOfHandlerArgsException,
|
13
|
+
TriggerCannotContainSpacesException)
|
14
14
|
|
15
15
|
|
16
16
|
class Router:
|
17
17
|
def __init__(self,
|
18
|
-
title: str =
|
18
|
+
title: str = None,
|
19
19
|
name: str = 'Default'):
|
20
20
|
self._title = title
|
21
21
|
self._name = name
|
@@ -54,21 +54,29 @@ class Router:
|
|
54
54
|
for command_handler in self._command_handlers:
|
55
55
|
handle_command = command_handler.get_handled_command()
|
56
56
|
if input_command_name.lower() == handle_command.get_trigger().lower():
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
57
|
+
self._validate_input_command(input_command_flags, command_handler)
|
58
|
+
elif handle_command.get_aliases():
|
59
|
+
if input_command_name.lower() in handle_command.get_aliases():
|
60
|
+
self._validate_input_command(input_command_flags, command_handler)
|
61
|
+
|
62
|
+
|
63
|
+
def _validate_input_command(self, input_command_flags: InputFlags, command_handler: CommandHandler):
|
64
|
+
handle_command = command_handler.get_handled_command()
|
65
|
+
if handle_command.get_registered_flags().get_flags():
|
66
|
+
if input_command_flags.get_flags():
|
67
|
+
if self._validate_input_flags(handle_command, input_command_flags):
|
68
|
+
command_handler.handling(input_command_flags)
|
69
|
+
return
|
70
|
+
else:
|
71
|
+
command_handler.handling(input_command_flags)
|
72
|
+
return
|
73
|
+
else:
|
74
|
+
if input_command_flags.get_flags():
|
75
|
+
self._not_valid_flag_handler(input_command_flags[0])
|
76
|
+
return
|
77
|
+
else:
|
78
|
+
command_handler.handling()
|
79
|
+
return
|
72
80
|
|
73
81
|
|
74
82
|
def _validate_input_flags(self, handle_command: Command, input_flags: InputFlags):
|
@@ -110,6 +118,21 @@ class Router:
|
|
110
118
|
self._ignore_command_register = ignore_command_register
|
111
119
|
|
112
120
|
|
121
|
+
def get_triggers(self):
|
122
|
+
all_triggers: list[str] = []
|
123
|
+
for command_handler in self._command_handlers:
|
124
|
+
all_triggers.append(command_handler.get_handled_command().get_trigger())
|
125
|
+
return all_triggers
|
126
|
+
|
127
|
+
|
128
|
+
def get_aliases(self):
|
129
|
+
all_aliases: list[str] = []
|
130
|
+
for command_handler in self._command_handlers:
|
131
|
+
if command_handler.get_handled_command().get_aliases():
|
132
|
+
all_aliases.extend(command_handler.get_handled_command().get_aliases())
|
133
|
+
return all_aliases
|
134
|
+
|
135
|
+
|
113
136
|
def get_command_handlers(self) -> CommandHandlers:
|
114
137
|
return self._command_handlers
|
115
138
|
|
@@ -118,7 +141,7 @@ class Router:
|
|
118
141
|
return self._name
|
119
142
|
|
120
143
|
|
121
|
-
def get_title(self) -> str:
|
144
|
+
def get_title(self) -> str | None:
|
122
145
|
return self._title
|
123
146
|
|
124
147
|
|
@@ -1,25 +0,0 @@
|
|
1
|
-
class InvalidRouterInstanceException(Exception):
|
2
|
-
def __str__(self):
|
3
|
-
return "Invalid Router Instance"
|
4
|
-
|
5
|
-
|
6
|
-
class InvalidDescriptionMessagePatternException(Exception):
|
7
|
-
def __init__(self, pattern: str):
|
8
|
-
self.pattern = pattern
|
9
|
-
def __str__(self):
|
10
|
-
return ("Invalid Description Message Pattern\n"
|
11
|
-
"Correct pattern example: [{command}] *=*=* {description}\n"
|
12
|
-
"The pattern must contain two variables: `command` and `description` - description of the command\n"
|
13
|
-
f"Your pattern: {self.pattern}")
|
14
|
-
|
15
|
-
|
16
|
-
class NoRegisteredRoutersException(Exception):
|
17
|
-
def __str__(self):
|
18
|
-
return "No Registered Router Found"
|
19
|
-
|
20
|
-
|
21
|
-
class NoRegisteredHandlersException(Exception):
|
22
|
-
def __init__(self, router_name):
|
23
|
-
self.router_name = router_name
|
24
|
-
def __str__(self):
|
25
|
-
return f"No Registered Handlers Found For '{self.router_name}'"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|