mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 18:15:28 +03:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 87239f1501 | |||
| 48c117dd72 | |||
| 1d782f6213 | |||
| ddf396a644 | |||
| c10af04280 | |||
| 890d863391 | |||
| 29b184b2ed | |||
| 6d331a57c1 | |||
| b0eb1e3e6c | |||
| 2d088caaaf | |||
| b4b7d5442c | |||
| 684760121c | |||
| dfc3c472ce | |||
| 8a3f742636 |
@@ -4,6 +4,7 @@ argenta/router/__pycache__/
|
|||||||
argenta/app/__pycache__/
|
argenta/app/__pycache__/
|
||||||
argenta/__pycache__/
|
argenta/__pycache__/
|
||||||
dist
|
dist
|
||||||
|
poetry.lock
|
||||||
tests/__pycache__
|
tests/__pycache__
|
||||||
tests/mock_default_app/handlers/__pycache__/
|
tests/mock_default_app/handlers/__pycache__/
|
||||||
tests/mock_default_app/__pycache__/
|
tests/mock_default_app/__pycache__/
|
||||||
|
|||||||
@@ -1,2 +1,273 @@
|
|||||||
# Argenta
|
# Argenta
|
||||||
Python library for creating cli apps
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Описание
|
||||||
|
**Argenta** — это библиотека для создания CLI-приложений на Python. Она предоставляет удобные инструменты для маршрутизации команд и обработки пользовательского ввода.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Установка
|
||||||
|
```bash
|
||||||
|
pip install argenta
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```bash
|
||||||
|
poetry add argenta
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Быстрый старт
|
||||||
|
Пример базового CLI-приложения с Argenta:
|
||||||
|
```python
|
||||||
|
#routers.py
|
||||||
|
from argenta.router import Router
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.command("hello")
|
||||||
|
def hello():
|
||||||
|
print("Hello, world!")
|
||||||
|
|
||||||
|
@router.unknown_command
|
||||||
|
def unlnown_command(command):
|
||||||
|
print(f'Command "{command}" undefined')
|
||||||
|
```
|
||||||
|
```python
|
||||||
|
#main.py
|
||||||
|
from argenta.app import App
|
||||||
|
from routers import router
|
||||||
|
|
||||||
|
app: App = App()
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
app.include_router(router)
|
||||||
|
app.start_polling()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Техническая документация
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## declared *classes* :
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### *class* :: `App`
|
||||||
|
Класс, определяющий поведение и состояние приложения
|
||||||
|
|
||||||
|
#### Конструктор
|
||||||
|
```python
|
||||||
|
App(prompt: str = 'Enter a command',
|
||||||
|
initial_greeting: str = '\nHello, I am Argenta\n',
|
||||||
|
farewell_message: str = '\nGoodBye\n',
|
||||||
|
exit_command: str = 'Q',
|
||||||
|
exit_command_description: str = 'Exit command',
|
||||||
|
exit_command_title: str = 'System points:',
|
||||||
|
ignore_exit_command_register: bool = True,
|
||||||
|
ignore_command_register: bool = False,
|
||||||
|
line_separate: str = '',
|
||||||
|
command_group_description_separate: str = '',
|
||||||
|
repeat_command_groups: bool = True,
|
||||||
|
print_func: Callable[[str], None] = print)
|
||||||
|
```
|
||||||
|
**Аргументы:**
|
||||||
|
- **name : mean**
|
||||||
|
- `prompt` (`str`): Сообщение перед вводом команды.
|
||||||
|
- `initial_greeting` (`str`): Приветственное сообщение при запуске.
|
||||||
|
- `farewell_message` (`str`): Сообщение при выходе.
|
||||||
|
- `exit_command` (`str`): Команда выхода (по умолчанию `'Q'`).
|
||||||
|
- `exit_command_description` (`str`): Описание команды выхода.
|
||||||
|
- `exit_command_title` (`str`): Заголовок перед списком команд выхода.
|
||||||
|
- `ignore_exit_command_register` (`bool`): Игнорировать регистр команды выхода.
|
||||||
|
- `ignore_command_register` (`bool`): Игнорировать регистр всех команд.
|
||||||
|
- `line_separate` (`str`): Разделительная строка между командами.
|
||||||
|
- `command_group_description_separate` (`str`): Разделитель между группами команд.
|
||||||
|
- `repeat_command_groups` (`bool`): Повторять описание команд перед вводом.
|
||||||
|
- `print_func` (`Callable[[str], None]`): Функция вывода текста в терминал (по умолчанию `print`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **declared *methods***
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**App().**`start_polling() -> None`
|
||||||
|
|
||||||
|
*method mean* **::** запускает жизненный цикл приложения
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**App().**`include_router(router: Router, is_main: bool = False) -> None`
|
||||||
|
|
||||||
|
*param* `router: Router` **::** регистрируемый роутер
|
||||||
|
|
||||||
|
*param* `is_main: bool` **::** будет ли являться регистрируемый роутер главным
|
||||||
|
*example* **::** `True` или `False`
|
||||||
|
|
||||||
|
*method mean* **::** регистрирует роутер в приложении
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**App().**`set_initial_message(message: str) -> None`
|
||||||
|
|
||||||
|
*param* `message: str` **::** устанавливаемое приветственное сообщение
|
||||||
|
*example* **::** `"Hello, I'm a cli example app"`
|
||||||
|
|
||||||
|
*method mean* **::** устанавливает сообщение, которое будет отображено при запуске программы
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**App().**`set_farewell_message(message: str) -> None`
|
||||||
|
|
||||||
|
*param* `message: str` **::** устанавливаемое сообщение при выходе
|
||||||
|
*example* **::** `"GoodBye !"`
|
||||||
|
|
||||||
|
*method mean* **::** устанавливает сообщение, которое будет отображено при выходе
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**App().**`set_description_message_pattern(pattern: str) -> None`
|
||||||
|
|
||||||
|
*param* `pattern: str` **::** паттерн описания команды при её выводе в консоль
|
||||||
|
*example* **::** `"[{command}] *=*=* {description}"`
|
||||||
|
|
||||||
|
*method mean* **::** устанавливает приветственное сообщение
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**App().**`get_main_router() -> Router`
|
||||||
|
|
||||||
|
*method mean* **::** возвращает `Router()`, который является главным в приложении
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**App().**`get_all_app_commands() -> list[str]`
|
||||||
|
|
||||||
|
*method mean* **::** возвращает список команд всех зарегистрированных роутеров, сохраняя их регистр
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Примечания
|
||||||
|
|
||||||
|
- Среди зарегистрированных в приложении роутеров должен быть один главный, является ли роутер главным
|
||||||
|
определяется значением аргумента `is_main` равным `True`, в методе `App().include_router()`, который по умолчанию равен
|
||||||
|
`False`, если в приложении зарегистрирован лишь один роутер, то он неявно устанавливается главным, если
|
||||||
|
зарегистрировано больше одного роутера, то требуется явное указание главного. При регистрации более одного
|
||||||
|
главного роутера вызывается исключение `OnlyOneMainRouterIsAllowedException`. При регистрации более одного
|
||||||
|
роутера и отсутствии указания главного вызывается исключение `MissingMainRouterException`
|
||||||
|
|
||||||
|
- В устанавливаемом паттерне сообщения описания команды необходимы быть два ключевых слова:
|
||||||
|
`command` и `description`, каждое из которых должно быть заключено в фигурные скобки, после обработки
|
||||||
|
паттерна на места этих ключевых слов будут подставлены соответствующие значения команды, при отсутствии
|
||||||
|
этих двух ключевых слов будет вызвано исключение `InvalidDescriptionMessagePatternException`
|
||||||
|
|
||||||
|
- Команды приложения не должны повторяться, при значении атрибута `ignore_command_register` равным `True`
|
||||||
|
допускается создание обработчиков для разных регистров одинаковых символов в команде, для примера `u` и `U`,
|
||||||
|
при значении атрибута `ignore_command_register` класса `App` равным `False` тот же пример вызывает исключение
|
||||||
|
`RepeatedCommandInDifferentRoutersException`. Исключение вызывается только при наличии пересекающихся команд
|
||||||
|
у __<u>разных</u>__ роутеров
|
||||||
|
|
||||||
|
- У главного обработчика должен быть зарегистрирован обработчик неизвестных команд:
|
||||||
|
```python
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.unknown_command
|
||||||
|
def unknown_command(command):
|
||||||
|
print(f'Command "{command}" undefined')
|
||||||
|
```
|
||||||
|
При отсутствии обработчика неизвестных команд у главного роутера будет вызвано исключение
|
||||||
|
`MissingHandlerForUnknownCommandsException`. При регистрации обработчика неизвестных команд у
|
||||||
|
__<u>не</u>__ главного роутера будет вызвано исключение `HandlerForUnknownCommandsOnNonMainRouterException`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### Исключения
|
||||||
|
|
||||||
|
- `InvalidRouterInstanceException` — Переданный объект в метод `App().include_router()` не является экземпляром класса `Router`.
|
||||||
|
- `InvalidDescriptionMessagePatternException` — Неправильный формат паттерна описания команд.
|
||||||
|
- `OnlyOneMainRouterIsAllowedException` — Регистрация более одного главного роутера.
|
||||||
|
- `MissingMainRouterException` — Отсутствует главный роутер.
|
||||||
|
- `MissingHandlerForUnknownCommandsException` — В основном роутере отсутствует обработчик неизвестных команд.
|
||||||
|
- `HandlerForUnknownCommandsOnNonMainRouterException` — Обработчик неизвестных команд определён не у основного роутера.
|
||||||
|
- `NoRegisteredRoutersException` — Отсутствуют зарегистрированные роутеры.
|
||||||
|
- `NoRegisteredHandlersException` — У роутера нет ни одного обработчика команд.
|
||||||
|
- `RepeatedCommandInDifferentRoutersException` — Одна и та же команда зарегистрирована в разных роутерах.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### *class* :: `Router`
|
||||||
|
Класс, который определяет и конфигурирует обработчики команд
|
||||||
|
|
||||||
|
#### Конструктор
|
||||||
|
```python
|
||||||
|
Router(title: str = 'Commands group title:',
|
||||||
|
name: str = 'subordinate')
|
||||||
|
```
|
||||||
|
|
||||||
|
**Аргументы:**
|
||||||
|
- **name : mean**
|
||||||
|
- `title` (`str`): Заголовок группы команд.
|
||||||
|
- `name` (`str`): Персональное название роутера
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#### **declared *methods***
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`@`Router().**`command(command: str, description: str = None)`
|
||||||
|
|
||||||
|
*param* `command: str` **::** строковый триггер, который будет выполнять указанные действия
|
||||||
|
*example* **::** `U` / `update` / `ExaMPLE`
|
||||||
|
|
||||||
|
*param* `description: str` **::** описание команды, которое будет выведено в консоль
|
||||||
|
*example* **::** `description for update command` или `example description`
|
||||||
|
|
||||||
|
*method mean* **::** декоратор регистрирует функцию как обработчик команды
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**`@`Router().**`unknown_command`
|
||||||
|
|
||||||
|
*method mean* **::** декоратор регистрирует функцию как обработчик неизвестных команд
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Router().**`get_name() -> str`
|
||||||
|
|
||||||
|
*method mean* **::** возвращает установленное название роутера
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Router().**`get_title() -> str`
|
||||||
|
|
||||||
|
*method mean* **::** возвращает установленный заголовок группы команд данного роутера
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Router().**`get_router_info() -> dict`
|
||||||
|
|
||||||
|
*method mean* **::** возвращает информацию о роутере
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Router().**`get_all_commands() -> list[str]`
|
||||||
|
|
||||||
|
*method mean* **::** возвращает все зарегистрированные команды для данного роутера
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### Исключения
|
||||||
|
- `InvalidCommandInstanceException` - Переданный объект для регистрации команды не является строкой
|
||||||
|
- `InvalidDescriptionInstanceException` - Переданный объект для регистрации описания команды не является строкой
|
||||||
|
- `UnknownCommandHandlerHasAlreadyBeenCreatedException` - Обработчик неизвестных команд уже создан
|
||||||
|
- `RepeatedCommandException` - Одна и та же команда зарегистрирована в одном роутере
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from .entity import App
|
from .entity import App
|
||||||
from .exceptions import (HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException,
|
from .exceptions import (HandlerForUnknownCommandsOnNonMainRouterException,
|
||||||
InvalidDescriptionMessagePatternException,
|
InvalidDescriptionMessagePatternException,
|
||||||
InvalidRouterInstanceException,
|
InvalidRouterInstanceException,
|
||||||
OnlyOneMainRouterIsAllowedException,
|
OnlyOneMainRouterIsAllowedException,
|
||||||
MissingMainRouterException,
|
MissingMainRouterException,
|
||||||
MissingHandlersForUnknownCommandsOnMainRouterException)
|
MissingHandlerForUnknownCommandsException)
|
||||||
|
|||||||
+123
-45
@@ -4,40 +4,60 @@ from .exceptions import (InvalidRouterInstanceException,
|
|||||||
InvalidDescriptionMessagePatternException,
|
InvalidDescriptionMessagePatternException,
|
||||||
OnlyOneMainRouterIsAllowedException,
|
OnlyOneMainRouterIsAllowedException,
|
||||||
MissingMainRouterException,
|
MissingMainRouterException,
|
||||||
MissingHandlersForUnknownCommandsOnMainRouterException,
|
MissingHandlerForUnknownCommandsException,
|
||||||
HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException)
|
HandlerForUnknownCommandsOnNonMainRouterException,
|
||||||
|
NoRegisteredRoutersException,
|
||||||
|
NoRegisteredHandlersException,
|
||||||
|
RepeatedCommandInDifferentRoutersException)
|
||||||
|
|
||||||
|
|
||||||
class App:
|
class App:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
prompt: str = 'Enter a command',
|
prompt: str = 'Enter a command',
|
||||||
exit_command: str = 'q',
|
initial_message: str = '\nHello, I am Argenta\n',
|
||||||
|
farewell_message: str = '\nGoodBye\n',
|
||||||
|
exit_command: str = 'Q',
|
||||||
|
exit_command_description: str = 'Exit command',
|
||||||
|
exit_command_title: str = 'System points:',
|
||||||
ignore_exit_command_register: bool = True,
|
ignore_exit_command_register: bool = True,
|
||||||
initial_greeting: str = '\nHello, I am Argenta\n',
|
ignore_command_register: bool = False,
|
||||||
farewell_message: str = 'GoodBye',
|
|
||||||
line_separate: str = '',
|
line_separate: str = '',
|
||||||
command_group_description_separate: str = '',
|
command_group_description_separate: str = '',
|
||||||
|
repeat_command_groups: bool = True,
|
||||||
print_func: Callable[[str], None] = print) -> None:
|
print_func: Callable[[str], None] = print) -> None:
|
||||||
self.prompt = prompt
|
self.prompt = prompt
|
||||||
self.print_func = print_func
|
self.print_func = print_func
|
||||||
self.exit_command = exit_command
|
self.exit_command = exit_command
|
||||||
|
self.exit_command_description = exit_command_description
|
||||||
|
self.exit_command_title = exit_command_title
|
||||||
self.ignore_exit_command_register = ignore_exit_command_register
|
self.ignore_exit_command_register = ignore_exit_command_register
|
||||||
self.farewell_message = farewell_message
|
self.farewell_message = farewell_message
|
||||||
self.initial_greeting = initial_greeting
|
self.initial_message = initial_message
|
||||||
self.line_separate = line_separate
|
self.line_separate = line_separate
|
||||||
self.command_group_description_separate = command_group_description_separate
|
self.command_group_description_separate = command_group_description_separate
|
||||||
|
self.ignore_command_register = ignore_command_register
|
||||||
|
self.repeat_command_groups = repeat_command_groups
|
||||||
|
|
||||||
self._routers: list[Router] = []
|
self._routers: list[Router] = []
|
||||||
self._registered_commands: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = []
|
self._registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = []
|
||||||
self._main_app_router: Router | None = None
|
self._app_main_router: Router | None = None
|
||||||
self._description_message_pattern: str = '[{command}] *=*=* {description}'
|
self._description_message_pattern: str = '[{command}] *=*=* {description}'
|
||||||
|
|
||||||
|
|
||||||
def start_polling(self) -> None:
|
def start_polling(self) -> None:
|
||||||
self.print_func(self.initial_greeting)
|
self._validate_number_of_routers()
|
||||||
|
self._validate_included_routers()
|
||||||
self._validate_main_router()
|
self._validate_main_router()
|
||||||
|
self._validate_all_router_commands()
|
||||||
|
|
||||||
|
self.print_func(self.initial_message)
|
||||||
|
|
||||||
|
if not self.repeat_command_groups:
|
||||||
|
self._print_command_group_description()
|
||||||
|
self.print_func(self.prompt)
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
|
if self.repeat_command_groups:
|
||||||
self._print_command_group_description()
|
self._print_command_group_description()
|
||||||
self.print_func(self.prompt)
|
self.print_func(self.prompt)
|
||||||
|
|
||||||
@@ -47,18 +67,22 @@ class App:
|
|||||||
self.print_func(self.line_separate)
|
self.print_func(self.line_separate)
|
||||||
|
|
||||||
is_unknown_command: bool = self._check_is_command_unknown(command)
|
is_unknown_command: bool = self._check_is_command_unknown(command)
|
||||||
|
|
||||||
if is_unknown_command:
|
if is_unknown_command:
|
||||||
|
if not self.repeat_command_groups:
|
||||||
|
self.print_func(self.prompt)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for router in self._routers:
|
for router in self._routers:
|
||||||
router.input_command_handler(command)
|
router.input_command_handler(command)
|
||||||
|
|
||||||
self.print_func(self.line_separate)
|
self.print_func(self.line_separate)
|
||||||
self.print_func(self.command_group_description_separate)
|
self.print_func(self.command_group_description_separate)
|
||||||
|
if not self.repeat_command_groups:
|
||||||
|
self.print_func(self.prompt)
|
||||||
|
|
||||||
|
|
||||||
def set_initial_greeting(self, greeting: str) -> None:
|
def set_initial_message(self, message: str) -> None:
|
||||||
self.initial_greeting: str = greeting
|
self.initial_message: str = message
|
||||||
|
|
||||||
|
|
||||||
def set_farewell_message(self, message: str) -> None:
|
def set_farewell_message(self, message: str) -> None:
|
||||||
@@ -74,16 +98,81 @@ class App:
|
|||||||
self._description_message_pattern: str = pattern
|
self._description_message_pattern: str = pattern
|
||||||
|
|
||||||
|
|
||||||
def _validate_main_router(self):
|
def get_main_router(self) -> Router:
|
||||||
if not self._main_app_router:
|
return self._app_main_router
|
||||||
raise MissingMainRouterException()
|
|
||||||
|
|
||||||
if not self._main_app_router.unknown_command_func:
|
|
||||||
raise MissingHandlersForUnknownCommandsOnMainRouterException()
|
def get_all_app_commands(self) -> list[str]:
|
||||||
|
all_commands: list[str] = []
|
||||||
|
for router in self._routers:
|
||||||
|
all_commands.extend(router.get_all_commands())
|
||||||
|
|
||||||
|
return all_commands
|
||||||
|
|
||||||
|
|
||||||
|
def include_router(self, router: Router, is_main: True | False = False) -> None:
|
||||||
|
if not isinstance(router, Router):
|
||||||
|
raise InvalidRouterInstanceException()
|
||||||
|
|
||||||
|
if is_main:
|
||||||
|
if not self._app_main_router:
|
||||||
|
self._app_main_router = router
|
||||||
|
router.set_router_as_main()
|
||||||
|
else:
|
||||||
|
raise OnlyOneMainRouterIsAllowedException(self._app_main_router.get_name())
|
||||||
|
|
||||||
|
router.set_ignore_command_register(self.ignore_command_register)
|
||||||
|
self._routers.append(router)
|
||||||
|
|
||||||
|
command_entities: list[dict[str, Callable[[], None] | str]] = router.get_command_entities()
|
||||||
|
self._registered_router_entities.append({'name': router.get_name(),
|
||||||
|
'title': router.get_title(),
|
||||||
|
'entity': router,
|
||||||
|
'commands': command_entities})
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_number_of_routers(self) -> None:
|
||||||
|
if not self._routers:
|
||||||
|
raise NoRegisteredRoutersException()
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_included_routers(self) -> None:
|
||||||
|
for router in self._routers:
|
||||||
|
if not router.get_command_entities():
|
||||||
|
raise NoRegisteredHandlersException(router.get_name())
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_main_router(self):
|
||||||
|
if not self._app_main_router:
|
||||||
|
if len(self._routers) > 1:
|
||||||
|
raise MissingMainRouterException()
|
||||||
|
else:
|
||||||
|
router = self._routers[0]
|
||||||
|
router.set_router_as_main()
|
||||||
|
self._app_main_router = router
|
||||||
|
|
||||||
|
if not self._app_main_router.unknown_command_func:
|
||||||
|
raise MissingHandlerForUnknownCommandsException()
|
||||||
|
|
||||||
for router in self._routers:
|
for router in self._routers:
|
||||||
if router.unknown_command_func and self._main_app_router is not router:
|
if router.unknown_command_func and self._app_main_router is not router:
|
||||||
raise HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException()
|
raise HandlerForUnknownCommandsOnNonMainRouterException()
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_all_router_commands(self) -> None:
|
||||||
|
for idx in range(len(self._registered_router_entities)):
|
||||||
|
current_router: Router = self._registered_router_entities[idx]['entity']
|
||||||
|
routers_without_current_router = self._registered_router_entities.copy()
|
||||||
|
routers_without_current_router.pop(idx)
|
||||||
|
|
||||||
|
current_router_all_commands: list[str] = current_router.get_all_commands()
|
||||||
|
|
||||||
|
for router_entity in routers_without_current_router:
|
||||||
|
if len(set(current_router_all_commands).intersection(set(router_entity['entity'].get_all_commands()))) > 0:
|
||||||
|
raise RepeatedCommandInDifferentRoutersException()
|
||||||
|
if self.ignore_command_register:
|
||||||
|
if len(set([x.lower() for x in current_router_all_commands]).intersection(set([x.lower() for x in router_entity['entity'].get_all_commands()]))) > 0:
|
||||||
|
raise RepeatedCommandInDifferentRoutersException()
|
||||||
|
|
||||||
|
|
||||||
def _checking_command_for_exit_command(self, command: str):
|
def _checking_command_for_exit_command(self, command: str):
|
||||||
@@ -98,25 +187,25 @@ class App:
|
|||||||
|
|
||||||
|
|
||||||
def _check_is_command_unknown(self, command: str):
|
def _check_is_command_unknown(self, command: str):
|
||||||
registered_commands: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = self._registered_commands
|
registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = self._registered_router_entities
|
||||||
for router in registered_commands:
|
for router_entity in registered_router_entities:
|
||||||
for command_entity in router['commands']:
|
for command_entity in router_entity['commands']:
|
||||||
if command_entity['command'].lower() == command.lower():
|
if command_entity['command'].lower() == command.lower():
|
||||||
if router['router'].ignore_command_register:
|
if self.ignore_command_register:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if command_entity['command'] == command:
|
if command_entity['command'] == command:
|
||||||
return False
|
return False
|
||||||
self._main_app_router.unknown_command_handler(command)
|
self._app_main_router.unknown_command_handler(command)
|
||||||
self.print_func(self.line_separate)
|
self.print_func(self.line_separate)
|
||||||
self.print_func(self.command_group_description_separate)
|
self.print_func(self.command_group_description_separate)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _print_command_group_description(self):
|
def _print_command_group_description(self):
|
||||||
for router in self._registered_commands:
|
for router_entity in self._registered_router_entities:
|
||||||
self.print_func(router['name'])
|
self.print_func(router_entity['title'])
|
||||||
for command_entity in router['commands']:
|
for command_entity in router_entity['commands']:
|
||||||
self.print_func(self._description_message_pattern.format(
|
self.print_func(self._description_message_pattern.format(
|
||||||
command=command_entity['command'],
|
command=command_entity['command'],
|
||||||
description=command_entity['description']
|
description=command_entity['description']
|
||||||
@@ -124,22 +213,11 @@ class App:
|
|||||||
)
|
)
|
||||||
self.print_func(self.command_group_description_separate)
|
self.print_func(self.command_group_description_separate)
|
||||||
|
|
||||||
|
self.print_func(self.exit_command_title)
|
||||||
def include_router(self, router: Router, is_main: bool = False) -> None:
|
self.print_func(self._description_message_pattern.format(
|
||||||
if not isinstance(router, Router):
|
command=self.exit_command,
|
||||||
raise InvalidRouterInstanceException()
|
description=self.exit_command_description
|
||||||
|
)
|
||||||
if is_main:
|
)
|
||||||
if not self._main_app_router:
|
self.print_func(self.command_group_description_separate)
|
||||||
self._main_app_router = router
|
|
||||||
router.set_router_as_main()
|
|
||||||
else:
|
|
||||||
raise OnlyOneMainRouterIsAllowedException(router)
|
|
||||||
|
|
||||||
self._routers.append(router)
|
|
||||||
|
|
||||||
registered_commands: list[dict[str, Callable[[], None] | str]] = router.get_registered_commands()
|
|
||||||
self._registered_commands.append({'name': router.get_name(),
|
|
||||||
'router': router,
|
|
||||||
'commands': registered_commands})
|
|
||||||
|
|
||||||
|
|||||||
@@ -28,12 +28,29 @@ class MissingMainRouterException(Exception):
|
|||||||
"One of the registered routers must be the main one")
|
"One of the registered routers must be the main one")
|
||||||
|
|
||||||
|
|
||||||
class MissingHandlersForUnknownCommandsOnMainRouterException(Exception):
|
class MissingHandlerForUnknownCommandsException(Exception):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return ("Missing Handlers For Unknown Commands On The Main Router\n"
|
return ("Missing Handlers For Unknown Commands On The Main Router\n"
|
||||||
"The main router must have a declared handler for unknown commands")
|
"The main router must have a declared handler for unknown commands")
|
||||||
|
|
||||||
|
|
||||||
class HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException(Exception):
|
class HandlerForUnknownCommandsOnNonMainRouterException(Exception):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '\nThe handler for unknown commands can only be declared for the main router'
|
return '\nThe handler for unknown commands can only be declared for the main router'
|
||||||
|
|
||||||
|
|
||||||
|
class NoRegisteredRoutersException(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return "No Registered Router Found"
|
||||||
|
|
||||||
|
|
||||||
|
class NoRegisteredHandlersException(Exception):
|
||||||
|
def __init__(self, router_name):
|
||||||
|
self.router_name = router_name
|
||||||
|
def __str__(self):
|
||||||
|
return f"No Registered Handlers Found For '{self.router_name}'"
|
||||||
|
|
||||||
|
|
||||||
|
class RepeatedCommandInDifferentRoutersException(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return "Commands in different handlers cannot be repeated"
|
||||||
|
|||||||
+70
-18
@@ -1,35 +1,36 @@
|
|||||||
from typing import Callable, Any
|
from typing import Callable, Any
|
||||||
from ..router.exceptions import (InvalidCommandInstanceException,
|
from ..router.exceptions import (InvalidCommandInstanceException,
|
||||||
UnknownCommandHandlerHasAlreadyBeenCreatedException,
|
UnknownCommandHandlerHasAlreadyBeenCreatedException,
|
||||||
InvalidDescriptionInstanceException)
|
InvalidDescriptionInstanceException,
|
||||||
|
RepeatedCommandException)
|
||||||
|
|
||||||
|
|
||||||
class Router:
|
class Router:
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
name: str,
|
title: str = 'Commands group title:',
|
||||||
ignore_command_register: bool = False):
|
name: str = 'subordinate'):
|
||||||
|
|
||||||
self.ignore_command_register = ignore_command_register
|
self.title = title
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
self._processed_commands: list[dict[str, Callable[[], None] | str]] = []
|
self._command_entities: list[dict[str, Callable[[], None] | str]] = []
|
||||||
self.unknown_command_func: Callable[[str], None] | None = None
|
self.unknown_command_func: Callable[[str], None] | None = None
|
||||||
self._is_main_router: bool = False
|
self._is_main_router: bool = False
|
||||||
|
self.ignore_command_register: bool = False
|
||||||
|
|
||||||
|
|
||||||
def command(self, command: str, description: str) -> Callable[[Any], Any]:
|
def command(self, command: str, description: str = None) -> Callable[[Any], Any]:
|
||||||
if not isinstance(command, str):
|
processed_description = Router._validate_description(command, description)
|
||||||
raise InvalidCommandInstanceException()
|
self._validate_command(command)
|
||||||
if not isinstance(description, str):
|
|
||||||
raise InvalidDescriptionInstanceException()
|
|
||||||
else:
|
|
||||||
def command_decorator(func):
|
def command_decorator(func):
|
||||||
self._processed_commands.append({'func': func,
|
self._command_entities.append({'handler_func': func,
|
||||||
'command': command,
|
'command': command,
|
||||||
'description': description})
|
'description': processed_description})
|
||||||
def wrapper(*args, **kwargs):
|
def wrapper(*args, **kwargs):
|
||||||
return func(*args, **kwargs)
|
return func(*args, **kwargs)
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return command_decorator
|
return command_decorator
|
||||||
|
|
||||||
|
|
||||||
@@ -45,27 +46,78 @@ class Router:
|
|||||||
|
|
||||||
|
|
||||||
def input_command_handler(self, input_command):
|
def input_command_handler(self, input_command):
|
||||||
for command_entity in self._processed_commands:
|
for command_entity in self._command_entities:
|
||||||
if input_command.lower() == command_entity['command'].lower():
|
if input_command.lower() == command_entity['command'].lower():
|
||||||
if self.ignore_command_register:
|
if self.ignore_command_register:
|
||||||
return command_entity['func']()
|
return command_entity['handler_func']()
|
||||||
else:
|
else:
|
||||||
if input_command == command_entity['command']:
|
if input_command == command_entity['command']:
|
||||||
return command_entity['func']()
|
return command_entity['handler_func']()
|
||||||
|
|
||||||
|
|
||||||
def unknown_command_handler(self, unknown_command):
|
def unknown_command_handler(self, unknown_command):
|
||||||
self.unknown_command_func(unknown_command)
|
self.unknown_command_func(unknown_command)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_command(self, command: str):
|
||||||
|
if not isinstance(command, str):
|
||||||
|
raise InvalidCommandInstanceException()
|
||||||
|
if command in self.get_all_commands():
|
||||||
|
raise RepeatedCommandException()
|
||||||
|
if self.ignore_command_register:
|
||||||
|
if command.lower() in [x.lower() for x in self.get_all_commands()]:
|
||||||
|
raise RepeatedCommandException()
|
||||||
|
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _validate_description(command: str, description: str):
|
||||||
|
if not isinstance(description, str):
|
||||||
|
if description is None:
|
||||||
|
description = f'description for "{command}" command'
|
||||||
|
else:
|
||||||
|
raise InvalidDescriptionInstanceException()
|
||||||
|
return description
|
||||||
|
|
||||||
|
|
||||||
def set_router_as_main(self):
|
def set_router_as_main(self):
|
||||||
|
if self.name == 'subordinate':
|
||||||
|
self.name = 'main'
|
||||||
self._is_main_router = True
|
self._is_main_router = True
|
||||||
|
|
||||||
|
|
||||||
def get_registered_commands(self) -> list[dict[str, Callable[[], None] | str]]:
|
def set_ignore_command_register(self, ignore_command_register: bool):
|
||||||
return self._processed_commands
|
self.ignore_command_register = ignore_command_register
|
||||||
|
|
||||||
|
|
||||||
|
def get_command_entities(self) -> list[dict[str, Callable[[], None] | str]]:
|
||||||
|
return self._command_entities
|
||||||
|
|
||||||
|
|
||||||
def get_name(self) -> str:
|
def get_name(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
|
def get_title(self) -> str:
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
|
||||||
|
def get_router_info(self) -> dict:
|
||||||
|
return {
|
||||||
|
'title': self.title,
|
||||||
|
'name': self.name,
|
||||||
|
'ignore_command_register': self.ignore_command_register,
|
||||||
|
'attributes': {
|
||||||
|
'command_entities': self._command_entities,
|
||||||
|
'unknown_command_func': self.unknown_command_func,
|
||||||
|
'is_main_router': self._is_main_router
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_commands(self) -> list[str]:
|
||||||
|
all_commands: list[str] = []
|
||||||
|
for command_entity in self._command_entities:
|
||||||
|
all_commands.append(command_entity['command'])
|
||||||
|
|
||||||
|
return all_commands
|
||||||
|
|||||||
@@ -11,3 +11,8 @@ class InvalidDescriptionInstanceException(Exception):
|
|||||||
class UnknownCommandHandlerHasAlreadyBeenCreatedException(Exception):
|
class UnknownCommandHandlerHasAlreadyBeenCreatedException(Exception):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Only one unknown command handler can be declared"
|
return "Only one unknown command handler can be declared"
|
||||||
|
|
||||||
|
|
||||||
|
class RepeatedCommandException(Exception):
|
||||||
|
def __str__(self):
|
||||||
|
return "Commands in handler cannot be repeated"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "argenta"
|
name = "argenta"
|
||||||
version = "0.1.3"
|
version = "0.2.2"
|
||||||
description = "python library for creating cli apps"
|
description = "python library for creating cli apps"
|
||||||
authors = [
|
authors = [
|
||||||
{name = "kolo",email = "kolo.is.main@gmail.com"}
|
{name = "kolo",email = "kolo.is.main@gmail.com"}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
from setuptools import setup, find_packages
|
|
||||||
|
|
||||||
with open("README.md", "r", encoding="utf-8") as fh:
|
|
||||||
long_description = fh.read()
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name="argenta",
|
|
||||||
version="0.1.3",
|
|
||||||
author="kolo",
|
|
||||||
author_email="kolo.is.main@gmail.com",
|
|
||||||
description="Python library for creating CLI apps",
|
|
||||||
long_description=long_description,
|
|
||||||
long_description_content_type="text/markdown",
|
|
||||||
packages=find_packages(),
|
|
||||||
install_requires=[
|
|
||||||
"requests",
|
|
||||||
],
|
|
||||||
classifiers=[
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Operating System :: OS Independent",
|
|
||||||
],
|
|
||||||
python_requires='>=3.11',
|
|
||||||
)
|
|
||||||
@@ -6,10 +6,8 @@ from ..handlers.handlers_implementation.solving_command import start_solving_com
|
|||||||
from ..handlers.handlers_implementation.upgrade_command import upgrade_command
|
from ..handlers.handlers_implementation.upgrade_command import upgrade_command
|
||||||
|
|
||||||
|
|
||||||
work_router: Router = Router(name='Work points:',
|
work_router: Router = Router(title='Work points:')
|
||||||
ignore_command_register=False)
|
settings_router: Router = Router(title='Settings points:')
|
||||||
settings_router: Router = Router(name='Settings points:',
|
|
||||||
ignore_command_register=True)
|
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ from rich.console import Console
|
|||||||
app: App = App(prompt='[italic white bold]What do you want to do(enter number of action)?',
|
app: App = App(prompt='[italic white bold]What do you want to do(enter number of action)?',
|
||||||
line_separate='[bold green]\n---------------------------------------------\n',
|
line_separate='[bold green]\n---------------------------------------------\n',
|
||||||
print_func=Console().print,
|
print_func=Console().print,
|
||||||
command_group_description_separate='')
|
command_group_description_separate='',
|
||||||
|
repeat_command_groups=False)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
@@ -20,7 +21,7 @@ def main():
|
|||||||
app.include_router(work_router, is_main=True)
|
app.include_router(work_router, is_main=True)
|
||||||
app.include_router(settings_router)
|
app.include_router(settings_router)
|
||||||
|
|
||||||
app.set_initial_greeting(initial_greeting)
|
app.set_initial_message(initial_greeting)
|
||||||
app.set_farewell_message(goodbye_message)
|
app.set_farewell_message(goodbye_message)
|
||||||
|
|
||||||
app.set_description_message_pattern('[bold red][{command}][/bold red] [blue]*=*=*[/blue] [bold yellow italic]{description}')
|
app.set_description_message_pattern('[bold red][{command}][/bold red] [blue]*=*=*[/blue] [bold yellow italic]{description}')
|
||||||
|
|||||||
@@ -1,28 +1,27 @@
|
|||||||
|
from pprint import pprint
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
from argenta.router import Router
|
from argenta.router import Router
|
||||||
|
|
||||||
|
|
||||||
work_router: Router = Router(name='Work points:',
|
work_router: Router = Router(name='work')
|
||||||
ignore_command_register=False)
|
settings_router: Router = Router(name='settings')
|
||||||
settings_router: Router = Router(name='Settings points:',
|
|
||||||
ignore_command_register=True)
|
|
||||||
|
|
||||||
console = Console()
|
console = Console()
|
||||||
|
|
||||||
|
|
||||||
@work_router.command(command='0', description='Get Help')
|
@work_router.command(command='a')
|
||||||
def command_help():
|
def command_help():
|
||||||
print('help command')
|
console.print('[bold red]command help [/bold red]')
|
||||||
|
|
||||||
|
|
||||||
@work_router.command(command='1', description='Start Solving')
|
@work_router.command(command='B', description='tester')
|
||||||
def command_start_solving():
|
def command_start_solving():
|
||||||
print('start solving')
|
console.print('[bold red]command start [/bold red]')
|
||||||
|
|
||||||
|
|
||||||
@settings_router.command(command='U', description='Update WordMath')
|
@settings_router.command(command='b')
|
||||||
def command_update():
|
def command_settings():
|
||||||
print('update wordmath')
|
console.print('[bold red]command settings [/bold red]')
|
||||||
|
|
||||||
|
|
||||||
@work_router.unknown_command
|
@work_router.unknown_command
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
|
from pprint import pprint
|
||||||
from tests.mock_default_app.handlers.routers import work_router, settings_router
|
from tests.mock_default_app.handlers.routers import work_router, settings_router
|
||||||
from argenta.app.entity import App
|
from argenta.app.entity import App
|
||||||
from art import text2art
|
from art import text2art
|
||||||
|
|
||||||
|
|
||||||
app: App = App()
|
app: App = App(ignore_command_register=False,
|
||||||
|
line_separate='\n-------------------------------\n')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
Reference in New Issue
Block a user