mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 18:15:28 +03:00
Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 77416cf22c | |||
| b6c84f1a91 | |||
| c2d235e576 | |||
| f7f5db58aa | |||
| 73303b1c08 | |||
| 22f1171192 | |||
| a844095fdc | |||
| a7c6a14705 | |||
| cfdb37330e | |||
| aef6a9ca38 | |||
| c8e0729be8 | |||
| c2bbc5f15d | |||
| 0acbf54e44 | |||
| c3d9541330 | |||
| f6561de9b3 | |||
| bebd84969b | |||
| 365347ea7f | |||
| 33cb528b1d | |||
| fd287c5da0 | |||
| 45f410e3e8 | |||
| 8b06e9cd39 | |||
| c38fe10006 | |||
| 03cbc64f48 | |||
| cbf7d3c578 | |||
| ea2d068022 | |||
| 5991851207 | |||
| f628c3b5b5 |
@@ -0,0 +1,31 @@
|
|||||||
|
name: mypy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "main" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "main" ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: "3.13"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install mypy
|
||||||
|
pip install .
|
||||||
|
|
||||||
|
- name: Run type checker
|
||||||
|
run: mypy -p argenta
|
||||||
@@ -2,9 +2,9 @@ name: ruff
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "kolo" ]
|
branches: [ "main" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "kolo" ]
|
branches: [ "main" ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ name: tests
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "kolo" ]
|
branches: [ "main" ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "kolo" ]
|
branches: [ "main" ]
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -24,8 +24,8 @@ jobs:
|
|||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install poetry
|
pip install uv
|
||||||
poetry install
|
uv sync --group dev
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: poetry run python -m unittest discover
|
run: uv run python -m unittest discover
|
||||||
|
|||||||
+2
-1
@@ -1,9 +1,10 @@
|
|||||||
*venv
|
*venv
|
||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
dist
|
dist
|
||||||
uv.lock
|
uv.lock
|
||||||
*__pycache__/
|
*__pycache__/
|
||||||
*.hist*
|
*.hist*
|
||||||
build
|
build
|
||||||
source
|
source
|
||||||
|
*cache
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Argenta
|
||||||
|
|
||||||
|
### Bibliothek zum Erstellen modularer CLI-Anwendungen
|
||||||
|
|
||||||
|
Mit Argenta können Sie die CLI-Funktionalität in isolierte, abstrahierte Umgebungen einkapseln. Zum Beispiel: Sie erstellen ein Dienstprogramm ähnlich dem Metasploit Framework, bei dem der Benutzer zuerst in einen bestimmten Scoop eintritt (z. B. ein Modul zum Scannen auswählt) und dann auf eine Reihe von Befehlen zugreift, die nur für diesen Kontext spezifisch sind. Argenta bietet eine einfache und prägnante Möglichkeit, eine solche Architektur zu konstruieren.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
```bash
|
||||||
|
pip install argenta
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```bash
|
||||||
|
poetry add argenta
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Schnellstart
|
||||||
|
|
||||||
|
Ein Beispiel für eine einfache Anwendung
|
||||||
|
```python
|
||||||
|
# routers.py
|
||||||
|
from argenta.router import Router
|
||||||
|
from argenta.command import Command
|
||||||
|
from argenta.response import Response
|
||||||
|
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.command(Command("hello"))
|
||||||
|
def handler(response: Response):
|
||||||
|
print("Hello, world!")
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# main.py
|
||||||
|
from argenta.app import App
|
||||||
|
from argenta.orchestrator import Orchestrator
|
||||||
|
from routers import router
|
||||||
|
|
||||||
|
app: App = App()
|
||||||
|
orchestrator: Orchestrator = Orchestrator()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
app.include_router(router)
|
||||||
|
orchestrator.start_polling(app)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Funktionen in der Entwicklung
|
||||||
|
|
||||||
|
- Vollständige Unterstützung für Autocompleter unter Linux
|
||||||
|
|
||||||
|
## Vollständige [Dokumentation](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,12 +1,20 @@
|
|||||||
# Argenta
|
# Argenta
|
||||||
|
|
||||||
### Библиотека для создания модульных CLI приложeний
|
### Library for creating modular CLI applications
|
||||||
|
|
||||||

|
#### 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)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Установка
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Installing
|
||||||
```bash
|
```bash
|
||||||
pip install argenta
|
pip install argenta
|
||||||
```
|
```
|
||||||
@@ -17,9 +25,9 @@ poetry add argenta
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Быстрый старт
|
# Quick start
|
||||||
|
|
||||||
Пример простейшего приложения
|
An example of a simple application
|
||||||
```python
|
```python
|
||||||
# routers.py
|
# routers.py
|
||||||
from argenta.router import Router
|
from argenta.router import Router
|
||||||
@@ -52,7 +60,14 @@ def main() -> None:
|
|||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
```
|
```
|
||||||
## Полная [документация](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Features in development
|
||||||
|
|
||||||
|
- Full support for autocompleter on Linux
|
||||||
|
|
||||||
|
## Full [docs](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# Argenta
|
||||||
|
|
||||||
|
### Библиотека для создания модульных CLI приложeний
|
||||||
|
|
||||||
|
Argenta позволяет инкапсулировать CLI фукциональность в изолированные, абстрагированные **среды**. К примеру: вы создаете утилиту, подобную Metasploit Framework, где пользователь сначала входит в определенный скоуп (например, выбирает модуль для сканирования), а затем получает доступ к набору команд, специфичных только для этого контекста. Argenta предоставляет простой и лаконичный способ для построения такой архитектуры.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Установка
|
||||||
|
```bash
|
||||||
|
pip install argenta
|
||||||
|
```
|
||||||
|
or
|
||||||
|
```bash
|
||||||
|
poetry add argenta
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Быстрый старт
|
||||||
|
|
||||||
|
Пример простейшего приложения
|
||||||
|
```python
|
||||||
|
# routers.py
|
||||||
|
from argenta.router import Router
|
||||||
|
from argenta.command import Command
|
||||||
|
from argenta.response import Response
|
||||||
|
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
@router.command(Command("hello"))
|
||||||
|
def handler(response: Response):
|
||||||
|
print("Hello, world!")
|
||||||
|
```
|
||||||
|
|
||||||
|
```python
|
||||||
|
# main.py
|
||||||
|
from argenta.app import App
|
||||||
|
from argenta.orchestrator import Orchestrator
|
||||||
|
from routers import router
|
||||||
|
|
||||||
|
app: App = App()
|
||||||
|
orchestrator: Orchestrator = Orchestrator()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
app.include_router(router)
|
||||||
|
orchestrator.start_polling(app)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
# Фичи в разработке
|
||||||
|
|
||||||
|
- Полноценная поддержка автокомплитера на Linux
|
||||||
|
|
||||||
|
## Полная [документация](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
from argenta.command import Command
|
||||||
|
from argenta.metrics import get_time_of_pre_cycle_setup
|
||||||
|
from argenta.response import Response
|
||||||
|
from argenta.router import Router
|
||||||
|
from argenta.app import App
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def commands_with_two_aliases(num_of_commands: int):
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
for i in range(num_of_commands):
|
||||||
|
@router.command(Command(f'cmd{i}', aliases=[f'cdr{i}', f'prt{i}']))
|
||||||
|
def handler(response: Response): # pyright: ignore[reportUnusedFunction, reportUnusedParameter]
|
||||||
|
pass
|
||||||
|
|
||||||
|
app = App()
|
||||||
|
app.include_router(router)
|
||||||
|
|
||||||
|
return get_time_of_pre_cycle_setup(app)
|
||||||
|
|
||||||
|
def commands_with_one_aliases(num_of_commands: int):
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
for i in range(num_of_commands):
|
||||||
|
@router.command(Command(f'cmd{i}', aliases=[f'cdr{i}']))
|
||||||
|
def handler(response: Response): # pyright: ignore[reportUnusedFunction, reportUnusedParameter]
|
||||||
|
pass
|
||||||
|
|
||||||
|
app = App()
|
||||||
|
app.include_router(router)
|
||||||
|
|
||||||
|
return get_time_of_pre_cycle_setup(app)
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
from mock.mock_app.handlers.routers import work_router
|
|
||||||
|
|
||||||
from argenta.app import App
|
|
||||||
from argenta.app.defaults import PredefinedMessages
|
|
||||||
from argenta.app.autocompleter import AutoCompleter
|
|
||||||
from argenta.orchestrator import Orchestrator
|
|
||||||
from argenta.orchestrator.argparser import ArgParser
|
|
||||||
from argenta.orchestrator.argparser.arguments import BooleanArgument
|
|
||||||
|
|
||||||
|
|
||||||
arg_parser = ArgParser(processed_args=[BooleanArgument("repeat")])
|
|
||||||
app: App = App(autocompleter=AutoCompleter(".hist"))
|
|
||||||
orchestrator: Orchestrator = Orchestrator()
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
app.include_router(work_router)
|
|
||||||
|
|
||||||
app.add_message_on_startup(PredefinedMessages.USAGE)
|
|
||||||
app.add_message_on_startup(PredefinedMessages.AUTOCOMPLETE)
|
|
||||||
app.add_message_on_startup(PredefinedMessages.HELP)
|
|
||||||
|
|
||||||
orchestrator.start_polling(app)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
+4
-88
@@ -1,89 +1,5 @@
|
|||||||
from argenta.app import App
|
import argparse
|
||||||
from argenta.app.autocompleter import AutoCompleter
|
|
||||||
from argenta.router import Router
|
|
||||||
from argenta.command import Command
|
|
||||||
from argenta.orchestrator import Orchestrator
|
|
||||||
from argenta.app.dividing_line import DynamicDividingLine
|
|
||||||
from argenta.response import Response
|
|
||||||
import platform
|
|
||||||
import psutil
|
|
||||||
import os
|
|
||||||
import subprocess
|
|
||||||
import socket
|
|
||||||
|
|
||||||
# Маршрутизатор для работы с файлами
|
parser = argparse.ArgumentParser(prog='myprogram')
|
||||||
file_router = Router("Файловые операции")
|
_ = parser.add_argument('--foo', help='foo of the %(prog)s program')
|
||||||
|
parser.print_help()
|
||||||
|
|
||||||
@file_router.command(Command("list", "Список файлов"))
|
|
||||||
def list_files(response: Response):
|
|
||||||
files = os.listdir()
|
|
||||||
for file in files:
|
|
||||||
print(file)
|
|
||||||
|
|
||||||
|
|
||||||
@file_router.command(Command("size", "Размер файла"))
|
|
||||||
def file_size(response: Response):
|
|
||||||
file_name = input("Введите имя файла: ")
|
|
||||||
if os.path.exists(file_name):
|
|
||||||
size = os.path.getsize(file_name)
|
|
||||||
print(f"Размер файла {file_name}: {size} байт")
|
|
||||||
else:
|
|
||||||
print(f"Файл {file_name} не найден")
|
|
||||||
|
|
||||||
|
|
||||||
# Маршрутизатор для системных операций
|
|
||||||
system_router = Router("Системные операции")
|
|
||||||
|
|
||||||
|
|
||||||
@system_router.command(Command("info", "Информация о системе"))
|
|
||||||
def system_info(response: Response):
|
|
||||||
print(f"Система: {platform.system()}")
|
|
||||||
print(f"Версия: {platform.version()}")
|
|
||||||
print(f"Архитектура: {platform.architecture()}")
|
|
||||||
print(f"Процессор: {platform.processor()}")
|
|
||||||
|
|
||||||
|
|
||||||
@system_router.command(Command("memory", "Информация о памяти"))
|
|
||||||
def memory_info(response: Response):
|
|
||||||
memory = psutil.virtual_memory()
|
|
||||||
print(f"Всего памяти: {memory.total / (1024**3):.2f} ГБ")
|
|
||||||
print(f"Доступно: {memory.available / (1024**3):.2f} ГБ")
|
|
||||||
print(f"Использовано: {memory.used / (1024**3):.2f} ГБ ({memory.percent}%)")
|
|
||||||
|
|
||||||
|
|
||||||
# Маршрутизатор для сетевых операций
|
|
||||||
network_router = Router("Сетевые операции")
|
|
||||||
|
|
||||||
|
|
||||||
@network_router.command(Command("ping", "Проверка доступности хоста"))
|
|
||||||
def ping_host(response: Response):
|
|
||||||
host = input("Введите имя хоста: ")
|
|
||||||
print(f"Пингую {host}...")
|
|
||||||
subprocess.run(["ping", "-c", "4", host])
|
|
||||||
|
|
||||||
|
|
||||||
@network_router.command(Command("ip", "Показать IP-адреса"))
|
|
||||||
def show_ip(response: Response):
|
|
||||||
hostname = socket.gethostname()
|
|
||||||
print(f"Имя хоста: {hostname}")
|
|
||||||
print(f"IP-адрес: {socket.gethostbyname(hostname)}")
|
|
||||||
|
|
||||||
|
|
||||||
# Создание приложения и регистрация маршрутизаторов
|
|
||||||
app = App(
|
|
||||||
prompt="System> ",
|
|
||||||
initial_message="Pingator",
|
|
||||||
dividing_line=DynamicDividingLine("*"),
|
|
||||||
autocompleter=AutoCompleter(".hist", "e"),
|
|
||||||
)
|
|
||||||
|
|
||||||
# Добавляем все маршрутизаторы
|
|
||||||
app.include_routers(file_router, system_router, network_router)
|
|
||||||
|
|
||||||
# Добавляем сообщение при запуске
|
|
||||||
app.add_message_on_startup("Для просмотра доступных команд нажмите Enter")
|
|
||||||
|
|
||||||
# Запускаем приложение
|
|
||||||
orchestrator = Orchestrator()
|
|
||||||
orchestrator.start_polling(app)
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
from rich.console import Console
|
|
||||||
|
|
||||||
|
|
||||||
console = Console()
|
|
||||||
|
|
||||||
|
|
||||||
def help_command():
|
|
||||||
console.print(
|
|
||||||
"[italic bold]The main functionality of the script is to convert an expression from a string "
|
|
||||||
"to a mathematical one and then calculate this expression. "
|
|
||||||
"Project GitHub: https://github.com/koloideal/WordMath[/italic bold]"
|
|
||||||
)
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
from rich.console import Console
|
|
||||||
|
|
||||||
from argenta.command import Command
|
|
||||||
from argenta.command.flag.defaults import PredefinedFlags
|
|
||||||
from argenta.command.flags import Flags
|
|
||||||
from argenta.response import Response
|
|
||||||
from argenta.router import Router
|
|
||||||
|
|
||||||
|
|
||||||
work_router: Router = Router(title="Work points:")
|
|
||||||
|
|
||||||
console = Console()
|
|
||||||
|
|
||||||
|
|
||||||
@work_router.command(
|
|
||||||
Command(
|
|
||||||
"get",
|
|
||||||
"Get Help",
|
|
||||||
aliases=["help", "Get_help"],
|
|
||||||
flags=Flags(PredefinedFlags.PORT, PredefinedFlags.HOST),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
def command_help(response: Response):
|
|
||||||
print(response.status)
|
|
||||||
print(response.undefined_flags.get_flags())
|
|
||||||
print(response.valid_flags.get_flags())
|
|
||||||
print(response.invalid_value_flags.get_flags())
|
|
||||||
|
|
||||||
|
|
||||||
@work_router.command("run")
|
|
||||||
def command_start_solving(response: Response):
|
|
||||||
print(response.status)
|
|
||||||
print(response.undefined_flags.get_flags())
|
|
||||||
print(response.valid_flags.get_flags())
|
|
||||||
print(response.invalid_value_flags.get_flags())
|
|
||||||
@@ -1,25 +1,22 @@
|
|||||||
from mock.mock_app.handlers.routers import work_router
|
from mock.mock_app.routers import work_router
|
||||||
|
|
||||||
from argenta.app import App
|
from argenta import App, Orchestrator
|
||||||
from argenta.app.defaults import PredefinedMessages
|
from argenta.app import PredefinedMessages, DynamicDividingLine, AutoCompleter
|
||||||
from argenta.app.dividing_line import DynamicDividingLine
|
from argenta.orchestrator import ArgParser
|
||||||
from argenta.app.autocompleter import AutoCompleter
|
from argenta.orchestrator.argparser import BooleanArgument
|
||||||
from argenta.orchestrator import Orchestrator
|
|
||||||
from argenta.orchestrator.argparser import ArgParser
|
|
||||||
from argenta.orchestrator.argparser.arguments import BooleanArgument
|
|
||||||
|
|
||||||
|
|
||||||
arg_parser = ArgParser(processed_args=[BooleanArgument("repeat")])
|
arg_parser = ArgParser(processed_args=[BooleanArgument("repeat")])
|
||||||
app: App = App(
|
app: App = App(
|
||||||
dividing_line=DynamicDividingLine(),
|
dividing_line=DynamicDividingLine(),
|
||||||
autocompleter=AutoCompleter(),
|
autocompleter=AutoCompleter(),
|
||||||
repeat_command_groups=False,
|
|
||||||
)
|
)
|
||||||
orchestrator: Orchestrator = Orchestrator(arg_parser)
|
orchestrator: Orchestrator = Orchestrator(arg_parser)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app.include_router(work_router)
|
app.include_router(work_router)
|
||||||
|
print(f"\n\n{orchestrator.get_input_args()}")
|
||||||
|
|
||||||
app.add_message_on_startup(PredefinedMessages.USAGE)
|
app.add_message_on_startup(PredefinedMessages.USAGE)
|
||||||
app.add_message_on_startup(PredefinedMessages.AUTOCOMPLETE)
|
app.add_message_on_startup(PredefinedMessages.AUTOCOMPLETE)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
from argenta.command import Command, PredefinedFlags, Flags, Flag, PossibleValues
|
||||||
|
from argenta.response import Response
|
||||||
|
from argenta import Router
|
||||||
|
|
||||||
|
|
||||||
|
work_router: Router = Router(title="Work points:")
|
||||||
|
|
||||||
|
flag = Flag('csdv', possible_values=PossibleValues.NEITHER)
|
||||||
|
|
||||||
|
|
||||||
|
@work_router.command(
|
||||||
|
Command("get",
|
||||||
|
description="Get Help",
|
||||||
|
aliases=["help", "Get_help"],
|
||||||
|
flags=Flags([PredefinedFlags.PORT,
|
||||||
|
PredefinedFlags.HOST])))
|
||||||
|
def command_help(response: Response):
|
||||||
|
print(response.status)
|
||||||
|
print(response.input_flags.flags)
|
||||||
|
|
||||||
|
|
||||||
|
@work_router.command("run")
|
||||||
|
def command_start_solving(response: Response):
|
||||||
|
print(response.status)
|
||||||
|
print(response.input_flags.flags)
|
||||||
+10
-2
@@ -1,9 +1,9 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "argenta"
|
name = "argenta"
|
||||||
version = "1.0.1"
|
version = "1.1.1"
|
||||||
description = "Python library for building modular CLI applications"
|
description = "Python library for building modular CLI applications"
|
||||||
authors = [{ name = "kolo", email = "kolo.is.main@gmail.com" }]
|
authors = [{ name = "kolo", email = "kolo.is.main@gmail.com" }]
|
||||||
requires-python = ">=3.11, <4.0"
|
requires-python = ">=3.11"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = { text = "MIT" }
|
license = { text = "MIT" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
@@ -26,3 +26,11 @@ exclude = [
|
|||||||
requires = ["hatchling"]
|
requires = ["hatchling"]
|
||||||
build-backend = "hatchling.build"
|
build-backend = "hatchling.build"
|
||||||
|
|
||||||
|
[dependency-groups]
|
||||||
|
dev = [
|
||||||
|
"mypy>=1.14.1",
|
||||||
|
"pytest>=8.3.2",
|
||||||
|
"ruff>=0.12.12",
|
||||||
|
"wemake-python-styleguide>=0.17.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
__all__ = [
|
||||||
|
'App',
|
||||||
|
'Orchestrator',
|
||||||
|
'Router',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
from argenta.app import App
|
||||||
|
from argenta.orchestrator import Orchestrator
|
||||||
|
from argenta.router import Router
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
__all__ = ["App"]
|
__all__ = [
|
||||||
|
"App",
|
||||||
|
"PredefinedMessages",
|
||||||
|
"DynamicDividingLine",
|
||||||
|
"StaticDividingLine",
|
||||||
|
"AutoCompleter"
|
||||||
|
]
|
||||||
|
|
||||||
from argenta.app.models import App
|
from argenta.app.models import App
|
||||||
|
from argenta.app.defaults import PredefinedMessages
|
||||||
|
from argenta.app.dividing_line.models import DynamicDividingLine, StaticDividingLine
|
||||||
|
from argenta.app.autocompleter.entity import AutoCompleter
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from typing import Never
|
|||||||
|
|
||||||
class AutoCompleter:
|
class AutoCompleter:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, history_filename: str = False, autocomplete_button: str = "tab"
|
self, history_filename: str | None = None, autocomplete_button: str = "tab"
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Public. Configures and implements auto-completion of input command
|
Public. Configures and implements auto-completion of input command
|
||||||
@@ -13,10 +13,10 @@ class AutoCompleter:
|
|||||||
:param autocomplete_button: the button for auto-completion
|
:param autocomplete_button: the button for auto-completion
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.history_filename = history_filename
|
self.history_filename: str | None = history_filename
|
||||||
self.autocomplete_button = autocomplete_button
|
self.autocomplete_button: str = autocomplete_button
|
||||||
|
|
||||||
def _complete(self, text, state) -> str | None:
|
def _complete(self, text: str, state: int) -> str | None:
|
||||||
"""
|
"""
|
||||||
Private. Auto-completion function
|
Private. Auto-completion function
|
||||||
:param text: part of the command being entered
|
:param text: part of the command being entered
|
||||||
@@ -24,7 +24,7 @@ class AutoCompleter:
|
|||||||
:return: the desired candidate as str or None
|
:return: the desired candidate as str or None
|
||||||
"""
|
"""
|
||||||
matches: list[str] = sorted(
|
matches: list[str] = sorted(
|
||||||
cmd for cmd in self.get_history_items() if cmd.startswith(text)
|
cmd for cmd in _get_history_items() if cmd.startswith(text)
|
||||||
)
|
)
|
||||||
if len(matches) > 1:
|
if len(matches) > 1:
|
||||||
common_prefix = matches[0]
|
common_prefix = matches[0]
|
||||||
@@ -38,7 +38,7 @@ class AutoCompleter:
|
|||||||
i += 1
|
i += 1
|
||||||
common_prefix = common_prefix[:i]
|
common_prefix = common_prefix[:i]
|
||||||
if state == 0:
|
if state == 0:
|
||||||
readline.insert_text(common_prefix[len(text) :])
|
readline.insert_text(common_prefix[len(text) :])
|
||||||
readline.redisplay()
|
readline.redisplay()
|
||||||
return None
|
return None
|
||||||
elif len(matches) == 1:
|
elif len(matches) == 1:
|
||||||
@@ -54,10 +54,10 @@ class AutoCompleter:
|
|||||||
"""
|
"""
|
||||||
if self.history_filename:
|
if self.history_filename:
|
||||||
if os.path.exists(self.history_filename):
|
if os.path.exists(self.history_filename):
|
||||||
readline.read_history_file(self.history_filename)
|
readline.read_history_file(self.history_filename)
|
||||||
else:
|
else:
|
||||||
for line in all_commands:
|
for line in all_commands:
|
||||||
readline.add_history(line)
|
readline.add_history(line)
|
||||||
|
|
||||||
readline.set_completer(self._complete)
|
readline.set_completer(self._complete)
|
||||||
readline.set_completer_delims(readline.get_completer_delims().replace(" ", ""))
|
readline.set_completer_delims(readline.get_completer_delims().replace(" ", ""))
|
||||||
@@ -69,7 +69,7 @@ class AutoCompleter:
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
if self.history_filename:
|
if self.history_filename:
|
||||||
readline.write_history_file(self.history_filename)
|
readline.write_history_file(self.history_filename)
|
||||||
with open(self.history_filename, "r") as history_file:
|
with open(self.history_filename, "r") as history_file:
|
||||||
raw_history = history_file.read()
|
raw_history = history_file.read()
|
||||||
pretty_history: list[str] = []
|
pretty_history: list[str] = []
|
||||||
@@ -77,15 +77,14 @@ class AutoCompleter:
|
|||||||
if line.split()[0] in all_commands:
|
if line.split()[0] in all_commands:
|
||||||
pretty_history.append(line)
|
pretty_history.append(line)
|
||||||
with open(self.history_filename, "w") as history_file:
|
with open(self.history_filename, "w") as history_file:
|
||||||
history_file.write("\n".join(pretty_history))
|
_ = history_file.write("\n".join(pretty_history))
|
||||||
|
|
||||||
@staticmethod
|
def _get_history_items() -> list[str] | list[Never]:
|
||||||
def get_history_items() -> list[str] | list[Never]:
|
"""
|
||||||
"""
|
Private. Returns a list of all commands entered by the user
|
||||||
Private. Returns a list of all commands entered by the user
|
:return: all commands entered by the user as list[str] | list[Never]
|
||||||
:return: all commands entered by the user as list[str] | list[Never]
|
"""
|
||||||
"""
|
return [
|
||||||
return [
|
readline.get_history_item(i)
|
||||||
readline.get_history_item(i)
|
for i in range(1, readline.get_current_history_length() + 1)
|
||||||
for i in range(1, readline.get_current_history_length() + 1)
|
]
|
||||||
]
|
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
from dataclasses import dataclass
|
from enum import StrEnum
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
class PredefinedMessages(StrEnum):
|
||||||
class PredefinedMessages:
|
|
||||||
"""
|
"""
|
||||||
Public. A dataclass with predetermined messages for quick use
|
Public. A dataclass with predetermined messages for quick use
|
||||||
"""
|
"""
|
||||||
|
|
||||||
USAGE = "[b dim]Usage[/b dim]: [i]<command> <[green]flags[/green]>[/i]"
|
USAGE = "[b dim]Usage[/b dim]: [i]<command> <[green]flags[/green]>[/i]"
|
||||||
HELP = "[b dim]Help[/b dim]: [i]<command>[/i] [b red]--help[/b red]"
|
HELP = "[b dim]Help[/b dim]: [i]<command>[/i] [b red]--help[/b red]"
|
||||||
AUTOCOMPLETE = "[b dim]Autocomplete[/b dim]: [i]<part>[/i] [bold]<tab>"
|
AUTOCOMPLETE = "[b dim]Autocomplete[/b dim]: [i]<part>[/i] [bold]<tab>"
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class BaseDividingLine(ABC):
|
|||||||
:param unit_part: the single part of the dividing line
|
:param unit_part: the single part of the dividing line
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._unit_part = unit_part
|
self._unit_part: str = unit_part
|
||||||
|
|
||||||
def get_unit_part(self) -> str:
|
def get_unit_part(self) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -22,7 +22,7 @@ class BaseDividingLine(ABC):
|
|||||||
|
|
||||||
|
|
||||||
class StaticDividingLine(BaseDividingLine):
|
class StaticDividingLine(BaseDividingLine):
|
||||||
def __init__(self, unit_part: str = "-", length: int = 25) -> None:
|
def __init__(self, unit_part: str = "-", *, length: int = 25) -> None:
|
||||||
"""
|
"""
|
||||||
Public. The static dividing line
|
Public. The static dividing line
|
||||||
:param unit_part: the single part of the dividing line
|
:param unit_part: the single part of the dividing line
|
||||||
@@ -30,9 +30,9 @@ class StaticDividingLine(BaseDividingLine):
|
|||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
super().__init__(unit_part)
|
super().__init__(unit_part)
|
||||||
self.length = length
|
self.length: int = length
|
||||||
|
|
||||||
def get_full_static_line(self, is_override: bool) -> str:
|
def get_full_static_line(self, *, is_override: bool) -> str:
|
||||||
"""
|
"""
|
||||||
Private. Returns the full line of the dividing line
|
Private. Returns the full line of the dividing line
|
||||||
:param is_override: has the default text layout been redefined
|
:param is_override: has the default text layout been redefined
|
||||||
@@ -53,7 +53,7 @@ class DynamicDividingLine(BaseDividingLine):
|
|||||||
"""
|
"""
|
||||||
super().__init__(unit_part)
|
super().__init__(unit_part)
|
||||||
|
|
||||||
def get_full_dynamic_line(self, length: int, is_override: bool) -> str:
|
def get_full_dynamic_line(self, *, length: int, is_override: bool) -> str:
|
||||||
"""
|
"""
|
||||||
Private. Returns the full line of the dividing line
|
Private. Returns the full line of the dividing line
|
||||||
:param length: the length of the dividing line
|
:param length: the length of the dividing line
|
||||||
|
|||||||
+178
-175
@@ -1,88 +1,84 @@
|
|||||||
from typing import Callable
|
|
||||||
from rich.console import Console
|
|
||||||
from rich.markup import escape
|
|
||||||
from art import text2art
|
|
||||||
from contextlib import redirect_stdout
|
|
||||||
import io
|
import io
|
||||||
import re
|
import re
|
||||||
|
from contextlib import redirect_stdout
|
||||||
|
from typing import Never, TypeAlias
|
||||||
|
|
||||||
|
from art import text2art # pyright: ignore[reportMissingTypeStubs, reportUnknownVariableType]
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.markup import escape
|
||||||
|
|
||||||
from argenta.command.models import Command, InputCommand
|
|
||||||
from argenta.router import Router
|
|
||||||
from argenta.router.defaults import system_router
|
|
||||||
from argenta.app.autocompleter import AutoCompleter
|
from argenta.app.autocompleter import AutoCompleter
|
||||||
from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine
|
from argenta.app.dividing_line.models import DynamicDividingLine, StaticDividingLine
|
||||||
from argenta.command.exceptions import (
|
from argenta.app.protocols import (
|
||||||
UnprocessedInputFlagException,
|
DescriptionMessageGenerator,
|
||||||
RepeatedInputFlagsException,
|
EmptyCommandHandler,
|
||||||
EmptyInputCommandException,
|
NonStandardBehaviorHandler,
|
||||||
BaseInputCommandException,
|
Printer,
|
||||||
)
|
)
|
||||||
from argenta.app.registered_routers.entity import RegisteredRouters
|
from argenta.app.registered_routers.entity import RegisteredRouters
|
||||||
|
from argenta.command.exceptions import (
|
||||||
|
EmptyInputCommandException,
|
||||||
|
InputCommandException,
|
||||||
|
RepeatedInputFlagsException,
|
||||||
|
UnprocessedInputFlagException,
|
||||||
|
)
|
||||||
|
from argenta.command.models import Command, InputCommand
|
||||||
from argenta.response import Response
|
from argenta.response import Response
|
||||||
|
from argenta.router import Router
|
||||||
|
from argenta.router.defaults import system_router
|
||||||
|
|
||||||
|
Matches: TypeAlias = list[str] | list[Never]
|
||||||
|
|
||||||
|
|
||||||
class BaseApp:
|
class BaseApp:
|
||||||
def __init__(
|
def __init__(self, *, prompt: str,
|
||||||
self,
|
initial_message: str,
|
||||||
prompt: str,
|
farewell_message: str,
|
||||||
initial_message: str,
|
exit_command: Command,
|
||||||
farewell_message: str,
|
system_router_title: str | None,
|
||||||
exit_command: Command,
|
ignore_command_register: bool,
|
||||||
system_router_title: str | None,
|
dividing_line: StaticDividingLine | DynamicDividingLine,
|
||||||
ignore_command_register: bool,
|
repeat_command_groups: bool,
|
||||||
dividing_line: StaticDividingLine | DynamicDividingLine,
|
override_system_messages: bool,
|
||||||
repeat_command_groups: bool,
|
autocompleter: AutoCompleter,
|
||||||
override_system_messages: bool,
|
print_func: Printer) -> None:
|
||||||
autocompleter: AutoCompleter,
|
self._prompt: str = prompt
|
||||||
print_func: Callable[[str], None],
|
self._print_func: Printer = print_func
|
||||||
) -> None:
|
self._exit_command: Command = exit_command
|
||||||
self._prompt = prompt
|
self._system_router_title: str | None = system_router_title
|
||||||
self._print_func = print_func
|
self._dividing_line: StaticDividingLine | DynamicDividingLine = dividing_line
|
||||||
self._exit_command = exit_command
|
self._ignore_command_register: bool = ignore_command_register
|
||||||
self._system_router_title = system_router_title
|
self._repeat_command_groups_description: bool = repeat_command_groups
|
||||||
self._dividing_line = dividing_line
|
self._override_system_messages: bool = override_system_messages
|
||||||
self._ignore_command_register = ignore_command_register
|
self._autocompleter: AutoCompleter = autocompleter
|
||||||
self._repeat_command_groups_description = repeat_command_groups
|
|
||||||
self._override_system_messages = override_system_messages
|
|
||||||
self._autocompleter = autocompleter
|
|
||||||
|
|
||||||
self._farewell_message = farewell_message
|
self._farewell_message: str = farewell_message
|
||||||
self._initial_message = initial_message
|
self._initial_message: str = initial_message
|
||||||
|
|
||||||
self._description_message_gen: Callable[[str, str], str] = (
|
self._description_message_gen: DescriptionMessageGenerator = lambda command, description: f"{command} *=*=* {description}"
|
||||||
lambda command, description: f"[{command}] *=*=* {description}"
|
|
||||||
)
|
|
||||||
self._registered_routers: RegisteredRouters = RegisteredRouters()
|
self._registered_routers: RegisteredRouters = RegisteredRouters()
|
||||||
self._messages_on_startup: list[str] = []
|
self._messages_on_startup: list[str] = []
|
||||||
|
|
||||||
self._all_registered_triggers_in_lower: list[str] = []
|
self._matching_lower_triggers_with_routers: dict[str, Router] = {}
|
||||||
self._all_registered_triggers_in_default_case: list[str] = []
|
self._matching_default_triggers_with_routers: dict[str, Router] = {}
|
||||||
|
|
||||||
self._incorrect_input_syntax_handler: Callable[[str], None] = (
|
self._current_matching_triggers_with_routers: dict[str, Router] = self._matching_lower_triggers_with_routers if self._ignore_command_register else self._matching_default_triggers_with_routers
|
||||||
lambda raw_command: print_func(f"Incorrect flag syntax: {raw_command}")
|
|
||||||
)
|
|
||||||
self._repeated_input_flags_handler: Callable[[str], None] = (
|
|
||||||
lambda raw_command: print_func(f"Repeated input flags: {raw_command}")
|
|
||||||
)
|
|
||||||
self._empty_input_command_handler: Callable[[], None] = lambda: print_func(
|
|
||||||
"Empty input command"
|
|
||||||
)
|
|
||||||
self._unknown_command_handler: Callable[[InputCommand], None] = (
|
|
||||||
lambda command: print_func(f"Unknown command: {command.get_trigger()}")
|
|
||||||
)
|
|
||||||
self._exit_command_handler: Callable[[Response], None] = (
|
|
||||||
lambda response: print_func(self._farewell_message)
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_description_message_pattern(self, _: Callable[[str, str], str]) -> None:
|
self._incorrect_input_syntax_handler: NonStandardBehaviorHandler[str] = lambda _: print_func(f"Incorrect flag syntax: {_}")
|
||||||
|
self._repeated_input_flags_handler: NonStandardBehaviorHandler[str] = lambda _: print_func(f"Repeated input flags: {_}")
|
||||||
|
self._empty_input_command_handler: EmptyCommandHandler = lambda: print_func("Empty input command")
|
||||||
|
self._unknown_command_handler: NonStandardBehaviorHandler[InputCommand] = lambda _: print_func(f"Unknown command: {_.trigger}")
|
||||||
|
self._exit_command_handler: NonStandardBehaviorHandler[Response] = lambda _: print_func(self._farewell_message)
|
||||||
|
|
||||||
|
def set_description_message_pattern(self, _: DescriptionMessageGenerator, /) -> None:
|
||||||
"""
|
"""
|
||||||
Public. Sets the output pattern of the available commands
|
Public. Sets the output pattern of the available commands
|
||||||
:param _: output pattern of the available commands
|
:param _: output pattern of the available commands
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._description_message_gen: Callable[[str, str], str] = _
|
self._description_message_gen = _
|
||||||
|
|
||||||
def set_incorrect_input_syntax_handler(self, _: Callable[[str], None]) -> None:
|
def set_incorrect_input_syntax_handler(self, _: NonStandardBehaviorHandler[str], /) -> None:
|
||||||
"""
|
"""
|
||||||
Public. Sets the handler for incorrect flags when entering a command
|
Public. Sets the handler for incorrect flags when entering a command
|
||||||
:param _: handler for incorrect flags when entering a command
|
:param _: handler for incorrect flags when entering a command
|
||||||
@@ -90,7 +86,7 @@ class BaseApp:
|
|||||||
"""
|
"""
|
||||||
self._incorrect_input_syntax_handler = _
|
self._incorrect_input_syntax_handler = _
|
||||||
|
|
||||||
def set_repeated_input_flags_handler(self, _: Callable[[str], None]) -> None:
|
def set_repeated_input_flags_handler(self, _: NonStandardBehaviorHandler[str], /) -> None:
|
||||||
"""
|
"""
|
||||||
Public. Sets the handler for repeated flags when entering a command
|
Public. Sets the handler for repeated flags when entering a command
|
||||||
:param _: handler for repeated flags when entering a command
|
:param _: handler for repeated flags when entering a command
|
||||||
@@ -98,7 +94,7 @@ class BaseApp:
|
|||||||
"""
|
"""
|
||||||
self._repeated_input_flags_handler = _
|
self._repeated_input_flags_handler = _
|
||||||
|
|
||||||
def set_unknown_command_handler(self, _: Callable[[str], None]) -> None:
|
def set_unknown_command_handler(self, _: NonStandardBehaviorHandler[InputCommand], /) -> None:
|
||||||
"""
|
"""
|
||||||
Public. Sets the handler for unknown commands when entering a command
|
Public. Sets the handler for unknown commands when entering a command
|
||||||
:param _: handler for unknown commands when entering a command
|
:param _: handler for unknown commands when entering a command
|
||||||
@@ -106,7 +102,7 @@ class BaseApp:
|
|||||||
"""
|
"""
|
||||||
self._unknown_command_handler = _
|
self._unknown_command_handler = _
|
||||||
|
|
||||||
def set_empty_command_handler(self, _: Callable[[], None]) -> None:
|
def set_empty_command_handler(self, _: EmptyCommandHandler, /) -> None:
|
||||||
"""
|
"""
|
||||||
Public. Sets the handler for empty commands when entering a command
|
Public. Sets the handler for empty commands when entering a command
|
||||||
:param _: handler for empty commands when entering a command
|
:param _: handler for empty commands when entering a command
|
||||||
@@ -114,7 +110,7 @@ class BaseApp:
|
|||||||
"""
|
"""
|
||||||
self._empty_input_command_handler = _
|
self._empty_input_command_handler = _
|
||||||
|
|
||||||
def set_exit_command_handler(self, _: Callable[[], None]) -> None:
|
def set_exit_command_handler(self, _: NonStandardBehaviorHandler[Response], /) -> None:
|
||||||
"""
|
"""
|
||||||
Public. Sets the handler for exit command when entering a command
|
Public. Sets the handler for exit command when entering a command
|
||||||
:param _: handler for exit command when entering a command
|
:param _: handler for exit command when entering a command
|
||||||
@@ -130,11 +126,12 @@ class BaseApp:
|
|||||||
for registered_router in self._registered_routers:
|
for registered_router in self._registered_routers:
|
||||||
if registered_router.title:
|
if registered_router.title:
|
||||||
self._print_func(registered_router.title)
|
self._print_func(registered_router.title)
|
||||||
for command_handler in registered_router.get_command_handlers():
|
for command_handler in registered_router.command_handlers:
|
||||||
|
handled_command = command_handler.handled_command
|
||||||
self._print_func(
|
self._print_func(
|
||||||
self._description_message_gen(
|
self._description_message_gen(
|
||||||
command_handler.get_handled_command().get_trigger(),
|
handled_command.trigger,
|
||||||
command_handler.get_handled_command().get_description(),
|
handled_command.description,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self._print_func("")
|
self._print_func("")
|
||||||
@@ -145,16 +142,7 @@ class BaseApp:
|
|||||||
:param text: framed text
|
:param text: framed text
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
if isinstance(self._dividing_line, StaticDividingLine):
|
if isinstance(self._dividing_line, DynamicDividingLine):
|
||||||
self._print_func(
|
|
||||||
self._dividing_line.get_full_static_line(self._override_system_messages)
|
|
||||||
)
|
|
||||||
print(text.strip("\n"))
|
|
||||||
self._print_func(
|
|
||||||
self._dividing_line.get_full_static_line(self._override_system_messages)
|
|
||||||
)
|
|
||||||
|
|
||||||
elif isinstance(self._dividing_line, DynamicDividingLine):
|
|
||||||
clear_text = re.sub(r"\u001b\[[0-9;]*m", "", text)
|
clear_text = re.sub(r"\u001b\[[0-9;]*m", "", text)
|
||||||
max_length_line = max([len(line) for line in clear_text.split("\n")])
|
max_length_line = max([len(line) for line in clear_text.split("\n")])
|
||||||
max_length_line = (
|
max_length_line = (
|
||||||
@@ -167,15 +155,27 @@ class BaseApp:
|
|||||||
|
|
||||||
self._print_func(
|
self._print_func(
|
||||||
self._dividing_line.get_full_dynamic_line(
|
self._dividing_line.get_full_dynamic_line(
|
||||||
max_length_line, self._override_system_messages
|
length=max_length_line, is_override=self._override_system_messages
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print(text.strip("\n"))
|
print(text.strip("\n"))
|
||||||
self._print_func(
|
self._print_func(
|
||||||
self._dividing_line.get_full_dynamic_line(
|
self._dividing_line.get_full_dynamic_line(
|
||||||
max_length_line, self._override_system_messages
|
length=max_length_line, is_override=self._override_system_messages
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
elif isinstance(self._dividing_line, StaticDividingLine): # pyright: ignore[reportUnnecessaryIsInstance]
|
||||||
|
self._print_func(
|
||||||
|
self._dividing_line.get_full_static_line(is_override=self._override_system_messages)
|
||||||
|
)
|
||||||
|
print(text.strip("\n"))
|
||||||
|
self._print_func(
|
||||||
|
self._dividing_line.get_full_static_line(is_override=self._override_system_messages)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def _is_exit_command(self, command: InputCommand) -> bool:
|
def _is_exit_command(self, command: InputCommand) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -183,20 +183,21 @@ class BaseApp:
|
|||||||
:param command: command to check
|
:param command: command to check
|
||||||
:return: is it an exit command or not as bool
|
:return: is it an exit command or not as bool
|
||||||
"""
|
"""
|
||||||
|
trigger = command.trigger
|
||||||
|
exit_trigger = self._exit_command.trigger
|
||||||
if self._ignore_command_register:
|
if self._ignore_command_register:
|
||||||
if (
|
if (
|
||||||
command.get_trigger().lower()
|
trigger.lower() == exit_trigger.lower()
|
||||||
== self._exit_command.get_trigger().lower()
|
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
elif command.get_trigger().lower() in [
|
elif trigger.lower() in [
|
||||||
x.lower() for x in self._exit_command.get_aliases()
|
x.lower() for x in self._exit_command.aliases
|
||||||
]:
|
]:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if command.get_trigger() == self._exit_command.get_trigger():
|
if trigger == exit_trigger:
|
||||||
return True
|
return True
|
||||||
elif command.get_trigger() in self._exit_command.get_aliases():
|
elif trigger in self._exit_command.aliases:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@@ -206,17 +207,17 @@ class BaseApp:
|
|||||||
:param command: command to check
|
:param command: command to check
|
||||||
:return: is it an unknown command or not as bool
|
:return: is it an unknown command or not as bool
|
||||||
"""
|
"""
|
||||||
input_command_trigger = command.get_trigger()
|
input_command_trigger = command.trigger
|
||||||
if self._ignore_command_register:
|
if self._ignore_command_register:
|
||||||
if input_command_trigger.lower() in self._all_registered_triggers_in_lower:
|
if input_command_trigger.lower() in list(self._current_matching_triggers_with_routers.keys()):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
if input_command_trigger in self._all_registered_triggers_in_default_case:
|
if input_command_trigger in list(self._current_matching_triggers_with_routers.keys()):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _error_handler(
|
def _error_handler(
|
||||||
self, error: BaseInputCommandException, raw_command: str
|
self, error: InputCommandException, raw_command: str
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Private. Handles parsing errors of the entered command
|
Private. Handles parsing errors of the entered command
|
||||||
@@ -224,13 +225,12 @@ class BaseApp:
|
|||||||
:param raw_command: the raw input command
|
:param raw_command: the raw input command
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
match error:
|
if isinstance(error, UnprocessedInputFlagException):
|
||||||
case UnprocessedInputFlagException():
|
self._incorrect_input_syntax_handler(raw_command)
|
||||||
self._incorrect_input_syntax_handler(raw_command)
|
elif isinstance(error, RepeatedInputFlagsException):
|
||||||
case RepeatedInputFlagsException():
|
self._repeated_input_flags_handler(raw_command)
|
||||||
self._repeated_input_flags_handler(raw_command)
|
elif isinstance(error, EmptyInputCommandException):
|
||||||
case EmptyInputCommandException():
|
self._empty_input_command_handler()
|
||||||
self._empty_input_command_handler()
|
|
||||||
|
|
||||||
def _setup_system_router(self) -> None:
|
def _setup_system_router(self) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -240,26 +240,25 @@ class BaseApp:
|
|||||||
system_router.title = self._system_router_title
|
system_router.title = self._system_router_title
|
||||||
|
|
||||||
@system_router.command(self._exit_command)
|
@system_router.command(self._exit_command)
|
||||||
def exit_command(response: Response) -> None:
|
def _(response: Response) -> None:
|
||||||
self._exit_command_handler(response)
|
self._exit_command_handler(response)
|
||||||
|
|
||||||
if system_router not in self._registered_routers.get_registered_routers():
|
if system_router not in self._registered_routers.registered_routers:
|
||||||
system_router.set_command_register_ignore(self._ignore_command_register)
|
system_router.command_register_ignore = self._ignore_command_register
|
||||||
self._registered_routers.add_registered_router(system_router)
|
self._registered_routers.add_registered_router(system_router)
|
||||||
|
|
||||||
def _most_similar_command(self, unknown_command: str) -> str | None:
|
def _most_similar_command(self, unknown_command: str) -> str | None:
|
||||||
all_commands = (
|
all_commands = list(self._current_matching_triggers_with_routers.keys())
|
||||||
self._all_registered_triggers_in_lower
|
|
||||||
if self._ignore_command_register
|
matches_startswith_unknown_command: Matches = sorted(
|
||||||
else self._all_registered_triggers_in_default_case
|
|
||||||
)
|
|
||||||
matches: list[str] | list = sorted(
|
|
||||||
cmd for cmd in all_commands if cmd.startswith(unknown_command)
|
cmd for cmd in all_commands if cmd.startswith(unknown_command)
|
||||||
)
|
)
|
||||||
if not matches:
|
matches_startswith_cmd: Matches = sorted(
|
||||||
matches: list[str] | list = sorted(
|
cmd for cmd in all_commands if unknown_command.startswith(cmd)
|
||||||
cmd for cmd in all_commands if unknown_command.startswith(cmd)
|
)
|
||||||
)
|
|
||||||
|
matches: Matches = matches_startswith_unknown_command or matches_startswith_cmd
|
||||||
|
|
||||||
if len(matches) == 1:
|
if len(matches) == 1:
|
||||||
return matches[0]
|
return matches[0]
|
||||||
elif len(matches) > 1:
|
elif len(matches) > 1:
|
||||||
@@ -272,35 +271,27 @@ class BaseApp:
|
|||||||
Private. Sets up default app view
|
Private. Sets up default app view
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._prompt = "[italic dim bold]What do you want to do?\n"
|
self._prompt = f"[italic dim bold]{self._prompt}"
|
||||||
self._initial_message = (
|
self._initial_message = ("\n" + f"[bold red]{text2art(self._initial_message, font='tarty1')}" + "\n")
|
||||||
f"\n[bold red]{text2art(self._initial_message, font='tarty1')}\n"
|
|
||||||
)
|
|
||||||
self._farewell_message = (
|
self._farewell_message = (
|
||||||
f"[bold red]\n{text2art(f'\n{self._farewell_message}\n', font='chanky')}[/bold red]\n"
|
"[bold red]\n\n" +
|
||||||
f"[red i]github.com/koloideal/Argenta[/red i] | [red bold i]made by kolo[/red bold i]\n"
|
str(text2art(self._farewell_message, font="chanky")) + # pyright: ignore[reportUnknownArgumentType]
|
||||||
|
"\n[/bold red]\n" +
|
||||||
|
"[red i]github.com/koloideal/Argenta[/red i] | [red bold i]made by kolo[/red bold i]\n"
|
||||||
)
|
)
|
||||||
self._description_message_gen = lambda command, description: (
|
self._description_message_gen = lambda command, description: (
|
||||||
f"[bold red]{escape('[' + command + ']')}[/bold red] "
|
f"[bold red]{escape('[' + command + ']')}[/bold red] "
|
||||||
f"[blue dim]*=*=*[/blue dim] "
|
f"[blue dim]*=*=*[/blue dim] "
|
||||||
f"[bold yellow italic]{escape(description)}"
|
f"[bold yellow italic]{escape(description)}"
|
||||||
)
|
)
|
||||||
self._incorrect_input_syntax_handler = lambda raw_command: self._print_func(
|
self._incorrect_input_syntax_handler = lambda raw_command: self._print_func(f"[red bold]Incorrect flag syntax: {escape(raw_command)}")
|
||||||
f"[red bold]Incorrect flag syntax: {escape(raw_command)}"
|
self._repeated_input_flags_handler = lambda raw_command: self._print_func(f"[red bold]Repeated input flags: {escape(raw_command)}")
|
||||||
)
|
self._empty_input_command_handler = lambda: self._print_func("[red bold]Empty input command")
|
||||||
self._repeated_input_flags_handler = lambda raw_command: self._print_func(
|
|
||||||
f"[red bold]Repeated input flags: {escape(raw_command)}"
|
|
||||||
)
|
|
||||||
self._empty_input_command_handler = lambda: self._print_func(
|
|
||||||
"[red bold]Empty input command"
|
|
||||||
)
|
|
||||||
|
|
||||||
def unknown_command_handler(command: InputCommand) -> None:
|
def unknown_command_handler(command: InputCommand) -> None:
|
||||||
cmd_trg: str = command.get_trigger()
|
cmd_trg: str = command.trigger
|
||||||
mst_sim_cmd: str | None = self._most_similar_command(cmd_trg)
|
mst_sim_cmd: str | None = self._most_similar_command(cmd_trg)
|
||||||
first_part_of_text = (
|
first_part_of_text = f"[red]Unknown command:[/red] [blue]{escape(cmd_trg)}[/blue]"
|
||||||
f"[red]Unknown command:[/red] [blue]{escape(cmd_trg)}[/blue]"
|
|
||||||
)
|
|
||||||
second_part_of_text = (
|
second_part_of_text = (
|
||||||
("[red], most similar:[/red] " + ("[blue]" + mst_sim_cmd + "[/blue]"))
|
("[red], most similar:[/red] " + ("[blue]" + mst_sim_cmd + "[/blue]"))
|
||||||
if mst_sim_cmd
|
if mst_sim_cmd
|
||||||
@@ -318,21 +309,22 @@ class BaseApp:
|
|||||||
self._setup_system_router()
|
self._setup_system_router()
|
||||||
|
|
||||||
for router_entity in self._registered_routers:
|
for router_entity in self._registered_routers:
|
||||||
self._all_registered_triggers_in_default_case.extend(
|
router_triggers = router_entity.triggers
|
||||||
router_entity.get_triggers()
|
router_aliases = router_entity.aliases
|
||||||
)
|
combined = router_triggers + router_aliases
|
||||||
self._all_registered_triggers_in_default_case.extend(
|
|
||||||
router_entity.get_aliases()
|
|
||||||
)
|
|
||||||
|
|
||||||
self._all_registered_triggers_in_lower.extend(
|
for trigger in combined:
|
||||||
[x.lower() for x in router_entity.get_triggers()]
|
self._matching_default_triggers_with_routers[trigger] = router_entity
|
||||||
)
|
self._matching_lower_triggers_with_routers[trigger.lower()] = router_entity
|
||||||
self._all_registered_triggers_in_lower.extend(
|
|
||||||
[x.lower() for x in router_entity.get_aliases()]
|
|
||||||
)
|
|
||||||
|
|
||||||
self._autocompleter.initial_setup(self._all_registered_triggers_in_lower)
|
self._autocompleter.initial_setup(list(self._current_matching_triggers_with_routers.keys()))
|
||||||
|
|
||||||
|
seen = {}
|
||||||
|
for item in list(self._current_matching_triggers_with_routers.keys()):
|
||||||
|
if item in seen:
|
||||||
|
Console().print(f"\n[b red]WARNING:[/b red] Overlapping trigger or alias: [b blue]{item}[/b blue]")
|
||||||
|
else:
|
||||||
|
seen[item] = True
|
||||||
|
|
||||||
if not self._override_system_messages:
|
if not self._override_system_messages:
|
||||||
self._setup_default_view()
|
self._setup_default_view()
|
||||||
@@ -343,25 +335,32 @@ class BaseApp:
|
|||||||
self._print_func(message)
|
self._print_func(message)
|
||||||
if self._messages_on_startup:
|
if self._messages_on_startup:
|
||||||
print("\n")
|
print("\n")
|
||||||
|
|
||||||
if not self._repeat_command_groups_description:
|
if not self._repeat_command_groups_description:
|
||||||
self._print_command_group_description()
|
self._print_command_group_description()
|
||||||
|
|
||||||
|
|
||||||
|
AVAILABLE_DIVIDING_LINES: TypeAlias = StaticDividingLine | DynamicDividingLine
|
||||||
|
DEFAULT_DIVIDING_LINE: StaticDividingLine = StaticDividingLine()
|
||||||
|
|
||||||
|
DEFAULT_PRINT_FUNC: Printer = Console().print
|
||||||
|
DEFAULT_AUTOCOMPLETER: AutoCompleter = AutoCompleter()
|
||||||
|
DEFAULT_EXIT_COMMAND: Command = Command("Q", description="Exit command")
|
||||||
|
|
||||||
|
|
||||||
class App(BaseApp):
|
class App(BaseApp):
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, *,
|
||||||
prompt: str = "What do you want to do?\n",
|
prompt: str = "What do you want to do?\n\n",
|
||||||
initial_message: str = "\nArgenta\n",
|
initial_message: str = "Argenta\n",
|
||||||
farewell_message: str = "\nSee you\n",
|
farewell_message: str = "\nSee you\n",
|
||||||
exit_command: Command = Command("Q", "Exit command"),
|
exit_command: Command = DEFAULT_EXIT_COMMAND,
|
||||||
system_router_title: str | None = "System points:",
|
system_router_title: str | None = "System points:",
|
||||||
ignore_command_register: bool = True,
|
ignore_command_register: bool = True,
|
||||||
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
|
dividing_line: AVAILABLE_DIVIDING_LINES = DEFAULT_DIVIDING_LINE,
|
||||||
repeat_command_groups: bool = True,
|
repeat_command_groups: bool = True,
|
||||||
override_system_messages: bool = False,
|
override_system_messages: bool = False,
|
||||||
autocompleter: AutoCompleter = AutoCompleter(),
|
autocompleter: AutoCompleter = DEFAULT_AUTOCOMPLETER,
|
||||||
print_func: Callable[[str], None] = Console().print,
|
print_func: Printer = DEFAULT_PRINT_FUNC,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Public. The essence of the application itself.
|
Public. The essence of the application itself.
|
||||||
@@ -406,40 +405,44 @@ class App(BaseApp):
|
|||||||
raw_command: str = Console().input(self._prompt)
|
raw_command: str = Console().input(self._prompt)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
input_command: InputCommand = InputCommand.parse(
|
input_command: InputCommand = InputCommand.parse(raw_command=raw_command)
|
||||||
raw_command=raw_command
|
except InputCommandException as error:
|
||||||
)
|
with redirect_stdout(io.StringIO()) as stderr:
|
||||||
except BaseInputCommandException as error:
|
|
||||||
with redirect_stdout(io.StringIO()) as f:
|
|
||||||
self._error_handler(error, raw_command)
|
self._error_handler(error, raw_command)
|
||||||
res: str = f.getvalue()
|
stderr_result: str = stderr.getvalue()
|
||||||
self._print_framed_text(res)
|
self._print_framed_text(stderr_result)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if self._is_exit_command(input_command):
|
if self._is_exit_command(input_command):
|
||||||
system_router.finds_appropriate_handler(input_command)
|
system_router.finds_appropriate_handler(input_command)
|
||||||
if self._ignore_command_register:
|
self._autocompleter.exit_setup(list(self._current_matching_triggers_with_routers.keys()))
|
||||||
self._autocompleter.exit_setup(
|
|
||||||
self._all_registered_triggers_in_lower
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
self._autocompleter.exit_setup(
|
|
||||||
self._all_registered_triggers_in_default_case
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if self._is_unknown_command(input_command):
|
if self._is_unknown_command(input_command):
|
||||||
with redirect_stdout(io.StringIO()) as f:
|
with redirect_stdout(io.StringIO()) as stdout:
|
||||||
self._unknown_command_handler(input_command)
|
self._unknown_command_handler(input_command)
|
||||||
res: str = f.getvalue()
|
stdout_res: str = stdout.getvalue()
|
||||||
self._print_framed_text(res)
|
self._print_framed_text(stdout_res)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
with redirect_stdout(io.StringIO()) as f:
|
processing_router = self._current_matching_triggers_with_routers[input_command.trigger.lower()]
|
||||||
for registered_router in self._registered_routers:
|
|
||||||
registered_router.finds_appropriate_handler(input_command)
|
if processing_router.disable_redirect_stdout:
|
||||||
res: str = f.getvalue()
|
if isinstance(self._dividing_line, StaticDividingLine):
|
||||||
self._print_framed_text(res)
|
self._print_func(self._dividing_line.get_full_static_line(is_override=self._override_system_messages))
|
||||||
|
processing_router.finds_appropriate_handler(input_command)
|
||||||
|
self._print_func(self._dividing_line.get_full_static_line(is_override=self._override_system_messages))
|
||||||
|
else:
|
||||||
|
dividing_line_unit_part: str = self._dividing_line.get_unit_part()
|
||||||
|
self._print_func(StaticDividingLine(dividing_line_unit_part).get_full_static_line(is_override=self._override_system_messages))
|
||||||
|
processing_router.finds_appropriate_handler(input_command)
|
||||||
|
self._print_func(StaticDividingLine(dividing_line_unit_part).get_full_static_line(is_override=self._override_system_messages))
|
||||||
|
else:
|
||||||
|
with redirect_stdout(io.StringIO()) as stdout:
|
||||||
|
processing_router.finds_appropriate_handler(input_command)
|
||||||
|
stdout_result: str = stdout.getvalue()
|
||||||
|
if stdout_result:
|
||||||
|
self._print_framed_text(stdout_result)
|
||||||
|
|
||||||
def include_router(self, router: Router) -> None:
|
def include_router(self, router: Router) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -447,7 +450,7 @@ class App(BaseApp):
|
|||||||
:param router: registered router
|
:param router: registered router
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
router.set_command_register_ignore(self._ignore_command_register)
|
router.command_register_ignore = self._ignore_command_register
|
||||||
self._registered_routers.add_registered_router(router)
|
self._registered_routers.add_registered_router(router)
|
||||||
|
|
||||||
def include_routers(self, *routers: Router) -> None:
|
def include_routers(self, *routers: Router) -> None:
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
from typing import Protocol, TypeVar
|
||||||
|
|
||||||
|
T = TypeVar('T', contravariant=True) # noqa: WPS111
|
||||||
|
|
||||||
|
|
||||||
|
class NonStandardBehaviorHandler(Protocol[T]):
|
||||||
|
def __call__(self, __param: T) -> None:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
class EmptyCommandHandler(Protocol):
|
||||||
|
def __call__(self) -> None:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class Printer(Protocol):
|
||||||
|
def __call__(self, __text: str) -> None:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
|
class DescriptionMessageGenerator(Protocol):
|
||||||
|
def __call__(self, __first_param: str, __second_param: str) -> str:
|
||||||
|
raise NotImplementedError
|
||||||
@@ -1,34 +1,27 @@
|
|||||||
from typing import Iterator
|
from typing import Iterator, Optional
|
||||||
|
|
||||||
from argenta.router import Router
|
from argenta.router import Router
|
||||||
|
|
||||||
|
|
||||||
class RegisteredRouters:
|
class RegisteredRouters:
|
||||||
def __init__(self, registered_routers: list[Router] = None) -> None:
|
def __init__(self, registered_routers: Optional[list[Router]] = None) -> None:
|
||||||
"""
|
"""
|
||||||
Private. Combines registered routers
|
Private. Combines registered routers
|
||||||
:param registered_routers: list of the registered routers
|
:param registered_routers: list of the registered routers
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._registered_routers = registered_routers if registered_routers else []
|
self.registered_routers: list[Router] = registered_routers if registered_routers else []
|
||||||
|
|
||||||
def get_registered_routers(self) -> list[Router]:
|
def add_registered_router(self, router: Router, /) -> None:
|
||||||
"""
|
|
||||||
Private. Returns the registered routers
|
|
||||||
:return: registered routers as list[Router]
|
|
||||||
"""
|
|
||||||
return self._registered_routers
|
|
||||||
|
|
||||||
def add_registered_router(self, router: Router) -> None:
|
|
||||||
"""
|
"""
|
||||||
Private. Adds a new registered router
|
Private. Adds a new registered router
|
||||||
:param router: registered router
|
:param router: registered router
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._registered_routers.append(router)
|
self.registered_routers.append(router)
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[Router]:
|
def __iter__(self) -> Iterator[Router]:
|
||||||
return iter(self._registered_routers)
|
return iter(self.registered_routers)
|
||||||
|
|
||||||
def __next__(self) -> Router:
|
def __next__(self) -> Router:
|
||||||
return next(iter(self._registered_routers))
|
return next(iter(self.registered_routers))
|
||||||
|
|||||||
@@ -1,3 +1,12 @@
|
|||||||
__all__ = ["Command"]
|
__all__ = [
|
||||||
|
"Command",
|
||||||
|
"PossibleValues",
|
||||||
|
"PredefinedFlags",
|
||||||
|
"InputCommand",
|
||||||
|
"Flags",
|
||||||
|
"Flag"
|
||||||
|
]
|
||||||
|
|
||||||
from argenta.command.models import Command
|
from argenta.command.models import Command, InputCommand
|
||||||
|
from argenta.command.flag import defaults as PredefinedFlags
|
||||||
|
from argenta.command.flag import (Flag, Flags, PossibleValues)
|
||||||
|
|||||||
@@ -1,42 +1,49 @@
|
|||||||
from argenta.command.flag.models import Flag, InputFlag
|
from argenta.command.flag.models import Flag, InputFlag
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import override
|
||||||
|
|
||||||
|
|
||||||
class BaseInputCommandException(Exception):
|
class InputCommandException(ABC, Exception):
|
||||||
"""
|
"""
|
||||||
Private. Base exception class for all exceptions raised when parse input command
|
Private. Base exception class for all exceptions raised when parse input command
|
||||||
"""
|
"""
|
||||||
|
@override
|
||||||
pass
|
@abstractmethod
|
||||||
|
def __str__(self) -> str:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class UnprocessedInputFlagException(BaseInputCommandException):
|
class UnprocessedInputFlagException(InputCommandException):
|
||||||
"""
|
"""
|
||||||
Private. Raised when an unprocessed input flag is detected
|
Private. Raised when an unprocessed input flag is detected
|
||||||
"""
|
"""
|
||||||
|
@override
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "Unprocessed Input Flags"
|
return "Unprocessed Input Flags"
|
||||||
|
|
||||||
|
|
||||||
class RepeatedInputFlagsException(BaseInputCommandException):
|
class RepeatedInputFlagsException(InputCommandException):
|
||||||
"""
|
"""
|
||||||
Private. Raised when repeated input flags are detected
|
Private. Raised when repeated input flags are detected
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, flag: Flag | InputFlag):
|
def __init__(self, flag: Flag | InputFlag):
|
||||||
self.flag = flag
|
self.flag: Flag | InputFlag = flag
|
||||||
|
super().__init__()
|
||||||
|
|
||||||
def __str__(self):
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
string_entity: str = self.flag.string_entity
|
||||||
return (
|
return (
|
||||||
"Repeated Input Flags\n"
|
"Repeated Input Flags\n"
|
||||||
f"Duplicate flag was detected in the input: '{self.flag.get_string_entity()}'"
|
f"Duplicate flag was detected in the input: '{string_entity}'"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class EmptyInputCommandException(BaseInputCommandException):
|
class EmptyInputCommandException(InputCommandException):
|
||||||
"""
|
"""
|
||||||
Private. Raised when an empty input command is detected
|
Private. Raised when an empty input command is detected
|
||||||
"""
|
"""
|
||||||
|
@override
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "Input Command is empty"
|
return "Input Command is empty"
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
__all__ = ["Flag", "InputFlag"]
|
__all__ = [
|
||||||
|
"Flag",
|
||||||
|
"InputFlag",
|
||||||
|
"Flags",
|
||||||
|
"PossibleValues",
|
||||||
|
"ValidationStatus"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
from argenta.command.flag.models import Flag, InputFlag
|
from argenta.command.flag.models import Flag, InputFlag, PossibleValues, ValidationStatus
|
||||||
|
from argenta.command.flag.flags.models import Flags
|
||||||
|
|||||||
@@ -1,31 +1,27 @@
|
|||||||
from dataclasses import dataclass
|
from typing import Literal
|
||||||
from argenta.command.flag.models import Flag
|
from argenta.command.flag.models import Flag, PossibleValues
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
DEFAULT_PREFIX: Literal["-", "--", "---"] = "-"
|
||||||
class PredefinedFlags:
|
|
||||||
"""
|
|
||||||
Public. A dataclass with predefined flags and most frequently used flags for quick use
|
|
||||||
"""
|
|
||||||
|
|
||||||
HELP = Flag(name="help", possible_values=False)
|
HELP = Flag(name="help", possible_values=PossibleValues.NEITHER)
|
||||||
SHORT_HELP = Flag(name="H", prefix="-", possible_values=False)
|
SHORT_HELP = Flag(name="H", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||||
|
|
||||||
INFO = Flag(name="info", possible_values=False)
|
INFO = Flag(name="info", possible_values=PossibleValues.NEITHER) # noqa: WPS110
|
||||||
SHORT_INFO = Flag(name="I", prefix="-", possible_values=False)
|
SHORT_INFO = Flag(name="I", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||||
|
|
||||||
ALL = Flag(name="all", possible_values=False)
|
ALL = Flag(name="all", possible_values=PossibleValues.NEITHER)
|
||||||
SHORT_ALL = Flag(name="A", prefix="-", possible_values=False)
|
SHORT_ALL = Flag(name="A", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||||
|
|
||||||
HOST = Flag(
|
HOST = Flag(
|
||||||
name="host", possible_values=re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
|
name="host", possible_values=re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$")
|
||||||
)
|
)
|
||||||
SHORT_HOST = Flag(
|
SHORT_HOST = Flag(
|
||||||
name="H",
|
name="H",
|
||||||
prefix="-",
|
prefix=DEFAULT_PREFIX,
|
||||||
possible_values=re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"),
|
possible_values=re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"),
|
||||||
)
|
)
|
||||||
|
|
||||||
PORT = Flag(name="port", possible_values=re.compile(r"^\d{1,5}$"))
|
PORT = Flag(name="port", possible_values=re.compile(r"^\d{1,5}$"))
|
||||||
SHORT_PORT = Flag(name="P", prefix="-", possible_values=re.compile(r"^\d{1,5}$"))
|
SHORT_PORT = Flag(name="P", prefix=DEFAULT_PREFIX, possible_values=re.compile(r"^\d{1,5}$"))
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
__all__ = [
|
||||||
|
"Flags",
|
||||||
|
"InputFlags"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
from argenta.command.flag.flags.models import (
|
||||||
|
Flags,
|
||||||
|
InputFlags
|
||||||
|
)
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
from argenta.command.flag.models import InputFlag, Flag
|
||||||
|
from typing import Generic, TypeVar, override
|
||||||
|
from collections.abc import Iterator
|
||||||
|
|
||||||
|
|
||||||
|
FlagType = TypeVar("FlagType")
|
||||||
|
|
||||||
|
|
||||||
|
class BaseFlags(Generic[FlagType]):
|
||||||
|
def __init__(self, flags: list[FlagType] | None = None) -> None:
|
||||||
|
"""
|
||||||
|
Public. A model that combines the registered flags
|
||||||
|
:param flags: the flags that will be registered
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.flags: list[FlagType] = flags if flags else []
|
||||||
|
|
||||||
|
def add_flag(self, flag: FlagType) -> None:
|
||||||
|
"""
|
||||||
|
Public. Adds a flag to the list of flags
|
||||||
|
:param flag: flag to add
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.flags.append(flag)
|
||||||
|
|
||||||
|
def add_flags(self, flags: list[FlagType]) -> None:
|
||||||
|
"""
|
||||||
|
Public. Adds a list of flags to the list of flags
|
||||||
|
:param flags: list of flags to add
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
self.flags.extend(flags)
|
||||||
|
|
||||||
|
def __iter__(self) -> Iterator[FlagType]:
|
||||||
|
return iter(self.flags)
|
||||||
|
|
||||||
|
def __next__(self) -> FlagType:
|
||||||
|
return next(iter(self))
|
||||||
|
|
||||||
|
def __getitem__(self, flag_index: int) -> FlagType:
|
||||||
|
return self.flags[flag_index]
|
||||||
|
|
||||||
|
def __bool__(self) -> bool:
|
||||||
|
return bool(self.flags)
|
||||||
|
|
||||||
|
|
||||||
|
class Flags(BaseFlags[Flag]):
|
||||||
|
def get_flag_by_name(self, name: str) -> Flag | None:
|
||||||
|
"""
|
||||||
|
Public. Returns the flag entity by its name or None if not found
|
||||||
|
:param name: the name of the flag to get
|
||||||
|
:return: entity of the flag or None
|
||||||
|
"""
|
||||||
|
return next((flag for flag in self.flags if flag.name == name), None)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if not isinstance(other, Flags):
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
if len(self.flags) != len(other.flags):
|
||||||
|
return False
|
||||||
|
|
||||||
|
flag_pairs: zip[tuple[Flag, Flag]] = zip(self.flags, other.flags)
|
||||||
|
return all(s_flag == o_flag for s_flag, o_flag in flag_pairs)
|
||||||
|
|
||||||
|
def __contains__(self, flag_to_check: object) -> bool:
|
||||||
|
if isinstance(flag_to_check, Flag):
|
||||||
|
for flag in self.flags:
|
||||||
|
if flag == flag_to_check:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
|
|
||||||
|
class InputFlags(BaseFlags[InputFlag]):
|
||||||
|
def get_flag_by_name(self, name: str) -> InputFlag | None:
|
||||||
|
"""
|
||||||
|
Public. Returns the flag entity by its name or None if not found
|
||||||
|
:param name: the name of the flag to get
|
||||||
|
:return: entity of the flag or None
|
||||||
|
"""
|
||||||
|
return next((flag for flag in self.flags if flag.name == name), None)
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if not isinstance(other, InputFlags):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
if len(self.flags) != len(other.flags):
|
||||||
|
return False
|
||||||
|
|
||||||
|
paired_flags: zip[tuple[InputFlag, InputFlag]] = zip(self.flags, other.flags)
|
||||||
|
|
||||||
|
return all(my_flag == other_flag for my_flag, other_flag in paired_flags)
|
||||||
|
|
||||||
|
def __contains__(self, ingressable_item: object) -> bool:
|
||||||
|
if isinstance(ingressable_item, InputFlag):
|
||||||
|
for flag in self.flags:
|
||||||
|
if flag == ingressable_item:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
|
||||||
@@ -1,49 +1,24 @@
|
|||||||
from typing import Literal, Pattern
|
from enum import Enum
|
||||||
|
from re import Pattern
|
||||||
|
from typing import Literal, override
|
||||||
|
|
||||||
|
|
||||||
class BaseFlag:
|
class PossibleValues(Enum):
|
||||||
def __init__(self, name: str, prefix: Literal["-", "--", "---"] = "--") -> None:
|
NEITHER = 'NEITHER'
|
||||||
"""
|
ALL = 'ALL'
|
||||||
Private. Base class for flags
|
|
||||||
:param name: the name of the flag
|
|
||||||
:param prefix: the prefix of the flag
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._name = name
|
|
||||||
self._prefix = prefix
|
|
||||||
|
|
||||||
def get_string_entity(self) -> str:
|
|
||||||
"""
|
|
||||||
Public. Returns a string representation of the flag
|
|
||||||
:return: string representation of the flag as str
|
|
||||||
"""
|
|
||||||
string_entity: str = self._prefix + self._name
|
|
||||||
return string_entity
|
|
||||||
|
|
||||||
def get_name(self) -> str:
|
|
||||||
"""
|
|
||||||
Public. Returns the name of the flag
|
|
||||||
:return: the name of the flag as str
|
|
||||||
"""
|
|
||||||
return self._name
|
|
||||||
|
|
||||||
def get_prefix(self) -> str:
|
|
||||||
"""
|
|
||||||
Public. Returns the prefix of the flag
|
|
||||||
:return: the prefix of the flag as str
|
|
||||||
"""
|
|
||||||
return self._prefix
|
|
||||||
|
|
||||||
def __eq__(self, other) -> bool:
|
|
||||||
return self.get_string_entity() == other.get_string_entity()
|
|
||||||
|
|
||||||
|
|
||||||
class Flag(BaseFlag):
|
class ValidationStatus(Enum):
|
||||||
|
VALID = 'VALID'
|
||||||
|
INVALID = 'INVALID'
|
||||||
|
UNDEFINED = 'UNDEFINED'
|
||||||
|
|
||||||
|
|
||||||
|
class Flag:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self, name: str, *,
|
||||||
name: str,
|
|
||||||
prefix: Literal["-", "--", "---"] = "--",
|
prefix: Literal["-", "--", "---"] = "--",
|
||||||
possible_values: list[str] | Pattern[str] | False = True,
|
possible_values: list[str] | Pattern[str] | PossibleValues = PossibleValues.ALL,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""
|
"""
|
||||||
Public. The entity of the flag being registered for subsequent processing
|
Public. The entity of the flag being registered for subsequent processing
|
||||||
@@ -52,42 +27,58 @@ class Flag(BaseFlag):
|
|||||||
:param possible_values: The possible values of the flag, if False then the flag cannot have a value
|
:param possible_values: The possible values of the flag, if False then the flag cannot have a value
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
super().__init__(name, prefix)
|
self.name: str = name
|
||||||
self.possible_values = possible_values
|
self.prefix: Literal["-", "--", "---"] = prefix
|
||||||
|
self.possible_values: list[str] | Pattern[str] | PossibleValues = possible_values
|
||||||
|
|
||||||
def validate_input_flag_value(self, input_flag_value: str | None):
|
def validate_input_flag_value(self, input_flag_value: str | None) -> bool:
|
||||||
"""
|
"""
|
||||||
Private. Validates the input flag value
|
Private. Validates the input flag value
|
||||||
:param input_flag_value: The input flag value to validate
|
:param input_flag_value: The input flag value to validate
|
||||||
:return: whether the entered flag is valid as bool
|
:return: whether the entered flag is valid as bool
|
||||||
"""
|
"""
|
||||||
if self.possible_values is False:
|
if self.possible_values == PossibleValues.NEITHER:
|
||||||
if input_flag_value is None:
|
return input_flag_value is None
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
elif isinstance(self.possible_values, Pattern):
|
|
||||||
if isinstance(input_flag_value, str):
|
|
||||||
is_valid = bool(self.possible_values.match(input_flag_value))
|
|
||||||
if bool(is_valid):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
elif isinstance(self.possible_values, list):
|
if isinstance(self.possible_values, Pattern):
|
||||||
if input_flag_value in self.possible_values:
|
return isinstance(input_flag_value, str) and bool(self.possible_values.match(input_flag_value))
|
||||||
return True
|
|
||||||
else:
|
if isinstance(self.possible_values, list):
|
||||||
return False
|
return input_flag_value in self.possible_values
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def string_entity(self) -> str:
|
||||||
|
"""
|
||||||
|
Public. Returns a string representation of the flag
|
||||||
|
:return: string representation of the flag as str
|
||||||
|
"""
|
||||||
|
string_entity: str = self.prefix + self.name
|
||||||
|
return string_entity
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return self.string_entity
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
return f'Flag<name={self.name}, prefix={self.prefix}>'
|
||||||
|
|
||||||
|
@override
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if isinstance(other, Flag):
|
||||||
|
return self.string_entity == other.string_entity
|
||||||
else:
|
else:
|
||||||
return True
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class InputFlag(BaseFlag):
|
class InputFlag:
|
||||||
def __init__(
|
def __init__(
|
||||||
self, name: str, prefix: Literal["-", "--", "---"] = "--", value: str = None
|
self, name: str, *,
|
||||||
|
prefix: Literal['-', '--', '---'] = '--',
|
||||||
|
input_value: str | None,
|
||||||
|
status: ValidationStatus | None
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Public. The entity of the flag of the entered command
|
Public. The entity of the flag of the entered command
|
||||||
@@ -96,26 +87,33 @@ class InputFlag(BaseFlag):
|
|||||||
:param value: the value of the input flag
|
:param value: the value of the input flag
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
super().__init__(name, prefix)
|
self.name: str = name
|
||||||
self._flag_value = value
|
self.prefix: Literal['-', '--', '---'] = prefix
|
||||||
|
self.input_value: str | None = input_value
|
||||||
|
self.status: ValidationStatus | None = status
|
||||||
|
|
||||||
|
@property
|
||||||
|
def string_entity(self) -> str:
|
||||||
|
"""
|
||||||
|
Public. Returns a string representation of the flag
|
||||||
|
:return: string representation of the flag as str
|
||||||
|
"""
|
||||||
|
string_entity: str = self.prefix + self.name
|
||||||
|
return string_entity
|
||||||
|
|
||||||
def get_value(self) -> str | None:
|
@override
|
||||||
"""
|
def __str__(self) -> str:
|
||||||
Public. Returns the value of the flag
|
return f'{self.string_entity} {self.input_value}'
|
||||||
:return: the value of the flag as str
|
|
||||||
"""
|
@override
|
||||||
return self._flag_value
|
def __repr__(self) -> str:
|
||||||
|
return f'InputFlag<name={self.name}, prefix={self.prefix}, value={self.input_value}, status={self.status}>'
|
||||||
|
|
||||||
def set_value(self, value):
|
@override
|
||||||
"""
|
def __eq__(self, other: object) -> bool:
|
||||||
Private. Sets the value of the flag
|
if isinstance(other, InputFlag):
|
||||||
:param value: the fag value to set
|
return (
|
||||||
:return: None
|
self.name == other.name
|
||||||
"""
|
)
|
||||||
self._flag_value = value
|
else:
|
||||||
|
raise NotImplementedError
|
||||||
def __eq__(self, other) -> bool:
|
|
||||||
return (
|
|
||||||
self.get_string_entity() == other.get_string_entity()
|
|
||||||
and self.get_value() == other.get_value()
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
__all__ = [
|
|
||||||
"Flags",
|
|
||||||
"InputFlags",
|
|
||||||
"UndefinedInputFlags",
|
|
||||||
"InvalidValueInputFlags",
|
|
||||||
"ValidInputFlags",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
from argenta.command.flags.models import (
|
|
||||||
Flags,
|
|
||||||
InputFlags,
|
|
||||||
UndefinedInputFlags,
|
|
||||||
InvalidValueInputFlags,
|
|
||||||
ValidInputFlags,
|
|
||||||
)
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
from argenta.command.flag.models import InputFlag, Flag
|
|
||||||
from typing import Generic, TypeVar
|
|
||||||
|
|
||||||
|
|
||||||
FlagType = TypeVar("FlagType")
|
|
||||||
|
|
||||||
|
|
||||||
class BaseFlags(Generic[FlagType]):
|
|
||||||
def __init__(self, *flags: FlagType):
|
|
||||||
"""
|
|
||||||
Public. A model that combines the registered flags
|
|
||||||
:param flags: the flags that will be registered
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._flags = flags if flags else []
|
|
||||||
|
|
||||||
def get_flags(self) -> list[FlagType]:
|
|
||||||
"""
|
|
||||||
Public. Returns a list of flags
|
|
||||||
:return: list of flags as list[FlagType]
|
|
||||||
"""
|
|
||||||
return self._flags
|
|
||||||
|
|
||||||
def add_flag(self, flag: FlagType):
|
|
||||||
"""
|
|
||||||
Public. Adds a flag to the list of flags
|
|
||||||
:param flag: flag to add
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._flags.append(flag)
|
|
||||||
|
|
||||||
def add_flags(self, flags: list[FlagType]):
|
|
||||||
"""
|
|
||||||
Public. Adds a list of flags to the list of flags
|
|
||||||
:param flags: list of flags to add
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._flags.extend(flags)
|
|
||||||
|
|
||||||
def get_flag(self, name: str) -> FlagType | None:
|
|
||||||
"""
|
|
||||||
Public. Returns the flag entity by its name or None if not found
|
|
||||||
:param name: the name of the flag to get
|
|
||||||
:return: entity of the flag or None
|
|
||||||
"""
|
|
||||||
if name in [flag.get_name() for flag in self._flags]:
|
|
||||||
return list(filter(lambda flag: flag.get_name() == name, self._flags))[0]
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(self._flags)
|
|
||||||
|
|
||||||
def __next__(self):
|
|
||||||
return next(iter(self))
|
|
||||||
|
|
||||||
def __getitem__(self, item):
|
|
||||||
return self._flags[item]
|
|
||||||
|
|
||||||
def __bool__(self):
|
|
||||||
return bool(self._flags)
|
|
||||||
|
|
||||||
def __eq__(self, other):
|
|
||||||
if len(self.get_flags()) != len(other.get_flags()):
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
for flag, other_flag in zip(self.get_flags(), other.get_flags()):
|
|
||||||
if not flag == other_flag:
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
class Flags(BaseFlags[Flag]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InputFlags(BaseFlags[InputFlag]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class ValidInputFlags(InputFlags):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class UndefinedInputFlags(InputFlags):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidValueInputFlags(InputFlags):
|
|
||||||
pass
|
|
||||||
+104
-150
@@ -1,39 +1,29 @@
|
|||||||
from argenta.command.flag.models import Flag, InputFlag
|
from argenta.command.flag.models import Flag, InputFlag, ValidationStatus
|
||||||
from argenta.command.flags.models import InputFlags, Flags
|
from argenta.command.flag.flags.models import InputFlags, Flags
|
||||||
from argenta.command.exceptions import (
|
from argenta.command.exceptions import (
|
||||||
UnprocessedInputFlagException,
|
UnprocessedInputFlagException,
|
||||||
RepeatedInputFlagsException,
|
RepeatedInputFlagsException,
|
||||||
EmptyInputCommandException,
|
EmptyInputCommandException,
|
||||||
)
|
)
|
||||||
from typing import Generic, TypeVar, cast, Literal
|
from typing import Never, Self, cast, Literal
|
||||||
|
|
||||||
|
|
||||||
InputCommandType = TypeVar("InputCommandType")
|
ParseFlagsResult = tuple[InputFlags, str | None, str | None]
|
||||||
|
ParseResult = tuple[str, InputFlags]
|
||||||
|
|
||||||
|
MIN_FLAG_PREFIX: str = "-"
|
||||||
|
DEFAULT_WITHOUT_FLAGS: Flags = Flags()
|
||||||
|
|
||||||
|
DEFAULT_WITHOUT_INPUT_FLAGS: InputFlags = InputFlags()
|
||||||
|
|
||||||
|
|
||||||
class BaseCommand:
|
class Command:
|
||||||
def __init__(self, trigger: str) -> None:
|
|
||||||
"""
|
|
||||||
Private. Base class for all commands
|
|
||||||
:param trigger: A string trigger, which, when entered by the user, indicates that the input corresponds to the command
|
|
||||||
"""
|
|
||||||
self._trigger = trigger
|
|
||||||
|
|
||||||
def get_trigger(self) -> str:
|
|
||||||
"""
|
|
||||||
Public. Returns the trigger of the command
|
|
||||||
:return: the trigger of the command as str
|
|
||||||
"""
|
|
||||||
return self._trigger
|
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
trigger: str,
|
trigger: str, *,
|
||||||
description: str = None,
|
description: str | None = None,
|
||||||
flags: Flag | Flags = None,
|
flags: Flag | Flags = DEFAULT_WITHOUT_FLAGS,
|
||||||
aliases: list[str] = None,
|
aliases: list[str] | None = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Public. The command that can and should be registered in the Router
|
Public. The command that can and should be registered in the Router
|
||||||
@@ -42,156 +32,120 @@ class Command(BaseCommand):
|
|||||||
:param flags: processed commands
|
:param flags: processed commands
|
||||||
:param aliases: string synonyms for the main trigger
|
:param aliases: string synonyms for the main trigger
|
||||||
"""
|
"""
|
||||||
super().__init__(trigger)
|
self.registered_flags: Flags = flags if isinstance(flags, Flags) else Flags([flags])
|
||||||
self._registered_flags: Flags = (
|
self.trigger: str = trigger
|
||||||
flags
|
self.description: str = description if description else "Command without description"
|
||||||
if isinstance(flags, Flags)
|
self.aliases: list[str] = aliases if aliases else []
|
||||||
else Flags(flags)
|
|
||||||
if isinstance(flags, Flag)
|
|
||||||
else Flags()
|
|
||||||
)
|
|
||||||
self._description = "Very useful command" if not description else description
|
|
||||||
self._aliases = aliases if isinstance(aliases, list) else []
|
|
||||||
|
|
||||||
def get_registered_flags(self) -> Flags:
|
|
||||||
"""
|
|
||||||
Private. Returns the registered flags
|
|
||||||
:return: the registered flags as Flags
|
|
||||||
"""
|
|
||||||
return self._registered_flags
|
|
||||||
|
|
||||||
def get_aliases(self) -> list[str] | list:
|
|
||||||
"""
|
|
||||||
Public. Returns the aliases of the command
|
|
||||||
:return: the aliases of the command as list[str] | list
|
|
||||||
"""
|
|
||||||
return self._aliases
|
|
||||||
|
|
||||||
def validate_input_flag(
|
def validate_input_flag(
|
||||||
self, flag: InputFlag
|
self, flag: InputFlag
|
||||||
) -> Literal["Undefined", "Valid", "Invalid"]:
|
) -> ValidationStatus:
|
||||||
"""
|
"""
|
||||||
Private. Validates the input flag
|
Private. Validates the input flag
|
||||||
:param flag: input flag for validation
|
:param flag: input flag for validation
|
||||||
:return: is input flag valid as bool
|
:return: is input flag valid as bool
|
||||||
"""
|
"""
|
||||||
registered_flags: Flags | None = self.get_registered_flags()
|
registered_flags: Flags = self.registered_flags
|
||||||
if registered_flags:
|
for registered_flag in registered_flags:
|
||||||
if isinstance(registered_flags, Flag):
|
if registered_flag.string_entity == flag.string_entity:
|
||||||
if registered_flags.get_string_entity() == flag.get_string_entity():
|
is_valid = registered_flag.validate_input_flag_value(flag.input_value)
|
||||||
is_valid = registered_flags.validate_input_flag_value(
|
if is_valid:
|
||||||
flag.get_value()
|
return ValidationStatus.VALID
|
||||||
)
|
|
||||||
if is_valid:
|
|
||||||
return "Valid"
|
|
||||||
else:
|
|
||||||
return "Invalid"
|
|
||||||
else:
|
else:
|
||||||
return "Undefined"
|
return ValidationStatus.INVALID
|
||||||
else:
|
return ValidationStatus.UNDEFINED
|
||||||
for registered_flag in registered_flags:
|
|
||||||
if registered_flag.get_string_entity() == flag.get_string_entity():
|
|
||||||
is_valid = registered_flag.validate_input_flag_value(
|
|
||||||
flag.get_value()
|
|
||||||
)
|
|
||||||
if is_valid:
|
|
||||||
return "Valid"
|
|
||||||
else:
|
|
||||||
return "Invalid"
|
|
||||||
return "Undefined"
|
|
||||||
return "Undefined"
|
|
||||||
|
|
||||||
def get_description(self) -> str:
|
|
||||||
"""
|
|
||||||
Private. Returns the description of the command
|
|
||||||
:return: the description of the command as str
|
|
||||||
"""
|
|
||||||
return self._description
|
|
||||||
|
|
||||||
|
|
||||||
class InputCommand(BaseCommand, Generic[InputCommandType]):
|
class InputCommand:
|
||||||
def __init__(self, trigger: str, input_flags: InputFlag | InputFlags = None):
|
def __init__(self, trigger: str, *,
|
||||||
|
input_flags: InputFlag | InputFlags = DEFAULT_WITHOUT_INPUT_FLAGS):
|
||||||
"""
|
"""
|
||||||
Private. The model of the input command, after parsing
|
Private. The model of the input command, after parsing
|
||||||
:param trigger:the trigger of the command
|
:param trigger:the trigger of the command
|
||||||
:param input_flags: the input flags
|
:param input_flags: the input flags
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
super().__init__(trigger)
|
self.trigger: str = trigger
|
||||||
self._input_flags: InputFlags = (
|
self.input_flags: InputFlags = input_flags if isinstance(input_flags, InputFlags) else InputFlags([input_flags])
|
||||||
input_flags
|
|
||||||
if isinstance(input_flags, InputFlags)
|
|
||||||
else InputFlags(input_flags)
|
|
||||||
if isinstance(input_flags, InputFlag)
|
|
||||||
else InputFlags()
|
|
||||||
)
|
|
||||||
|
|
||||||
def _set_input_flags(self, input_flags: InputFlags) -> None:
|
@classmethod
|
||||||
"""
|
def parse(cls, raw_command: str) -> Self:
|
||||||
Private. Sets the input flags
|
|
||||||
:param input_flags: the input flags to set
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._input_flags = input_flags
|
|
||||||
|
|
||||||
def get_input_flags(self) -> InputFlags:
|
|
||||||
"""
|
|
||||||
Private. Returns the input flags
|
|
||||||
:return: the input flags as InputFlags
|
|
||||||
"""
|
|
||||||
return self._input_flags
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def parse(raw_command: str) -> InputCommandType:
|
|
||||||
"""
|
"""
|
||||||
Private. Parse the raw input command
|
Private. Parse the raw input command
|
||||||
:param raw_command: raw input command
|
:param raw_command: raw input command
|
||||||
:return: model of the input command, after parsing as InputCommand
|
:return: model of the input command, after parsing as InputCommand
|
||||||
"""
|
"""
|
||||||
if not raw_command:
|
trigger, input_flags = CommandParser(raw_command).parse_raw_command()
|
||||||
|
|
||||||
|
return cls(trigger=trigger, input_flags=input_flags)
|
||||||
|
|
||||||
|
|
||||||
|
class CommandParser:
|
||||||
|
def __init__(self, raw_command: str) -> None:
|
||||||
|
self.raw_command: str = raw_command
|
||||||
|
self._parsed_input_flags: InputFlags = InputFlags()
|
||||||
|
|
||||||
|
def parse_raw_command(self) -> ParseResult:
|
||||||
|
if not self.raw_command:
|
||||||
raise EmptyInputCommandException()
|
raise EmptyInputCommandException()
|
||||||
|
|
||||||
list_of_tokens = raw_command.split()
|
input_flags, crnt_flag_name, crnt_flag_val = self._parse_flags(self.raw_command.split()[1:])
|
||||||
command = list_of_tokens.pop(0)
|
|
||||||
|
|
||||||
input_flags: InputFlags = InputFlags()
|
if any([crnt_flag_name, crnt_flag_val]):
|
||||||
current_flag_name, current_flag_value = None, None
|
|
||||||
|
|
||||||
for k, _ in enumerate(list_of_tokens):
|
|
||||||
if _.startswith("-"):
|
|
||||||
if len(_) < 2 or len(_[: _.rfind("-")]) > 3:
|
|
||||||
raise UnprocessedInputFlagException()
|
|
||||||
current_flag_name = _
|
|
||||||
else:
|
|
||||||
if not current_flag_name or current_flag_value:
|
|
||||||
raise UnprocessedInputFlagException()
|
|
||||||
current_flag_value = _
|
|
||||||
|
|
||||||
if current_flag_name:
|
|
||||||
if not len(list_of_tokens) == k + 1:
|
|
||||||
if not list_of_tokens[k + 1].startswith("-"):
|
|
||||||
continue
|
|
||||||
|
|
||||||
input_flag = InputFlag(
|
|
||||||
name=current_flag_name[current_flag_name.rfind("-") + 1 :],
|
|
||||||
prefix=cast(
|
|
||||||
Literal["-", "--", "---"],
|
|
||||||
current_flag_name[: current_flag_name.rfind("-") + 1],
|
|
||||||
),
|
|
||||||
value=current_flag_value,
|
|
||||||
)
|
|
||||||
|
|
||||||
all_flags = [
|
|
||||||
flag.get_string_entity() for flag in input_flags.get_flags()
|
|
||||||
]
|
|
||||||
if input_flag.get_string_entity() not in all_flags:
|
|
||||||
input_flags.add_flag(input_flag)
|
|
||||||
else:
|
|
||||||
raise RepeatedInputFlagsException(input_flag)
|
|
||||||
|
|
||||||
current_flag_name, current_flag_value = None, None
|
|
||||||
|
|
||||||
if any([current_flag_name, current_flag_value]):
|
|
||||||
raise UnprocessedInputFlagException()
|
raise UnprocessedInputFlagException()
|
||||||
else:
|
else:
|
||||||
return InputCommand(trigger=command, input_flags=input_flags)
|
return (self.raw_command.split()[0], input_flags)
|
||||||
|
|
||||||
|
def _parse_flags(self, _tokens: list[str] | list[Never]) -> ParseFlagsResult:
|
||||||
|
crnt_flg_name, crnt_flg_val = None, None
|
||||||
|
for index, token in enumerate(_tokens):
|
||||||
|
crnt_flg_name, crnt_flg_val = _parse_single_token(token, crnt_flg_name, crnt_flg_val)
|
||||||
|
|
||||||
|
if not crnt_flg_name or self._is_next_token_value(index, _tokens):
|
||||||
|
continue
|
||||||
|
|
||||||
|
input_flag = InputFlag(
|
||||||
|
name=crnt_flg_name[crnt_flg_name.rfind(MIN_FLAG_PREFIX) + 1:],
|
||||||
|
prefix=cast(
|
||||||
|
Literal["-", "--", "---"],
|
||||||
|
crnt_flg_name[:crnt_flg_name.rfind(MIN_FLAG_PREFIX) + 1],
|
||||||
|
),
|
||||||
|
input_value=crnt_flg_val,
|
||||||
|
status=None
|
||||||
|
)
|
||||||
|
|
||||||
|
if input_flag in self._parsed_input_flags:
|
||||||
|
raise RepeatedInputFlagsException(input_flag)
|
||||||
|
|
||||||
|
self._parsed_input_flags.add_flag(input_flag)
|
||||||
|
crnt_flg_name, crnt_flg_val = None, None
|
||||||
|
|
||||||
|
return (self._parsed_input_flags, crnt_flg_name, crnt_flg_val)
|
||||||
|
|
||||||
|
def _is_next_token_value(self, current_index: int,
|
||||||
|
_tokens: list[str] | list[Never]) -> bool:
|
||||||
|
next_index = current_index + 1
|
||||||
|
if next_index >= len(_tokens):
|
||||||
|
return False
|
||||||
|
|
||||||
|
next_token = _tokens[next_index]
|
||||||
|
return not next_token.startswith(MIN_FLAG_PREFIX)
|
||||||
|
|
||||||
|
def _parse_single_token(
|
||||||
|
token: str,
|
||||||
|
crnt_flag_name: str | None,
|
||||||
|
crnt_flag_val: str | None
|
||||||
|
) -> tuple[str | None, str | None]:
|
||||||
|
if not token.startswith(MIN_FLAG_PREFIX):
|
||||||
|
if not crnt_flag_name or crnt_flag_val:
|
||||||
|
raise UnprocessedInputFlagException
|
||||||
|
return crnt_flag_name, token
|
||||||
|
|
||||||
|
prefix = token[:token.rfind(MIN_FLAG_PREFIX)]
|
||||||
|
if len(token) < 2 or len(prefix) > 2:
|
||||||
|
raise UnprocessedInputFlagException
|
||||||
|
|
||||||
|
new_flag_name = token
|
||||||
|
new_flag_value = None
|
||||||
|
|
||||||
|
return new_flag_name, new_flag_value
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
__all__ = ["get_time_of_pre_cycle_setup"]
|
||||||
|
|
||||||
|
|
||||||
|
from argenta.metrics.main import get_time_of_pre_cycle_setup
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import io
|
||||||
|
from contextlib import redirect_stdout
|
||||||
|
from time import time
|
||||||
|
|
||||||
|
from argenta import App
|
||||||
|
|
||||||
|
|
||||||
|
def get_time_of_pre_cycle_setup(app: App) -> float:
|
||||||
|
"""
|
||||||
|
Public. Return time of pre cycle setup
|
||||||
|
:param app: app instance for testing time of pre cycle setup
|
||||||
|
:return: time of pre cycle setup as float
|
||||||
|
"""
|
||||||
|
start = time()
|
||||||
|
with redirect_stdout(io.StringIO()):
|
||||||
|
app._pre_cycle_setup() # pyright: ignore[reportPrivateUsage]
|
||||||
|
end = time()
|
||||||
|
return end - start
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
__all__ = ["Orchestrator"]
|
__all__ = [
|
||||||
|
"Orchestrator",
|
||||||
|
"ArgParser"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
from argenta.orchestrator.entity import Orchestrator
|
from argenta.orchestrator.entity import Orchestrator
|
||||||
|
from argenta.orchestrator.argparser.entity import ArgParser
|
||||||
|
|||||||
@@ -1,4 +1,12 @@
|
|||||||
__all__ = ["ArgParser"]
|
__all__ = [
|
||||||
|
"ArgParser",
|
||||||
|
"PositionalArgument",
|
||||||
|
"OptionalArgument",
|
||||||
|
"BooleanArgument"
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
from argenta.orchestrator.argparser.entity import ArgParser
|
from argenta.orchestrator.argparser.entity import ArgParser
|
||||||
|
from argenta.orchestrator.argparser.arguments import (BooleanArgument,
|
||||||
|
PositionalArgument,
|
||||||
|
OptionalArgument)
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Literal
|
from typing import Literal, override
|
||||||
|
|
||||||
|
|
||||||
class BaseArgument(ABC):
|
class BaseArgument(ABC):
|
||||||
"""
|
"""
|
||||||
Private. Base class for all arguments
|
Private. Base class for all arguments
|
||||||
"""
|
"""
|
||||||
|
@property
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_string_entity(self) -> str:
|
def string_entity(self) -> str:
|
||||||
"""
|
"""
|
||||||
Public. Returns the string representation of the argument
|
Public. Returns the string representation of the argument
|
||||||
:return: the string representation as a str
|
:return: the string representation as a str
|
||||||
"""
|
"""
|
||||||
pass
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|
||||||
class PositionalArgument(BaseArgument):
|
class PositionalArgument(BaseArgument):
|
||||||
@@ -22,9 +22,11 @@ class PositionalArgument(BaseArgument):
|
|||||||
Public. Required argument at startup
|
Public. Required argument at startup
|
||||||
:param name: name of the argument, must not start with minus (-)
|
:param name: name of the argument, must not start with minus (-)
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name: str = name
|
||||||
|
|
||||||
def get_string_entity(self):
|
@property
|
||||||
|
@override
|
||||||
|
def string_entity(self) -> str:
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
@@ -35,10 +37,12 @@ class OptionalArgument(BaseArgument):
|
|||||||
:param name: name of the argument
|
:param name: name of the argument
|
||||||
:param prefix: prefix of the argument
|
:param prefix: prefix of the argument
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name: str = name
|
||||||
self.prefix = prefix
|
self.prefix: Literal["-", "--", "---"] = prefix
|
||||||
|
|
||||||
def get_string_entity(self):
|
@property
|
||||||
|
@override
|
||||||
|
def string_entity(self) -> str:
|
||||||
return self.prefix + self.name
|
return self.prefix + self.name
|
||||||
|
|
||||||
|
|
||||||
@@ -49,8 +53,10 @@ class BooleanArgument(BaseArgument):
|
|||||||
:param name: name of the argument
|
:param name: name of the argument
|
||||||
:param prefix: prefix of the argument
|
:param prefix: prefix of the argument
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self.name: str = name
|
||||||
self.prefix = prefix
|
self.prefix: Literal["-", "--", "---"] = prefix
|
||||||
|
|
||||||
def get_string_entity(self):
|
@property
|
||||||
|
@override
|
||||||
|
def string_entity(self) -> str:
|
||||||
return self.prefix + self.name
|
return self.prefix + self.name
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser, Namespace
|
||||||
|
|
||||||
from argenta.orchestrator.argparser.arguments.models import (
|
from argenta.orchestrator.argparser.arguments.models import (
|
||||||
BooleanArgument,
|
BooleanArgument,
|
||||||
@@ -10,7 +10,7 @@ from argenta.orchestrator.argparser.arguments.models import (
|
|||||||
class ArgParser:
|
class ArgParser:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
processed_args: list[PositionalArgument | OptionalArgument | BooleanArgument],
|
processed_args: list[PositionalArgument | OptionalArgument | BooleanArgument], *,
|
||||||
name: str = "Argenta",
|
name: str = "Argenta",
|
||||||
description: str = "Argenta available arguments",
|
description: str = "Argenta available arguments",
|
||||||
epilog: str = "github.com/koloideal/Argenta | made by kolo",
|
epilog: str = "github.com/koloideal/Argenta | made by kolo",
|
||||||
@@ -22,38 +22,18 @@ class ArgParser:
|
|||||||
:param epilog: the epilog of the ArgParse instance
|
:param epilog: the epilog of the ArgParse instance
|
||||||
:param processed_args: registered and processed arguments
|
:param processed_args: registered and processed arguments
|
||||||
"""
|
"""
|
||||||
self.name = name
|
self._name: str = name
|
||||||
self.description = description
|
self._description: str = description
|
||||||
self.epilog = epilog
|
self._epilog: str = epilog
|
||||||
|
|
||||||
self.entity: ArgumentParser = ArgumentParser(
|
self._entity: ArgumentParser = ArgumentParser(prog=name, description=description, epilog=epilog)
|
||||||
prog=name, description=description, epilog=epilog
|
self._processed_args: list[PositionalArgument | OptionalArgument | BooleanArgument] = processed_args
|
||||||
)
|
|
||||||
self.args: (
|
for arg in processed_args:
|
||||||
list[PositionalArgument | OptionalArgument | BooleanArgument] | None
|
if isinstance(arg, PositionalArgument) or isinstance(arg, OptionalArgument):
|
||||||
) = processed_args
|
_ = self._entity.add_argument(arg.string_entity)
|
||||||
|
else:
|
||||||
|
_ = self._entity.add_argument(arg.string_entity, action="store_true")
|
||||||
|
|
||||||
def set_args(
|
def parse_args(self) -> Namespace:
|
||||||
self, *args: PositionalArgument | OptionalArgument | BooleanArgument
|
return self._entity.parse_args()
|
||||||
) -> None:
|
|
||||||
"""
|
|
||||||
Public. Sets the arguments to be processed
|
|
||||||
:param args: processed arguments
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self.args.extend(args)
|
|
||||||
|
|
||||||
def register_args(self) -> None:
|
|
||||||
"""
|
|
||||||
Private. Registers initialized command line arguments
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
if not self.args:
|
|
||||||
return
|
|
||||||
for arg in self.args:
|
|
||||||
if type(arg) is PositionalArgument:
|
|
||||||
self.entity.add_argument(arg.get_string_entity())
|
|
||||||
elif type(arg) is OptionalArgument:
|
|
||||||
self.entity.add_argument(arg.get_string_entity())
|
|
||||||
elif type(arg) is BooleanArgument:
|
|
||||||
self.entity.add_argument(arg.get_string_entity(), action="store_true")
|
|
||||||
|
|||||||
@@ -5,18 +5,15 @@ from argenta.orchestrator.argparser import ArgParser
|
|||||||
|
|
||||||
|
|
||||||
class Orchestrator:
|
class Orchestrator:
|
||||||
def __init__(self, arg_parser: ArgParser = False):
|
def __init__(self, arg_parser: ArgParser | None = None):
|
||||||
"""
|
"""
|
||||||
Public. An orchestrator and configurator that defines the behavior of an integrated system, one level higher than the App
|
Public. An orchestrator and configurator that defines the behavior of an integrated system, one level higher than the App
|
||||||
:param arg_parser: Cmd argument parser and configurator at startup
|
:param arg_parser: Cmd argument parser and configurator at startup
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.arg_parser: ArgParser | False = arg_parser
|
self._arg_parser: ArgParser | None = arg_parser
|
||||||
if arg_parser:
|
|
||||||
self.arg_parser.register_args()
|
|
||||||
|
|
||||||
@staticmethod
|
def start_polling(self, app: App) -> None:
|
||||||
def start_polling(app: App) -> None:
|
|
||||||
"""
|
"""
|
||||||
Public. Starting the user input processing cycle
|
Public. Starting the user input processing cycle
|
||||||
:param app: a running application
|
:param app: a running application
|
||||||
@@ -29,7 +26,7 @@ class Orchestrator:
|
|||||||
Public. Returns the arguments parsed
|
Public. Returns the arguments parsed
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
if self.arg_parser:
|
if self._arg_parser:
|
||||||
return self.arg_parser.entity.parse_args()
|
return self._arg_parser.parse_args()
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
__all__ = ["Response", "Status"]
|
__all__ = ["Response", "ResponseStatus"]
|
||||||
|
|
||||||
|
|
||||||
from argenta.response.entity import Response
|
from argenta.response.entity import Response
|
||||||
from argenta.response.status import Status
|
from argenta.response.status import ResponseStatus
|
||||||
|
|||||||
@@ -1,29 +1,23 @@
|
|||||||
from argenta.response.status import Status
|
from typing import Literal
|
||||||
from argenta.command.flags import (
|
from argenta.command.flag.flags.models import InputFlags
|
||||||
ValidInputFlags,
|
from argenta.response.status import ResponseStatus
|
||||||
UndefinedInputFlags,
|
|
||||||
InvalidValueInputFlags,
|
|
||||||
)
|
EMPTY_INPUT_FLAGS: InputFlags = InputFlags()
|
||||||
|
|
||||||
|
|
||||||
class Response:
|
class Response:
|
||||||
__slots__ = ("status", "valid_flags", "undefined_flags", "invalid_value_flags")
|
__slots__: tuple[Literal['status', 'input_flags'], ...] = ("status", "input_flags")
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
status: Status = None,
|
status: ResponseStatus,
|
||||||
valid_flags: ValidInputFlags = ValidInputFlags(),
|
input_flags: InputFlags = EMPTY_INPUT_FLAGS,
|
||||||
undefined_flags: UndefinedInputFlags = UndefinedInputFlags(),
|
|
||||||
invalid_value_flags: InvalidValueInputFlags = InvalidValueInputFlags(),
|
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Public. The entity of the user input sent to the handler
|
Public. The entity of the user input sent to the handler
|
||||||
:param status: the status of the response
|
:param status: the status of the response
|
||||||
:param valid_flags: valid input flags
|
:param input_flags: all input flags
|
||||||
:param undefined_flags: undefined input flags
|
|
||||||
:param invalid_value_flags: input flags with invalid values
|
|
||||||
"""
|
"""
|
||||||
self.status = status
|
self.status: ResponseStatus = status
|
||||||
self.valid_flags = valid_flags
|
self.input_flags: InputFlags = input_flags
|
||||||
self.undefined_flags = undefined_flags
|
|
||||||
self.invalid_value_flags = invalid_value_flags
|
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
|
|
||||||
class Status(Enum):
|
class ResponseStatus(Enum):
|
||||||
ALL_FLAGS_VALID = "ALL_FLAGS_VALID"
|
ALL_FLAGS_VALID = "ALL_FLAGS_VALID"
|
||||||
UNDEFINED_FLAGS = "UNDEFINED_FLAGS"
|
UNDEFINED_FLAGS = "UNDEFINED_FLAGS"
|
||||||
INVALID_VALUE_FLAGS = "INVALID_VALUE_FLAGS"
|
INVALID_VALUE_FLAGS = "INVALID_VALUE_FLAGS"
|
||||||
UNDEFINED_AND_INVALID_FLAGS = "UNDEFINED_AND_INVALID_FLAGS"
|
UNDEFINED_AND_INVALID_FLAGS = "UNDEFINED_AND_INVALID_FLAGS"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_flags(cls, *, has_invalid_value_flags: bool, has_undefined_flags: bool) -> 'ResponseStatus':
|
||||||
|
key = (has_invalid_value_flags, has_undefined_flags)
|
||||||
|
status_map: dict[tuple[bool, bool], ResponseStatus] = {
|
||||||
|
(True, True): cls.UNDEFINED_AND_INVALID_FLAGS,
|
||||||
|
(True, False): cls.INVALID_VALUE_FLAGS,
|
||||||
|
(False, True): cls.UNDEFINED_FLAGS,
|
||||||
|
(False, False): cls.ALL_FLAGS_VALID,
|
||||||
|
}
|
||||||
|
return status_map[key]
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
__all__ = ["Router"]
|
|
||||||
|
|
||||||
|
|
||||||
from argenta.router.entity import Router
|
from argenta.router.entity import Router
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = ["Router"]
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
from typing import Callable, Iterator
|
from collections.abc import Iterator
|
||||||
|
from typing import Callable
|
||||||
|
|
||||||
from argenta.command import Command
|
from argenta.command import Command
|
||||||
from argenta.response import Response
|
from argenta.response import Response
|
||||||
|
|
||||||
|
|
||||||
class CommandHandler:
|
class CommandHandler:
|
||||||
def __init__(self, handler: Callable[[Response], None], handled_command: Command):
|
def __init__(self, handler_as_func: Callable[[Response], None], handled_command: Command):
|
||||||
"""
|
"""
|
||||||
Private. Entity of the model linking the handler and the command being processed
|
Private. Entity of the model linking the handler and the command being processed
|
||||||
:param handler: the handler being called
|
:param handler: the handler being called
|
||||||
:param handled_command: the command being processed
|
:param handled_command: the command being processed
|
||||||
"""
|
"""
|
||||||
self._handler = handler
|
self.handler_as_func: Callable[[Response], None] = handler_as_func
|
||||||
self._handled_command = handled_command
|
self.handled_command: Command = handled_command
|
||||||
|
|
||||||
def handling(self, response: Response) -> None:
|
def handling(self, response: Response) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -20,37 +21,16 @@ class CommandHandler:
|
|||||||
:param response: the entity of response: various groups of flags and status of response
|
:param response: the entity of response: various groups of flags and status of response
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self._handler(response)
|
self.handler_as_func(response)
|
||||||
|
|
||||||
def get_handler(self) -> Callable[[Response], None]:
|
|
||||||
"""
|
|
||||||
Private. Returns the handler being called
|
|
||||||
:return: the handler being called as Callable[[Response], None]
|
|
||||||
"""
|
|
||||||
return self._handler
|
|
||||||
|
|
||||||
def get_handled_command(self) -> Command:
|
|
||||||
"""
|
|
||||||
Private. Returns the command being processed
|
|
||||||
:return: the command being processed as Command
|
|
||||||
"""
|
|
||||||
return self._handled_command
|
|
||||||
|
|
||||||
|
|
||||||
class CommandHandlers:
|
class CommandHandlers:
|
||||||
def __init__(self, command_handlers: list[CommandHandler] = None):
|
def __init__(self, command_handlers: list[CommandHandler] | None = None):
|
||||||
"""
|
"""
|
||||||
Private. The model that unites all CommandHandler of the routers
|
Private. The model that unites all CommandHandler of the routers
|
||||||
:param command_handlers: list of CommandHandlers for register
|
:param command_handlers: list of CommandHandlers for register
|
||||||
"""
|
"""
|
||||||
self.command_handlers = command_handlers if command_handlers else []
|
self.command_handlers: list[CommandHandler] = command_handlers if command_handlers else []
|
||||||
|
|
||||||
def get_handlers(self) -> list[CommandHandler]:
|
|
||||||
"""
|
|
||||||
Private. Returns the list of CommandHandlers
|
|
||||||
:return: the list of CommandHandlers as list[CommandHandler]
|
|
||||||
"""
|
|
||||||
return self.command_handlers
|
|
||||||
|
|
||||||
def add_handler(self, command_handler: CommandHandler) -> None:
|
def add_handler(self, command_handler: CommandHandler) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
+138
-164
@@ -1,17 +1,14 @@
|
|||||||
from typing import Callable, Literal, Type
|
from typing import Callable, TypeAlias
|
||||||
from inspect import getfullargspec, get_annotations, getsourcefile, getsourcelines
|
from inspect import getfullargspec, get_annotations, getsourcefile, getsourcelines
|
||||||
from rich.console import Console
|
from rich.console import Console
|
||||||
|
|
||||||
from argenta.command import Command
|
from argenta.command import Command, InputCommand
|
||||||
from argenta.command.models import InputCommand
|
from argenta.command.flag import ValidationStatus
|
||||||
from argenta.response import Response, Status
|
from argenta.response import Response, ResponseStatus
|
||||||
from argenta.router.command_handler.entity import CommandHandlers, CommandHandler
|
from argenta.router.command_handler.entity import CommandHandlers, CommandHandler
|
||||||
from argenta.command.flags.models import (
|
from argenta.command.flag.flags import (
|
||||||
Flags,
|
Flags,
|
||||||
InputFlags,
|
InputFlags
|
||||||
UndefinedInputFlags,
|
|
||||||
ValidInputFlags,
|
|
||||||
InvalidValueInputFlags,
|
|
||||||
)
|
)
|
||||||
from argenta.router.exceptions import (
|
from argenta.router.exceptions import (
|
||||||
RepeatedFlagNameException,
|
RepeatedFlagNameException,
|
||||||
@@ -21,38 +18,50 @@ from argenta.router.exceptions import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
HandlerFunc: TypeAlias = Callable[[Response], None]
|
||||||
|
|
||||||
|
|
||||||
class Router:
|
class Router:
|
||||||
def __init__(self, title: str | None = "Awesome title"):
|
def __init__(
|
||||||
|
self, *, title: str | None = "Default title",
|
||||||
|
disable_redirect_stdout: bool = False
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Public. Directly configures and manages handlers
|
Public. Directly configures and manages handlers
|
||||||
:param title: the title of the router, displayed when displaying the available commands
|
:param title: the title of the router, displayed when displaying the available commands
|
||||||
|
:param disable_redirect_stdout: Disables stdout forwarding, if the argument value is True,
|
||||||
|
the StaticDividingLine will be forced to be used as a line separator for this router,
|
||||||
|
disabled forwarding is needed when there is text output in conjunction with a text input request (for example, input()),
|
||||||
|
if the argument value is True, the output of the input() prompt is intercepted and not displayed,
|
||||||
|
which is ambiguous behavior and can lead to unexpected work
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
self.title = title
|
self.title: str | None = title
|
||||||
|
self.disable_redirect_stdout: bool = disable_redirect_stdout
|
||||||
|
|
||||||
self._command_handlers: CommandHandlers = CommandHandlers()
|
self.command_handlers: CommandHandlers = CommandHandlers()
|
||||||
self._ignore_command_register: bool = False
|
self.command_register_ignore: bool = False
|
||||||
|
|
||||||
def command(self, command: Command | str) -> Callable:
|
def command(self, command: Command | str) -> Callable[[HandlerFunc], HandlerFunc]:
|
||||||
"""
|
"""
|
||||||
Public. Registers handler
|
Public. Registers handler
|
||||||
:param command: Registered command
|
:param command: Registered command
|
||||||
:return: decorated handler as Callable
|
:return: decorated handler as Callable
|
||||||
"""
|
"""
|
||||||
self._validate_command(command)
|
|
||||||
if isinstance(command, str):
|
if isinstance(command, str):
|
||||||
command = Command(command)
|
redefined_command = Command(command)
|
||||||
|
else:
|
||||||
|
redefined_command = command
|
||||||
|
|
||||||
def command_decorator(func):
|
_validate_command(redefined_command)
|
||||||
Router._validate_func_args(func)
|
|
||||||
self._command_handlers.add_handler(CommandHandler(func, command))
|
|
||||||
|
|
||||||
def wrapper(*args, **kwargs):
|
def decorator(func: HandlerFunc) -> HandlerFunc:
|
||||||
return func(*args, **kwargs)
|
_validate_func_args(func)
|
||||||
|
self.command_handlers.add_handler(CommandHandler(func, redefined_command))
|
||||||
|
|
||||||
return wrapper
|
return func
|
||||||
|
|
||||||
return command_decorator
|
return decorator
|
||||||
|
|
||||||
def finds_appropriate_handler(self, input_command: InputCommand) -> None:
|
def finds_appropriate_handler(self, input_command: InputCommand) -> None:
|
||||||
"""
|
"""
|
||||||
@@ -60,14 +69,14 @@ class Router:
|
|||||||
:param input_command: input command as InputCommand
|
:param input_command: input command as InputCommand
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
input_command_name: str = input_command.get_trigger()
|
input_command_name: str = input_command.trigger
|
||||||
input_command_flags: InputFlags = input_command.get_input_flags()
|
input_command_flags: InputFlags = input_command.input_flags
|
||||||
|
|
||||||
for command_handler in self._command_handlers:
|
for command_handler in self.command_handlers:
|
||||||
handle_command = command_handler.get_handled_command()
|
handle_command = command_handler.handled_command
|
||||||
if input_command_name.lower() == handle_command.get_trigger().lower():
|
if input_command_name.lower() == handle_command.trigger.lower():
|
||||||
self.process_input_command(input_command_flags, command_handler)
|
self.process_input_command(input_command_flags, command_handler)
|
||||||
if input_command_name.lower() in handle_command.get_aliases():
|
if input_command_name.lower() in handle_command.aliases:
|
||||||
self.process_input_command(input_command_flags, command_handler)
|
self.process_input_command(input_command_flags, command_handler)
|
||||||
|
|
||||||
def process_input_command(
|
def process_input_command(
|
||||||
@@ -79,160 +88,125 @@ class Router:
|
|||||||
:param command_handler: command handler for input command as CommandHandler
|
:param command_handler: command handler for input command as CommandHandler
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
handle_command = command_handler.get_handled_command()
|
handle_command = command_handler.handled_command
|
||||||
response: Response = Response()
|
if handle_command.registered_flags.flags:
|
||||||
if handle_command.get_registered_flags().get_flags():
|
if input_command_flags.flags:
|
||||||
if input_command_flags.get_flags():
|
response: Response = _structuring_input_flags(handle_command, input_command_flags)
|
||||||
response: Response = self._structuring_input_flags(
|
|
||||||
handle_command, input_command_flags
|
|
||||||
)
|
|
||||||
command_handler.handling(response)
|
command_handler.handling(response)
|
||||||
else:
|
else:
|
||||||
response.status = Status.ALL_FLAGS_VALID
|
response = Response(ResponseStatus.ALL_FLAGS_VALID)
|
||||||
command_handler.handling(response)
|
command_handler.handling(response)
|
||||||
else:
|
else:
|
||||||
if input_command_flags.get_flags():
|
if input_command_flags.flags:
|
||||||
response.status = Status.UNDEFINED_FLAGS
|
undefined_flags = InputFlags()
|
||||||
response.undefined_flags = UndefinedInputFlags()
|
for input_flag in input_command_flags:
|
||||||
response.undefined_flags.add_flags(input_command_flags.get_flags())
|
input_flag.status = ValidationStatus.UNDEFINED
|
||||||
|
undefined_flags.add_flag(input_flag)
|
||||||
|
response = Response(ResponseStatus.UNDEFINED_FLAGS, input_flags=undefined_flags)
|
||||||
command_handler.handling(response)
|
command_handler.handling(response)
|
||||||
else:
|
else:
|
||||||
response.status = Status.ALL_FLAGS_VALID
|
response = Response(ResponseStatus.ALL_FLAGS_VALID)
|
||||||
command_handler.handling(response)
|
command_handler.handling(response)
|
||||||
|
|
||||||
@staticmethod
|
@property
|
||||||
def _structuring_input_flags(
|
def triggers(self) -> list[str]:
|
||||||
handled_command: Command, input_flags: InputFlags
|
|
||||||
) -> Response:
|
|
||||||
"""
|
|
||||||
Private. Validates flags of input command
|
|
||||||
:param handled_command: entity of the handled command
|
|
||||||
:param input_flags:
|
|
||||||
:return: entity of response as Response
|
|
||||||
"""
|
|
||||||
valid_input_flags: ValidInputFlags = ValidInputFlags()
|
|
||||||
invalid_value_input_flags: InvalidValueInputFlags = InvalidValueInputFlags()
|
|
||||||
undefined_input_flags: UndefinedInputFlags = UndefinedInputFlags()
|
|
||||||
for flag in input_flags:
|
|
||||||
flag_status: Literal["Undefined", "Valid", "Invalid"] = (
|
|
||||||
handled_command.validate_input_flag(flag)
|
|
||||||
)
|
|
||||||
match flag_status:
|
|
||||||
case "Valid":
|
|
||||||
valid_input_flags.add_flag(flag)
|
|
||||||
case "Undefined":
|
|
||||||
undefined_input_flags.add_flag(flag)
|
|
||||||
case "Invalid":
|
|
||||||
invalid_value_input_flags.add_flag(flag)
|
|
||||||
|
|
||||||
if (
|
|
||||||
not invalid_value_input_flags.get_flags()
|
|
||||||
and not undefined_input_flags.get_flags()
|
|
||||||
):
|
|
||||||
status = Status.ALL_FLAGS_VALID
|
|
||||||
elif (
|
|
||||||
invalid_value_input_flags.get_flags()
|
|
||||||
and not undefined_input_flags.get_flags()
|
|
||||||
):
|
|
||||||
status = Status.INVALID_VALUE_FLAGS
|
|
||||||
elif (
|
|
||||||
not invalid_value_input_flags.get_flags()
|
|
||||||
and undefined_input_flags.get_flags()
|
|
||||||
):
|
|
||||||
status = Status.UNDEFINED_FLAGS
|
|
||||||
else:
|
|
||||||
status = Status.UNDEFINED_AND_INVALID_FLAGS
|
|
||||||
|
|
||||||
return Response(
|
|
||||||
invalid_value_flags=invalid_value_input_flags,
|
|
||||||
valid_flags=valid_input_flags,
|
|
||||||
status=status,
|
|
||||||
undefined_flags=undefined_input_flags,
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _validate_command(command: Command | str) -> None:
|
|
||||||
"""
|
|
||||||
Private. Validates the command registered in handler
|
|
||||||
:param command: validated command
|
|
||||||
:return: None if command is valid else raise exception
|
|
||||||
"""
|
|
||||||
match type(command).__name__:
|
|
||||||
case "Command":
|
|
||||||
command_name: str = command.get_trigger()
|
|
||||||
if command_name.find(" ") != -1:
|
|
||||||
raise TriggerContainSpacesException()
|
|
||||||
flags: Flags = command.get_registered_flags()
|
|
||||||
if flags:
|
|
||||||
flags_name: list = [x.get_string_entity().lower() for x in flags]
|
|
||||||
if len(set(flags_name)) < len(flags_name):
|
|
||||||
raise RepeatedFlagNameException()
|
|
||||||
case "str":
|
|
||||||
if command.find(" ") != -1:
|
|
||||||
raise TriggerContainSpacesException()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _validate_func_args(func: Callable) -> None:
|
|
||||||
"""
|
|
||||||
Private. Validates the arguments of the handler
|
|
||||||
:param func: entity of the handler func
|
|
||||||
:return: None if func is valid else raise exception
|
|
||||||
"""
|
|
||||||
transferred_args = getfullargspec(func).args
|
|
||||||
if len(transferred_args) > 1:
|
|
||||||
raise TooManyTransferredArgsException()
|
|
||||||
elif len(transferred_args) == 0:
|
|
||||||
raise RequiredArgumentNotPassedException()
|
|
||||||
|
|
||||||
transferred_arg: str = transferred_args[0]
|
|
||||||
func_annotations: dict[str, Type] = get_annotations(func)
|
|
||||||
|
|
||||||
if arg_annotation := func_annotations.get(transferred_arg):
|
|
||||||
if arg_annotation is Response:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
file_path: str = getsourcefile(func)
|
|
||||||
source_line: int = getsourcelines(func)[1] + 1
|
|
||||||
fprint = Console().print
|
|
||||||
fprint(
|
|
||||||
f'\nFile "{file_path}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint '
|
|
||||||
f"of argument([green]{transferred_arg}[/green]) passed to the handler is [/i][bold blue]{Response}[/bold blue],"
|
|
||||||
f" [i]but[/i] [bold blue]{arg_annotation}[/bold blue] [i]is specified[/i]\n",
|
|
||||||
highlight=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_command_register_ignore(self, _: bool) -> None:
|
|
||||||
"""
|
|
||||||
Private. Sets the router behavior on the input commands register
|
|
||||||
:param _: is command register ignore
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
self._ignore_command_register = _
|
|
||||||
|
|
||||||
def get_triggers(self) -> list[str]:
|
|
||||||
"""
|
"""
|
||||||
Public. Gets registered triggers
|
Public. Gets registered triggers
|
||||||
:return: registered in router triggers as list[str]
|
:return: registered in router triggers as list[str]
|
||||||
"""
|
"""
|
||||||
all_triggers: list[str] = []
|
all_triggers: list[str] = []
|
||||||
for command_handler in self._command_handlers:
|
for command_handler in self.command_handlers:
|
||||||
all_triggers.append(command_handler.get_handled_command().get_trigger())
|
all_triggers.append(command_handler.handled_command.trigger)
|
||||||
return all_triggers
|
return all_triggers
|
||||||
|
|
||||||
def get_aliases(self) -> list[str]:
|
@property
|
||||||
|
def aliases(self) -> list[str]:
|
||||||
"""
|
"""
|
||||||
Public. Gets registered aliases
|
Public. Gets registered aliases
|
||||||
:return: registered in router aliases as list[str]
|
:return: registered in router aliases as list[str]
|
||||||
"""
|
"""
|
||||||
all_aliases: list[str] = []
|
all_aliases: list[str] = []
|
||||||
for command_handler in self._command_handlers:
|
for command_handler in self.command_handlers:
|
||||||
if command_handler.get_handled_command().get_aliases():
|
if command_handler.handled_command.aliases:
|
||||||
all_aliases.extend(command_handler.get_handled_command().get_aliases())
|
all_aliases.extend(command_handler.handled_command.aliases)
|
||||||
return all_aliases
|
return all_aliases
|
||||||
|
|
||||||
def get_command_handlers(self) -> CommandHandlers:
|
|
||||||
"""
|
class CommandDecorator:
|
||||||
Private. Gets registered command handlers
|
def __init__(self, router_instance: Router, command: Command):
|
||||||
:return: registered command handlers as CommandHandlers
|
self.router: Router = router_instance
|
||||||
"""
|
self.command: Command = command
|
||||||
return self._command_handlers
|
|
||||||
|
def __call__(self, handler_func: Callable[[Response], None]) -> Callable[[Response], None]:
|
||||||
|
_validate_func_args(handler_func)
|
||||||
|
self.router.command_handlers.add_handler(CommandHandler(handler_func, self.command))
|
||||||
|
return handler_func
|
||||||
|
|
||||||
|
|
||||||
|
def _structuring_input_flags(handled_command: Command,
|
||||||
|
input_flags: InputFlags) -> Response:
|
||||||
|
"""
|
||||||
|
Private. Validates flags of input command
|
||||||
|
:param handled_command: entity of the handled command
|
||||||
|
:param input_flags:
|
||||||
|
:return: entity of response as Response
|
||||||
|
"""
|
||||||
|
invalid_value_flags, undefined_flags = False, False
|
||||||
|
|
||||||
|
for flag in input_flags:
|
||||||
|
flag_status: ValidationStatus = (handled_command.validate_input_flag(flag))
|
||||||
|
flag.status = flag_status
|
||||||
|
if flag_status == ValidationStatus.INVALID:
|
||||||
|
invalid_value_flags = True
|
||||||
|
elif flag_status == ValidationStatus.UNDEFINED:
|
||||||
|
undefined_flags = True
|
||||||
|
|
||||||
|
status = ResponseStatus.from_flags(has_invalid_value_flags=invalid_value_flags,
|
||||||
|
has_undefined_flags=undefined_flags)
|
||||||
|
|
||||||
|
return Response(
|
||||||
|
status=status,
|
||||||
|
input_flags=input_flags
|
||||||
|
)
|
||||||
|
|
||||||
|
def _validate_func_args(func: Callable[[Response], None]) -> None:
|
||||||
|
"""
|
||||||
|
Private. Validates the arguments of the handler
|
||||||
|
:param func: entity of the handler func
|
||||||
|
:return: None if func is valid else raise exception
|
||||||
|
"""
|
||||||
|
transferred_args = getfullargspec(func).args
|
||||||
|
if len(transferred_args) > 1:
|
||||||
|
raise TooManyTransferredArgsException()
|
||||||
|
elif len(transferred_args) == 0:
|
||||||
|
raise RequiredArgumentNotPassedException()
|
||||||
|
|
||||||
|
transferred_arg: str = transferred_args[0]
|
||||||
|
func_annotations: dict[str, None] = get_annotations(func)
|
||||||
|
|
||||||
|
arg_annotation = func_annotations.get(transferred_arg)
|
||||||
|
|
||||||
|
if arg_annotation is not None:
|
||||||
|
if arg_annotation is not Response:
|
||||||
|
source_line: int = getsourcelines(func)[1]
|
||||||
|
Console().print(
|
||||||
|
f'\nFile "{getsourcefile(func)}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint ' +
|
||||||
|
f"of argument([green]{transferred_arg}[/green]) passed to the handler must be [/i][bold blue]{Response}[/bold blue]," +
|
||||||
|
f" [i]but[/i] [bold blue]{arg_annotation}[/bold blue] [i]is specified[/i]",
|
||||||
|
highlight=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_command(command: Command) -> None:
|
||||||
|
"""
|
||||||
|
Private. Validates the command registered in handler
|
||||||
|
:param command: validated command
|
||||||
|
:return: None if command is valid else raise exception
|
||||||
|
"""
|
||||||
|
command_name: str = command.trigger
|
||||||
|
if command_name.find(" ") != -1:
|
||||||
|
raise TriggerContainSpacesException()
|
||||||
|
flags: Flags = command.registered_flags
|
||||||
|
flags_name: list[str] = [flag.string_entity.lower() for flag in flags]
|
||||||
|
if len(set(flags_name)) < len(flags_name):
|
||||||
|
raise RepeatedFlagNameException()
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
|
from typing import override
|
||||||
|
|
||||||
|
|
||||||
class RepeatedFlagNameException(Exception):
|
class RepeatedFlagNameException(Exception):
|
||||||
"""
|
"""
|
||||||
Private. Raised when a repeated flag name is registered
|
Private. Raised when a repeated flag name is registered
|
||||||
"""
|
"""
|
||||||
|
@override
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "Repeated registered flag names in register command"
|
return "Repeated registered flag names in register command"
|
||||||
|
|
||||||
|
|
||||||
@@ -11,8 +14,8 @@ class TooManyTransferredArgsException(Exception):
|
|||||||
"""
|
"""
|
||||||
Private. Raised when too many arguments are passed
|
Private. Raised when too many arguments are passed
|
||||||
"""
|
"""
|
||||||
|
@override
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "Too many transferred arguments"
|
return "Too many transferred arguments"
|
||||||
|
|
||||||
|
|
||||||
@@ -20,8 +23,8 @@ class RequiredArgumentNotPassedException(Exception):
|
|||||||
"""
|
"""
|
||||||
Private. Raised when a required argument is not passed
|
Private. Raised when a required argument is not passed
|
||||||
"""
|
"""
|
||||||
|
@override
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "Required argument not passed"
|
return "Required argument not passed"
|
||||||
|
|
||||||
|
|
||||||
@@ -29,6 +32,6 @@ class TriggerContainSpacesException(Exception):
|
|||||||
"""
|
"""
|
||||||
Private. Raised when there is a space in the trigger being registered
|
Private. Raised when there is a space in the trigger being registered
|
||||||
"""
|
"""
|
||||||
|
@override
|
||||||
def __str__(self):
|
def __str__(self) -> str:
|
||||||
return "Command trigger cannot contain spaces"
|
return "Command trigger cannot contain spaces"
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ import io
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from argenta.app import App
|
from argenta.app import App
|
||||||
from argenta.command import Command
|
from argenta.command import Command, PredefinedFlags
|
||||||
|
from argenta.command.flag.models import ValidationStatus
|
||||||
from argenta.router import Router
|
from argenta.router import Router
|
||||||
from argenta.command.flags.models import Flags
|
from argenta.command.flag.flags.models import Flags
|
||||||
from argenta.command.flag.defaults import PredefinedFlags
|
|
||||||
from argenta.orchestrator import Orchestrator
|
from argenta.orchestrator import Orchestrator
|
||||||
from argenta.response import Response
|
from argenta.response import Response
|
||||||
|
|
||||||
@@ -22,13 +22,13 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print('test command')
|
print('test command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.get_trigger()}'))
|
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.trigger}'))
|
||||||
orchestrator.start_polling(app)
|
orchestrator.start_polling(app)
|
||||||
|
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
@@ -43,14 +43,14 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print('test command')
|
print('test command')
|
||||||
|
|
||||||
app = App(ignore_command_register=False,
|
app = App(ignore_command_register=False,
|
||||||
override_system_messages=True,
|
override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.get_trigger()}'))
|
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.trigger}'))
|
||||||
orchestrator.start_polling(app)
|
orchestrator.start_polling(app)
|
||||||
|
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
@@ -65,8 +65,10 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'test command with undefined flag: {response.undefined_flags.get_flag('help').get_string_entity()}')
|
undefined_flag = response.input_flags.get_flag_by_name('help')
|
||||||
|
if undefined_flag and undefined_flag.status == ValidationStatus.UNDEFINED:
|
||||||
|
print(f'test command with undefined flag: {undefined_flag.string_entity}')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -85,9 +87,12 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
flag = response.undefined_flags.get_flag("port")
|
undefined_flag = response.input_flags.get_flag_by_name("port")
|
||||||
print(f'test command with undefined flag with value: {flag.get_string_entity()} {flag.get_value()}')
|
if undefined_flag and undefined_flag.status == ValidationStatus.UNDEFINED:
|
||||||
|
print(f'test command with undefined flag with value: {undefined_flag.string_entity} {undefined_flag.input_value}')
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -104,12 +109,13 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
def test_input_correct_command_with_one_correct_flag_an_one_incorrect_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
def test_input_correct_command_with_one_correct_flag_an_one_incorrect_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
||||||
router = Router()
|
router = Router()
|
||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
flags = Flags(PredefinedFlags.HOST)
|
flags = Flags([PredefinedFlags.HOST])
|
||||||
|
|
||||||
@router.command(Command('test', flags=flags))
|
@router.command(Command('test', flags=flags))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
flag = response.undefined_flags.get_flag("port")
|
undefined_flag = response.input_flags.get_flag_by_name("port")
|
||||||
print(f'connecting to host with flag: {flag.get_string_entity()} {flag.get_value()}')
|
if undefined_flag and undefined_flag.status == ValidationStatus.UNDEFINED:
|
||||||
|
print(f'connecting to host with flag: {undefined_flag.string_entity} {undefined_flag.input_value}')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -128,13 +134,13 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'test command')
|
print(f'test command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.get_trigger()}'))
|
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.trigger}'))
|
||||||
orchestrator.start_polling(app)
|
orchestrator.start_polling(app)
|
||||||
|
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
@@ -149,17 +155,17 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'test command')
|
print(f'test command')
|
||||||
|
|
||||||
@router.command(Command('more'))
|
@router.command(Command('more'))
|
||||||
def test(response: Response):
|
def test1(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'more command')
|
print(f'more command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
app.include_router(router)
|
app.include_router(router)
|
||||||
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.get_trigger()}'))
|
app.set_unknown_command_handler(lambda command: print(f'Unknown command: {command.trigger}'))
|
||||||
orchestrator.start_polling(app)
|
orchestrator.start_polling(app)
|
||||||
|
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
@@ -174,7 +180,7 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'test command')
|
print(f'test command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
@@ -195,7 +201,7 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'test command')
|
print(f'test command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
@@ -216,7 +222,7 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test', flags=PredefinedFlags.PORT))
|
@router.command(Command('test', flags=PredefinedFlags.PORT))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print('test command')
|
print('test command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
@@ -227,4 +233,25 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
|
|
||||||
output = mock_stdout.getvalue()
|
output = mock_stdout.getvalue()
|
||||||
|
|
||||||
self.assertIn("\nRepeated input flags: \"test --port 22 --port 33\"\n", output)
|
self.assertIn('Repeated input flags: "test --port 22 --port 33"', output)
|
||||||
|
|
||||||
|
@patch("builtins.input", side_effect=["test --help", "q"])
|
||||||
|
@patch("sys.stdout", new_callable=io.StringIO)
|
||||||
|
def test_input_correct_command_with_unregistered_flag3(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
||||||
|
router = Router()
|
||||||
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
|
@router.command(Command('test'))
|
||||||
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
|
undefined_flag = response.input_flags.get_flag_by_name('help')
|
||||||
|
if undefined_flag and undefined_flag.status == ValidationStatus.UNDEFINED:
|
||||||
|
print(f'test command with undefined flag: {undefined_flag.string_entity}')
|
||||||
|
|
||||||
|
app = App(override_system_messages=True,
|
||||||
|
print_func=print)
|
||||||
|
app.include_router(router)
|
||||||
|
orchestrator.start_polling(app)
|
||||||
|
|
||||||
|
output = mock_stdout.getvalue()
|
||||||
|
|
||||||
|
self.assertIn('\ntest command with undefined flag: --help\n', output)
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import io
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
from argenta.app import App
|
from argenta.app import App
|
||||||
from argenta.command import Command
|
from argenta.command import Command, PredefinedFlags
|
||||||
|
from argenta.command.flag.models import PossibleValues, ValidationStatus
|
||||||
from argenta.response import Response
|
from argenta.response import Response
|
||||||
from argenta.router import Router
|
from argenta.router import Router
|
||||||
from argenta.orchestrator import Orchestrator
|
from argenta.orchestrator import Orchestrator
|
||||||
from argenta.command.flag import Flag
|
from argenta.command.flag import Flag
|
||||||
from argenta.command.flags import Flags
|
from argenta.command.flag.flags import Flags
|
||||||
from argenta.command.flag.defaults import PredefinedFlags
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print('test command')
|
print('test command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
@@ -43,7 +43,7 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print('test command')
|
print('test command')
|
||||||
|
|
||||||
app = App(ignore_command_register=True,
|
app = App(ignore_command_register=True,
|
||||||
@@ -62,11 +62,13 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
def test_input_correct_command_with_custom_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
def test_input_correct_command_with_custom_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
||||||
router = Router()
|
router = Router()
|
||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
flag = Flag('help', '--', False)
|
flag = Flag('help', prefix='--', possible_values=PossibleValues.NEITHER)
|
||||||
|
|
||||||
@router.command(Command('test', flags=flag))
|
@router.command(Command('test', flags=flag))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'\nhelp for {response.valid_flags.get_flag('help').get_name()} flag\n')
|
valid_flag = response.input_flags.get_flag_by_name('help')
|
||||||
|
if valid_flag and valid_flag.status == ValidationStatus.VALID:
|
||||||
|
print(f'\nhelp for {valid_flag.name} flag\n')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -82,12 +84,13 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
def test_input_correct_command_with_custom_flag2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
def test_input_correct_command_with_custom_flag2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
||||||
router = Router()
|
router = Router()
|
||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
flag = Flag('port', '--', re.compile(r'^\d{1,5}$'))
|
flag = Flag('port', prefix='--', possible_values=re.compile(r'^\d{1,5}$'))
|
||||||
|
|
||||||
@router.command(Command('test', flags=flag))
|
@router.command(Command('test', flags=flag))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
input_flag = response.valid_flags.get_flag('port')
|
valid_flag = response.input_flags.get_flag_by_name('port')
|
||||||
print(f'flag value for {input_flag.get_name()} flag : {input_flag.get_value()}')
|
if valid_flag and valid_flag.status == ValidationStatus.VALID:
|
||||||
|
print(f'flag value for {valid_flag.name} flag : {valid_flag.input_value}')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -107,8 +110,10 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
flag = PredefinedFlags.SHORT_HELP
|
flag = PredefinedFlags.SHORT_HELP
|
||||||
|
|
||||||
@router.command(Command('test', flags=flag))
|
@router.command(Command('test', flags=flag))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'help for {response.valid_flags.get_flag('H').get_name()} flag')
|
valid_flag = response.input_flags.get_flag_by_name('H')
|
||||||
|
if valid_flag and valid_flag.status == ValidationStatus.VALID:
|
||||||
|
print(f'help for {valid_flag.name} flag')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -128,8 +133,9 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
flag = PredefinedFlags.INFO
|
flag = PredefinedFlags.INFO
|
||||||
|
|
||||||
@router.command(Command('test', flags=flag))
|
@router.command(Command('test', flags=flag))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
if response.valid_flags.get_flag('info'):
|
valid_flag = response.input_flags.get_flag_by_name('info')
|
||||||
|
if valid_flag and valid_flag.status == ValidationStatus.VALID:
|
||||||
print('info about test command')
|
print('info about test command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
@@ -150,8 +156,10 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
flag = PredefinedFlags.HOST
|
flag = PredefinedFlags.HOST
|
||||||
|
|
||||||
@router.command(Command('test', flags=flag))
|
@router.command(Command('test', flags=flag))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'connecting to host {response.valid_flags.get_flag('host').get_value()}')
|
valid_flag = response.input_flags.get_flag_by_name('host')
|
||||||
|
if valid_flag and valid_flag.status == ValidationStatus.VALID:
|
||||||
|
print(f'connecting to host {valid_flag.input_value}')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -168,12 +176,14 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
def test_input_correct_command_with_two_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
def test_input_correct_command_with_two_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
|
||||||
router = Router()
|
router = Router()
|
||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
flags = Flags(PredefinedFlags.HOST, PredefinedFlags.PORT)
|
flags = Flags([PredefinedFlags.HOST, PredefinedFlags.PORT])
|
||||||
|
|
||||||
@router.command(Command('test', flags=flags))
|
@router.command(Command('test', flags=flags))
|
||||||
def test(response: Response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
valid_flags = response.valid_flags
|
host_flag = response.input_flags.get_flag_by_name('host')
|
||||||
print(f'connecting to host {valid_flags.get_flag('host').get_value()} and port {valid_flags.get_flag('port').get_value()}')
|
port_flag = response.input_flags.get_flag_by_name('port')
|
||||||
|
if (host_flag and host_flag.status == ValidationStatus.VALID) and (port_flag and port_flag.status == ValidationStatus.VALID):
|
||||||
|
print(f'connecting to host {host_flag.input_value} and port {port_flag.input_value}')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
print_func=print)
|
print_func=print)
|
||||||
@@ -192,11 +202,11 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'test command')
|
print(f'test command')
|
||||||
|
|
||||||
@router.command(Command('some'))
|
@router.command(Command('some'))
|
||||||
def test2(response):
|
def test2(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'some command')
|
print(f'some command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
@@ -216,15 +226,15 @@ class TestSystemHandlerNormalWork(TestCase):
|
|||||||
orchestrator = Orchestrator()
|
orchestrator = Orchestrator()
|
||||||
|
|
||||||
@router.command(Command('test'))
|
@router.command(Command('test'))
|
||||||
def test(response):
|
def test(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'test command')
|
print(f'test command')
|
||||||
|
|
||||||
@router.command(Command('some'))
|
@router.command(Command('some'))
|
||||||
def test(response):
|
def test1(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'some command')
|
print(f'some command')
|
||||||
|
|
||||||
@router.command(Command('more'))
|
@router.command(Command('more'))
|
||||||
def test(response):
|
def test2(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
print(f'more command')
|
print(f'more command')
|
||||||
|
|
||||||
app = App(override_system_messages=True,
|
app = App(override_system_messages=True,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ from argenta.app import App
|
|||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
from argenta.router import Router
|
||||||
|
|
||||||
|
|
||||||
class MyTestCase(unittest.TestCase):
|
class MyTestCase(unittest.TestCase):
|
||||||
def test_is_exit_command1(self):
|
def test_is_exit_command1(self):
|
||||||
@@ -33,25 +35,25 @@ class MyTestCase(unittest.TestCase):
|
|||||||
def test_is_unknown_command1(self):
|
def test_is_unknown_command1(self):
|
||||||
app = App()
|
app = App()
|
||||||
app.set_unknown_command_handler(lambda command: None)
|
app.set_unknown_command_handler(lambda command: None)
|
||||||
app._all_registered_triggers_in_lower = ['fr', 'tr', 'de']
|
app._current_matching_triggers_with_routers = {'fr': Router(), 'tr': Router(), 'de': Router()}
|
||||||
self.assertEqual(app._is_unknown_command(InputCommand('fr')), False)
|
self.assertEqual(app._is_unknown_command(InputCommand('fr')), False)
|
||||||
|
|
||||||
def test_is_unknown_command2(self):
|
def test_is_unknown_command2(self):
|
||||||
app = App()
|
app = App()
|
||||||
app.set_unknown_command_handler(lambda command: None)
|
app.set_unknown_command_handler(lambda command: None)
|
||||||
app._all_registered_triggers_in_lower = ['fr', 'tr', 'de']
|
app._current_matching_triggers_with_routers = {'fr': Router(), 'tr': Router(), 'de': Router()}
|
||||||
self.assertEqual(app._is_unknown_command(InputCommand('cr')), True)
|
self.assertEqual(app._is_unknown_command(InputCommand('cr')), True)
|
||||||
|
|
||||||
def test_is_unknown_command3(self):
|
def test_is_unknown_command3(self):
|
||||||
app = App(ignore_command_register=False)
|
app = App(ignore_command_register=False)
|
||||||
app.set_unknown_command_handler(lambda command: None)
|
app.set_unknown_command_handler(lambda command: None)
|
||||||
app._all_registered_triggers_in_default_case = ['Pr', 'tW', 'deQW']
|
app._current_matching_triggers_with_routers = {'Pr': Router(), 'tW': Router(), 'deQW': Router()}
|
||||||
self.assertEqual(app._is_unknown_command(InputCommand('pr')), True)
|
self.assertEqual(app._is_unknown_command(InputCommand('pr')), True)
|
||||||
|
|
||||||
def test_is_unknown_command4(self):
|
def test_is_unknown_command4(self):
|
||||||
app = App(ignore_command_register=False)
|
app = App(ignore_command_register=False)
|
||||||
app.set_unknown_command_handler(lambda command: None)
|
app.set_unknown_command_handler(lambda command: None)
|
||||||
app._all_registered_triggers_in_default_case = ['Pr', 'tW', 'deQW']
|
app._current_matching_triggers_with_routers = {'Pr': Router(), 'tW': Router(), 'deQW': Router()}
|
||||||
self.assertEqual(app._is_unknown_command(InputCommand('tW')), False)
|
self.assertEqual(app._is_unknown_command(InputCommand('tW')), False)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from argenta.command.flag import Flag, InputFlag
|
from argenta.command.flag import Flag, InputFlag
|
||||||
from argenta.command.flags import Flags
|
from argenta.command.flag.flags import Flags
|
||||||
from argenta.command.models import InputCommand, Command
|
from argenta.command.flag.models import PossibleValues
|
||||||
|
from argenta.command.models import InputCommand, Command, ValidationStatus
|
||||||
from argenta.command.exceptions import (UnprocessedInputFlagException,
|
from argenta.command.exceptions import (UnprocessedInputFlagException,
|
||||||
RepeatedInputFlagsException,
|
RepeatedInputFlagsException,
|
||||||
EmptyInputCommandException)
|
EmptyInputCommandException)
|
||||||
@@ -11,7 +12,7 @@ import re
|
|||||||
|
|
||||||
class TestInputCommand(unittest.TestCase):
|
class TestInputCommand(unittest.TestCase):
|
||||||
def test_parse_correct_raw_command(self):
|
def test_parse_correct_raw_command(self):
|
||||||
self.assertEqual(InputCommand.parse('ssh --host 192.168.0.3').get_trigger(), 'ssh')
|
self.assertEqual(InputCommand.parse('ssh --host 192.168.0.3').trigger, 'ssh')
|
||||||
|
|
||||||
def test_parse_raw_command_without_flag_name_with_value(self):
|
def test_parse_raw_command_without_flag_name_with_value(self):
|
||||||
with self.assertRaises(UnprocessedInputFlagException):
|
with self.assertRaises(UnprocessedInputFlagException):
|
||||||
@@ -27,35 +28,35 @@ class TestInputCommand(unittest.TestCase):
|
|||||||
|
|
||||||
def test_validate_valid_input_flag1(self):
|
def test_validate_valid_input_flag1(self):
|
||||||
command = Command('some', flags=Flag('test'))
|
command = Command('some', flags=Flag('test'))
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('test')), 'Valid')
|
self.assertEqual(command.validate_input_flag(InputFlag('test', input_value=None, status=None)), ValidationStatus.VALID)
|
||||||
|
|
||||||
def test_validate_valid_input_flag2(self):
|
def test_validate_valid_input_flag2(self):
|
||||||
command = Command('some', flags=Flags(Flag('test'), Flag('more')))
|
command = Command('some', flags=Flags([Flag('test'), Flag('more')]))
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('more')), 'Valid')
|
self.assertEqual(command.validate_input_flag(InputFlag('more', input_value=None, status=None)), ValidationStatus.VALID)
|
||||||
|
|
||||||
def test_validate_undefined_input_flag1(self):
|
def test_validate_undefined_input_flag1(self):
|
||||||
command = Command('some', flags=Flag('test'))
|
command = Command('some', flags=Flag('test'))
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('more')), 'Undefined')
|
self.assertEqual(command.validate_input_flag(InputFlag('more', input_value=None, status=None)), ValidationStatus.UNDEFINED)
|
||||||
|
|
||||||
def test_validate_undefined_input_flag2(self):
|
def test_validate_undefined_input_flag2(self):
|
||||||
command = Command('some', flags=Flags(Flag('test'), Flag('more')))
|
command = Command('some', flags=Flags([Flag('test'), Flag('more')]))
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('case')), 'Undefined')
|
self.assertEqual(command.validate_input_flag(InputFlag('case', input_value=None, status=None)), ValidationStatus.UNDEFINED)
|
||||||
|
|
||||||
def test_validate_undefined_input_flag3(self):
|
def test_validate_undefined_input_flag3(self):
|
||||||
command = Command('some')
|
command = Command('some')
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('case')), 'Undefined')
|
self.assertEqual(command.validate_input_flag(InputFlag('case', input_value=None, status=None)), ValidationStatus.UNDEFINED)
|
||||||
|
|
||||||
def test_invalid_input_flag1(self):
|
def test_invalid_input_flag1(self):
|
||||||
command = Command('some', flags=Flag('test', possible_values=False))
|
command = Command('some', flags=Flag('test', possible_values=PossibleValues.NEITHER))
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('test', value='example')), 'Invalid')
|
self.assertEqual(command.validate_input_flag(InputFlag('test', input_value='example', status=None)), ValidationStatus.INVALID)
|
||||||
|
|
||||||
def test_invalid_input_flag2(self):
|
def test_invalid_input_flag2(self):
|
||||||
command = Command('some', flags=Flag('test', possible_values=['some', 'case']))
|
command = Command('some', flags=Flag('test', possible_values=['some', 'case']))
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('test', value='slay')), 'Invalid')
|
self.assertEqual(command.validate_input_flag(InputFlag('test', input_value='slay', status=None)), ValidationStatus.INVALID)
|
||||||
|
|
||||||
def test_invalid_input_flag3(self):
|
def test_invalid_input_flag3(self):
|
||||||
command = Command('some', flags=Flag('test', possible_values=re.compile(r'^ex\d{, 2}op$')))
|
command = Command('some', flags=Flag('test', possible_values=re.compile(r'^ex\d{, 2}op$')))
|
||||||
self.assertEqual(command.validate_input_flag(InputFlag('test', value='example')), 'Invalid')
|
self.assertEqual(command.validate_input_flag(InputFlag('test', input_value='example', status=None)), ValidationStatus.INVALID)
|
||||||
|
|
||||||
def test_isinstance_parse_correct_raw_command(self):
|
def test_isinstance_parse_correct_raw_command(self):
|
||||||
cmd = InputCommand.parse('ssh --host 192.168.0.3')
|
cmd = InputCommand.parse('ssh --host 192.168.0.3')
|
||||||
|
|||||||
@@ -6,11 +6,11 @@ import unittest
|
|||||||
class TestDividingLine(unittest.TestCase):
|
class TestDividingLine(unittest.TestCase):
|
||||||
def test_get_static_dividing_line_full_line(self):
|
def test_get_static_dividing_line_full_line(self):
|
||||||
line = StaticDividingLine('-')
|
line = StaticDividingLine('-')
|
||||||
self.assertEqual(line.get_full_static_line(True).count('-'), 25)
|
self.assertEqual(line.get_full_static_line(is_override=True).count('-'), 25)
|
||||||
|
|
||||||
def test_get_dynamic_dividing_line_full_line(self):
|
def test_get_dynamic_dividing_line_full_line(self):
|
||||||
line = DynamicDividingLine()
|
line = DynamicDividingLine()
|
||||||
self.assertEqual(line.get_full_dynamic_line(20, True).count('-'), 20)
|
self.assertEqual(line.get_full_dynamic_line(length=20, is_override=True).count('-'), 20)
|
||||||
|
|
||||||
def test_get_dividing_line_unit_part(self):
|
def test_get_dividing_line_unit_part(self):
|
||||||
line = StaticDividingLine('')
|
line = StaticDividingLine('')
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from argenta.command.flag import Flag, InputFlag
|
from argenta.command.flag import Flag, InputFlag, PossibleValues
|
||||||
from argenta.command.flags import InputFlags, Flags
|
from argenta.command.flag.flags import InputFlags, Flags
|
||||||
|
|
||||||
import unittest
|
import unittest
|
||||||
import re
|
import re
|
||||||
@@ -7,35 +7,34 @@ import re
|
|||||||
|
|
||||||
class TestFlag(unittest.TestCase):
|
class TestFlag(unittest.TestCase):
|
||||||
def test_get_string_entity(self):
|
def test_get_string_entity(self):
|
||||||
self.assertEqual(Flag(name='test').get_string_entity(),
|
self.assertEqual(Flag(name='test').string_entity,
|
||||||
'--test')
|
'--test')
|
||||||
|
|
||||||
def test_get_string_entity2(self):
|
def test_get_string_entity2(self):
|
||||||
self.assertEqual(Flag(name='test',
|
self.assertEqual(Flag(name='test',
|
||||||
prefix='---').get_string_entity(),
|
prefix='---').string_entity,
|
||||||
'---test')
|
'---test')
|
||||||
|
|
||||||
def test_get_flag_name(self):
|
def test_get_flag_name(self):
|
||||||
self.assertEqual(Flag(name='test').get_name(),
|
self.assertEqual(Flag(name='test').name,
|
||||||
'test')
|
'test')
|
||||||
|
|
||||||
def test_get_flag_prefix(self):
|
def test_get_flag_prefix(self):
|
||||||
self.assertEqual(Flag(name='test').get_prefix(),
|
self.assertEqual(Flag(name='test').prefix,
|
||||||
'--')
|
'--')
|
||||||
|
|
||||||
def test_get_flag_prefix2(self):
|
def test_get_flag_prefix2(self):
|
||||||
self.assertEqual(Flag(name='test',
|
self.assertEqual(Flag(name='test',
|
||||||
prefix='--').get_prefix(),
|
prefix='--').prefix,
|
||||||
'--')
|
'--')
|
||||||
|
|
||||||
def test_get_flag_value_without_set(self):
|
def test_get_flag_value_without_set(self):
|
||||||
self.assertEqual(InputFlag(name='test').get_value(),
|
self.assertEqual(InputFlag(name='test', input_value=None, status=None).input_value,
|
||||||
None)
|
None)
|
||||||
|
|
||||||
def test_get_flag_value_with_set(self):
|
def test_get_flag_value_with_set(self):
|
||||||
flag = InputFlag(name='test')
|
flag = InputFlag(name='test', input_value='example', status=None)
|
||||||
flag.set_value('example')
|
self.assertEqual(flag.input_value, 'example')
|
||||||
self.assertEqual(flag.get_value(), 'example')
|
|
||||||
|
|
||||||
def test_validate_incorrect_flag_value_with_list_of_possible_flag_values(self):
|
def test_validate_incorrect_flag_value_with_list_of_possible_flag_values(self):
|
||||||
flag = Flag(name='test', possible_values=['1', '2', '3'])
|
flag = Flag(name='test', possible_values=['1', '2', '3'])
|
||||||
@@ -54,37 +53,37 @@ class TestFlag(unittest.TestCase):
|
|||||||
self.assertEqual(flag.validate_input_flag_value('192.168.9.8'), True)
|
self.assertEqual(flag.validate_input_flag_value('192.168.9.8'), True)
|
||||||
|
|
||||||
def test_validate_correct_empty_flag_value_without_possible_flag_values(self):
|
def test_validate_correct_empty_flag_value_without_possible_flag_values(self):
|
||||||
flag = Flag(name='test', possible_values=False)
|
flag = Flag(name='test', possible_values=PossibleValues.NEITHER)
|
||||||
self.assertEqual(flag.validate_input_flag_value(None), True)
|
self.assertEqual(flag.validate_input_flag_value(None), True)
|
||||||
|
|
||||||
def test_validate_correct_empty_flag_value_with_possible_flag_values(self):
|
def test_validate_correct_empty_flag_value_with_possible_flag_values(self):
|
||||||
flag = Flag(name='test', possible_values=True)
|
flag = Flag(name='test', possible_values=PossibleValues.NEITHER)
|
||||||
self.assertEqual(flag.validate_input_flag_value(None), True)
|
self.assertEqual(flag.validate_input_flag_value(None), True)
|
||||||
|
|
||||||
def test_validate_incorrect_random_flag_value_without_possible_flag_values(self):
|
def test_validate_incorrect_random_flag_value_without_possible_flag_values(self):
|
||||||
flag = Flag(name='test', possible_values=False)
|
flag = Flag(name='test', possible_values=PossibleValues.NEITHER)
|
||||||
self.assertEqual(flag.validate_input_flag_value('random value'), False)
|
self.assertEqual(flag.validate_input_flag_value('random value'), False)
|
||||||
|
|
||||||
def test_validate_correct_random_flag_value_with_possible_flag_values(self):
|
def test_validate_correct_random_flag_value_with_possible_flag_values(self):
|
||||||
flag = Flag(name='test', possible_values=True)
|
flag = Flag(name='test', possible_values=PossibleValues.ALL)
|
||||||
self.assertEqual(flag.validate_input_flag_value('random value'), True)
|
self.assertEqual(flag.validate_input_flag_value('random value'), True)
|
||||||
|
|
||||||
def test_get_input_flag1(self):
|
def test_get_input_flag1(self):
|
||||||
flag = InputFlag(name='test')
|
flag = InputFlag(name='test', input_value=None, status=None)
|
||||||
input_flags = InputFlags(flag)
|
input_flags = InputFlags([flag])
|
||||||
self.assertEqual(input_flags.get_flag('test'), flag)
|
self.assertEqual(input_flags.get_flag_by_name('test'), flag)
|
||||||
|
|
||||||
def test_get_input_flag2(self):
|
def test_get_input_flag2(self):
|
||||||
flag = InputFlag(name='test')
|
flag = InputFlag(name='test', input_value=None, status=None)
|
||||||
flag2 = InputFlag(name='some')
|
flag2 = InputFlag(name='some', input_value=None, status=None)
|
||||||
input_flags = InputFlags(flag, flag2)
|
input_flags = InputFlags([flag, flag2])
|
||||||
self.assertEqual(input_flags.get_flag('some'), flag2)
|
self.assertEqual(input_flags.get_flag_by_name('some'), flag2)
|
||||||
|
|
||||||
def test_get_undefined_input_flag(self):
|
def test_get_undefined_input_flag(self):
|
||||||
flag = InputFlag(name='test')
|
flag = InputFlag(name='test', input_value=None, status=None)
|
||||||
flag2 = InputFlag(name='some')
|
flag2 = InputFlag(name='some', input_value=None, status=None)
|
||||||
input_flags = InputFlags(flag, flag2)
|
input_flags = InputFlags([flag, flag2])
|
||||||
self.assertEqual(input_flags.get_flag('case'), None)
|
self.assertEqual(input_flags.get_flag_by_name('case'), None)
|
||||||
|
|
||||||
def test_get_flags(self):
|
def test_get_flags(self):
|
||||||
flags = Flags()
|
flags = Flags()
|
||||||
@@ -94,18 +93,18 @@ class TestFlag(unittest.TestCase):
|
|||||||
Flag('test3'),
|
Flag('test3'),
|
||||||
]
|
]
|
||||||
flags.add_flags(list_of_flags)
|
flags.add_flags(list_of_flags)
|
||||||
self.assertEqual(flags.get_flags(),
|
self.assertEqual(flags.flags,
|
||||||
list_of_flags)
|
list_of_flags)
|
||||||
|
|
||||||
def test_add_flag(self):
|
def test_add_flag(self):
|
||||||
flags = Flags()
|
flags = Flags()
|
||||||
flags.add_flag(Flag('test'))
|
flags.add_flag(Flag('test'))
|
||||||
self.assertEqual(len(flags.get_flags()), 1)
|
self.assertEqual(len(flags.flags), 1)
|
||||||
|
|
||||||
def test_add_flags(self):
|
def test_add_flags(self):
|
||||||
flags = Flags()
|
flags = Flags()
|
||||||
flags.add_flags([Flag('test'), Flag('test2')])
|
flags.add_flags([Flag('test'), Flag('test2')])
|
||||||
self.assertEqual(len(flags.get_flags()), 2)
|
self.assertEqual(len(flags.flags), 2)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
from argenta.command.flag import InputFlag, Flag
|
from argenta.command.flag import InputFlag, Flag
|
||||||
from argenta.command.flags import Flags, InputFlags, UndefinedInputFlags, InvalidValueInputFlags, ValidInputFlags
|
from argenta.command.flag.flags import Flags, InputFlags
|
||||||
|
from argenta.command.flag.models import PossibleValues, ValidationStatus
|
||||||
|
from argenta.response.entity import Response
|
||||||
from argenta.router import Router
|
from argenta.router import Router
|
||||||
from argenta.command import Command
|
from argenta.command import Command
|
||||||
|
from argenta.router.entity import _structuring_input_flags, _validate_command, _validate_func_args # pyright: ignore[reportPrivateUsage]
|
||||||
from argenta.router.exceptions import (TriggerContainSpacesException,
|
from argenta.router.exceptions import (TriggerContainSpacesException,
|
||||||
RepeatedFlagNameException,
|
RepeatedFlagNameException,
|
||||||
TooManyTransferredArgsException,
|
TooManyTransferredArgsException,
|
||||||
@@ -13,106 +16,98 @@ import re
|
|||||||
|
|
||||||
class TestRouter(unittest.TestCase):
|
class TestRouter(unittest.TestCase):
|
||||||
def test_register_command_with_spaces_in_trigger(self):
|
def test_register_command_with_spaces_in_trigger(self):
|
||||||
router = Router()
|
|
||||||
with self.assertRaises(TriggerContainSpacesException):
|
with self.assertRaises(TriggerContainSpacesException):
|
||||||
router._validate_command(Command(trigger='command with spaces'))
|
_validate_command(Command(trigger='command with spaces'))
|
||||||
|
|
||||||
def test_register_command_with_repeated_flags(self):
|
def test_register_command_with_repeated_flags(self):
|
||||||
router = Router()
|
|
||||||
with self.assertRaises(RepeatedFlagNameException):
|
with self.assertRaises(RepeatedFlagNameException):
|
||||||
router._validate_command(Command(trigger='command', flags=Flags(Flag('test'), Flag('test'))))
|
_validate_command(Command(trigger='command', flags=Flags([Flag('test'), Flag('test')])))
|
||||||
|
|
||||||
def test_structuring_input_flags1(self):
|
def test_structuring_input_flags1(self):
|
||||||
router = Router()
|
|
||||||
cmd = Command('cmd')
|
cmd = Command('cmd')
|
||||||
input_flags = InputFlags(InputFlag('ssh'))
|
input_flags = InputFlags([InputFlag('ssh', input_value=None, status=None)])
|
||||||
self.assertEqual(router._structuring_input_flags(cmd, input_flags).undefined_flags, UndefinedInputFlags(InputFlag('ssh')))
|
self.assertEqual(_structuring_input_flags(cmd, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value=None, status=ValidationStatus.UNDEFINED)]))
|
||||||
|
|
||||||
def test_structuring_input_flags2(self):
|
def test_structuring_input_flags2(self):
|
||||||
router = Router()
|
|
||||||
cmd = Command('cmd')
|
cmd = Command('cmd')
|
||||||
input_flags = InputFlags(InputFlag('ssh', value='some'))
|
input_flags = InputFlags([InputFlag('ssh', input_value='some', status=None)])
|
||||||
self.assertEqual(router._structuring_input_flags(cmd, input_flags).undefined_flags, UndefinedInputFlags(InputFlag('ssh', value='some')))
|
self.assertEqual(_structuring_input_flags(cmd, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value='some', status=ValidationStatus.UNDEFINED)]))
|
||||||
|
|
||||||
def test_structuring_input_flags3(self):
|
def test_structuring_input_flags3(self):
|
||||||
router = Router()
|
|
||||||
cmd = Command('cmd', flags=Flag('port'))
|
cmd = Command('cmd', flags=Flag('port'))
|
||||||
input_flags = InputFlags(InputFlag('ssh', value='some2'))
|
input_flags = InputFlags([InputFlag('ssh', input_value='some2', status=None)])
|
||||||
self.assertEqual(router._structuring_input_flags(cmd, input_flags).undefined_flags, UndefinedInputFlags(InputFlag('ssh', value='some2')))
|
self.assertEqual(_structuring_input_flags(cmd, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value='some2', status=ValidationStatus.UNDEFINED)]))
|
||||||
|
|
||||||
def test_structuring_input_flags4(self):
|
def test_structuring_input_flags4(self):
|
||||||
router = Router()
|
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=False))
|
input_flags = InputFlags([InputFlag('ssh', input_value='some3', status=None)])
|
||||||
input_flags = InputFlags(InputFlag('ssh', value='some3'))
|
self.assertEqual(_structuring_input_flags(command, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value='some3', status=ValidationStatus.INVALID)]))
|
||||||
self.assertEqual(router._structuring_input_flags(command, input_flags).invalid_value_flags, InvalidValueInputFlags(InputFlag('ssh', value='some3')))
|
|
||||||
|
|
||||||
def test_structuring_input_flags5(self):
|
def test_structuring_input_flags5(self):
|
||||||
router = Router()
|
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'some[1-5]$')))
|
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'some[1-5]$')))
|
||||||
input_flags = InputFlags(InputFlag('ssh', value='some40'))
|
input_flags = InputFlags([InputFlag('ssh', input_value='some40', status=None)])
|
||||||
self.assertEqual(router._structuring_input_flags(command, input_flags).invalid_value_flags, InvalidValueInputFlags(InputFlag('ssh', value='some40')))
|
self.assertEqual(_structuring_input_flags(command, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value='some40', status=ValidationStatus.INVALID)]))
|
||||||
|
|
||||||
def test_structuring_input_flags6(self):
|
def test_structuring_input_flags6(self):
|
||||||
router = Router()
|
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=['example']))
|
command = Command('cmd', flags=Flag('ssh', possible_values=['example']))
|
||||||
input_flags = InputFlags(InputFlag('ssh', value='example2'))
|
input_flags = InputFlags([InputFlag('ssh', input_value='example2', status=None)])
|
||||||
self.assertEqual(router._structuring_input_flags(command, input_flags).invalid_value_flags, InvalidValueInputFlags(InputFlag('ssh', value='example2')))
|
self.assertEqual(_structuring_input_flags(command, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value='example2', status=ValidationStatus.INVALID)]))
|
||||||
|
|
||||||
def test_structuring_input_flags7(self):
|
def test_structuring_input_flags7(self):
|
||||||
command = Command('cmd', flags=Flag('port'))
|
command = Command('cmd', flags=Flag('port'))
|
||||||
input_flags = InputFlags(InputFlag('port', value='some2'))
|
input_flags = InputFlags([InputFlag('port', input_value='some2', status=None)])
|
||||||
self.assertEqual(Router()._structuring_input_flags(command, input_flags).valid_flags, ValidInputFlags(InputFlag('port', value='some2')))
|
self.assertEqual(_structuring_input_flags(command, input_flags).input_flags, InputFlags([InputFlag('port', input_value='some2', status=ValidationStatus.VALID)]))
|
||||||
|
|
||||||
def test_structuring_input_flags8(self):
|
def test_structuring_input_flags8(self):
|
||||||
command = Command('cmd', flags=Flag('port', possible_values=['some2', 'some3']))
|
command = Command('cmd', flags=Flag('port', possible_values=['some2', 'some3']))
|
||||||
input_flags = InputFlags(InputFlag('port', value='some2'))
|
input_flags = InputFlags([InputFlag('port', input_value='some2', status=None)])
|
||||||
self.assertEqual(Router()._structuring_input_flags(command, input_flags).valid_flags, ValidInputFlags(InputFlag('port', value='some2')))
|
self.assertEqual(_structuring_input_flags(command, input_flags).input_flags, InputFlags([InputFlag('port', input_value='some2', status=ValidationStatus.VALID)]))
|
||||||
|
|
||||||
def test_structuring_input_flags9(self):
|
def test_structuring_input_flags9(self):
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'more[1-5]$')))
|
command = Command('cmd', flags=Flag('ssh', possible_values=re.compile(r'more[1-5]$')))
|
||||||
input_flags = InputFlags(InputFlag('ssh', value='more5'))
|
input_flags = InputFlags([InputFlag('ssh', input_value='more5', status=None)])
|
||||||
self.assertEqual(Router()._structuring_input_flags(command, input_flags).valid_flags, ValidInputFlags(InputFlag('ssh', value='more5')))
|
self.assertEqual(_structuring_input_flags(command, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value='more5', status=ValidationStatus.VALID)]))
|
||||||
|
|
||||||
def test_structuring_input_flags10(self):
|
def test_structuring_input_flags10(self):
|
||||||
command = Command('cmd', flags=Flag('ssh', possible_values=False))
|
command = Command('cmd', flags=Flag('ssh', possible_values=PossibleValues.NEITHER))
|
||||||
input_flags = InputFlags(InputFlag('ssh'))
|
input_flags = InputFlags([InputFlag('ssh', input_value=None, status=None)])
|
||||||
self.assertEqual(Router()._structuring_input_flags(command, input_flags).valid_flags, ValidInputFlags(InputFlag('ssh')))
|
self.assertEqual(_structuring_input_flags(command, input_flags).input_flags, InputFlags([InputFlag('ssh', input_value=None, status=ValidationStatus.VALID)]))
|
||||||
|
|
||||||
def test_validate_incorrect_func_args1(self):
|
def test_validate_incorrect_func_args1(self):
|
||||||
def handler():
|
def handler():
|
||||||
pass
|
pass
|
||||||
with self.assertRaises(RequiredArgumentNotPassedException):
|
with self.assertRaises(RequiredArgumentNotPassedException):
|
||||||
Router()._validate_func_args(handler)
|
_validate_func_args(handler) # pyright: ignore[reportArgumentType]
|
||||||
|
|
||||||
def test_validate_incorrect_func_args2(self):
|
def test_validate_incorrect_func_args2(self):
|
||||||
def handler(args, kwargs):
|
def handler(args, kwargs): # pyright: ignore[reportMissingParameterType, reportUnknownParameterType]
|
||||||
pass
|
pass
|
||||||
with self.assertRaises(TooManyTransferredArgsException):
|
with self.assertRaises(TooManyTransferredArgsException):
|
||||||
Router()._validate_func_args(handler)
|
_validate_func_args(handler) # pyright: ignore[reportArgumentType]
|
||||||
|
|
||||||
def test_get_router_aliases(self):
|
def test_get_router_aliases(self):
|
||||||
router = Router()
|
router = Router()
|
||||||
@router.command(Command('some', aliases=['test', 'case']))
|
@router.command(Command('some', aliases=['test', 'case']))
|
||||||
def handler(response):
|
def handler(response: Response) -> None: # pyright: ignore[reportUnusedFunction]
|
||||||
pass
|
pass
|
||||||
self.assertListEqual(router.get_aliases(), ['test', 'case'])
|
self.assertListEqual(router.aliases, ['test', 'case'])
|
||||||
|
|
||||||
def test_get_router_aliases2(self):
|
def test_get_router_aliases2(self):
|
||||||
router = Router()
|
router = Router()
|
||||||
@router.command(Command('some', aliases=['test', 'case']))
|
@router.command(Command('some', aliases=['test', 'case']))
|
||||||
def handler(response):
|
def handler(response: Response): # pyright: ignore[reportUnusedFunction]
|
||||||
pass
|
pass
|
||||||
@router.command(Command('ext', aliases=['more', 'foo']))
|
@router.command(Command('ext', aliases=['more', 'foo']))
|
||||||
def handler2(response):
|
def handler2(response: Response): # pyright: ignore[reportUnusedFunction]
|
||||||
pass
|
pass
|
||||||
self.assertListEqual(router.get_aliases(), ['test', 'case', 'more', 'foo'])
|
self.assertListEqual(router.aliases, ['test', 'case', 'more', 'foo'])
|
||||||
|
|
||||||
def test_get_router_aliases3(self):
|
def test_get_router_aliases3(self):
|
||||||
router = Router()
|
router = Router()
|
||||||
@router.command(Command('some'))
|
@router.command(Command('some'))
|
||||||
def handler(response):
|
def handler(response: Response): # pyright: ignore[reportUnusedFunction]
|
||||||
pass
|
pass
|
||||||
self.assertListEqual(router.get_aliases(), [])
|
self.assertListEqual(router.aliases, [])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user