Python библиотека для создания модульных CLI приложений
Argenta — это современная Python-библиотека для создания модульных CLI приложений. Библиотека предоставляет простой и элегантный способ создания интерактивных консольных приложений с богатым функционалом, включая автодополнение команд, обработку флагов и аргументов, пользовательские обработчики ошибок и многое другое.
Основная цель Argenta — упростить процесс создания сложных консольных интерфейсов, предоставляя модульную архитектуру и интуитивно понятный API. Библиотека отлично подходит для создания CLI-утилит, административных интерфейсов и других приложений, требующих взаимодействия через терминал.
pip install argenta
Или с помощью Poetry:
poetry add argenta
Ниже приведены примеры создания простых CLI-приложений с использованием Argenta.
Создадим минимальное приложение с одной командой hello, которая выводит сообщение
"Hello, world!".
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!")
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()
Создадим приложение с командой ssh, которая обрабатывает флаги --host и
--port.
import re
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.orchestrator import Orchestrator
from argenta.command.flag.defaults import PredefinedFlags
from argenta.command.flag import Flags, Flag, InputFlags
router = Router()
registered_flags = Flags(PredefinedFlags.HOST,
Flag('port', '--', re.compile(r'^[0-9]{1,4}$')))
@router.command(Command("hello"))
def handler(response: Response):
print("Hello, world!")
@router.command(Command(trigger="ssh",
description='connect via ssh',
flags=registered_flags))
def handler_with_flags(response: Response):
# Работа с правильными флагами
for flag in response.valid_flags:
print(f'Flag name: {flag.get_name()}\n'
f'Flag value: {flag.get_value()}')
# Проверка статуса и работа с неопределенными флагами
if response.status == Status.UNDEFINED_FLAGS or response.status == Status.UNDEFINED_AND_INVALID_FLAGS:
print("\nНеопределенные флаги:")
for flag in response.undefined_flags:
print(f'Undefined flag: {flag.get_name()}')
# Работа с флагами с неверными значениями
if response.status == Status.INVALID_VALUE_FLAGS or response.status == Status.UNDEFINED_AND_INVALID_FLAGS:
print("\nФлаги с неверными значениями:")
for flag in response.invalid_value_flags:
print(f'Invalid value flag: {flag.get_name()} = {flag.get_value()}')
После запуска этого приложения вы сможете вводить команды вида:
ssh --host 192.168.1.10 --port 22
И ваш обработчик получит эти флаги и их значения для дальнейшей обработки. Даже при наличии неверных флагов или некорректных значений, управление все равно будет передано обработчику.
Библиотека Argenta построена на модульной архитектуре, которая обеспечивает гибкость и расширяемость. Ниже приведена схема взаимодействия основных компонентов:
Orchestrator и настройка аргументов запускаApp и настройка его параметровRouter и регистрация хэндлеровRouter) в приложенииorchestrator.start_polling(app)В этом разделе подробно описаны основные компоненты библиотеки, их методы и использование.
App — это основной класс приложения, который управляет жизненным циклом и поведением
CLI. Он отвечает за регистрацию маршрутизаторов, обработку системных событий и настройку пользовательского
интерфейса.
def __init__(prompt: str = 'What do you want to do?',
initial_message: str = 'Argenta',
farewell_message: str = 'See you',
exit_command: Command = Command('Q', 'Exit command'),
system_router_title: str | None = 'System points:',
ignore_command_register: bool = True,
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
repeat_command_groups: bool = True,
override_system_messages: bool = False,
autocompleter: AutoCompleter = AutoCompleter(),
print_func: Callable[[str], None] = Console().print) -> None
prompt - сообщение перед вводом командыinitial_message - сообщение при запуске приложенияfarewell_message - сообщение при завершении приложенияexit_command - команда для выхода из приложенияsystem_router_title - заголовок системного маршрутизатораignore_command_register - игнорировать ли регистр при вводе командdividing_line - объект разделительной линииrepeat_command_groups - повторять ли доступные команды и их описаниеoverride_system_messages - переопределять ли форматирование системных сообщенийautocompleter - объект автодополненияprint_func - функция вывода системных сообщенийdef set_description_message_pattern(_: Callable[[str, str], str]) -> None
Установка шаблона вывода доступных команд.
Параметры:
_ - шаблон вывода доступных командdef set_incorrect_input_syntax_handler(_: Callable[[str], None]) -> None
Установка обработчика некорректного синтаксиса при вводе команды.
Параметры:
_ - обработчик неправильных флаговdef set_repeated_input_flags_handler(_: Callable[[str], None]) -> None
Установка обработчика повторяющихся флагов при вводе команды.
Параметры:
_ - обработчик повторяющихся флаговdef set_unknown_command_handler(_: Callable[[str], None]) -> None
Установка обработчика неизвестных команд.
Параметры:
_ - обработчик неизвестных командdef set_empty_command_handler(_: Callable[[], None]) -> None
Установка обработчика пустых команд при вводе.
Параметры:
_ - обработчик пустых командdef set_exit_command_handler(_: Callable[[], None]) -> None
Установка обработчика команды выхода.
Параметры:
_ - обработчик команды выходаdef include_router(router: Router) -> None
Регистрация маршрутизатора в приложении.
Параметры:
router - регистрируемый маршрутизаторdef include_routers(*routers: Router) -> None
Регистрация нескольких маршрутизаторов в приложении.
Параметры:
routers - список регистрируемых маршрутизаторовdef add_message_on_startup(message: str) -> None
Добавление сообщения, которое будет отображаться при запуске приложения.
Параметры:
message - добавляемое сообщениеfrom argenta.app import App
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.app.dividing_line import DynamicDividingLine
# Создаем маршрутизатор с командой
router = Router("Основные команды")
@router.command(Command("hello", "Приветствие"))
def hello(response: Response):
print("Привет, мир!")
# Создаем приложение с настройками
app = App(
prompt="Введите команду:",
initial_message="Добро пожаловать в мое приложение!",
farewell_message="До свидания!",
dividing_line=DynamicDividingLine("="),
ignore_command_register=True
)
# Регистрируем маршрутизатор
app.include_router(router)
# Настраиваем обработчик неизвестных команд
app.set_unknown_command_handler(lambda cmd: print(f"Неизвестная команда: {cmd}"))
Router — это класс, который непосредственно настраивает и управляет обработчиками команд.
Он регистрирует команды и связывает их с функциями-обработчиками.
def __init__(title: str = None)
Параметры:
title - заголовок маршрутизатора, отображаемый при выводе доступных командdef command(command: Command) -> Callable
Декоратор для регистрации обработчика команды.
Параметры:
command - регистрируемая командаВозвращает:
def get_triggers() -> list[str]
Получение зарегистрированных триггеров.
Возвращает:
def get_aliases() -> list[str]
Получение зарегистрированных псевдонимов.
Возвращает:
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
# Создаем маршрутизатор с заголовком
router = Router("Управление файлами")
# Регистрируем команду без флагов
@router.command(Command("list", "Вывести список файлов"))
def list_files(response: Response):
import os
files = os.listdir()
for file in files:
print(file)
# Регистрируем команду с псевдонимами
@router.command(Command("info", "Информация о файле", aliases=["stat", "details"]))
def file_info(response: Response):
print("Эта команда показывает информацию о файле")
Command — это класс, представляющий команду, которая может и должна быть зарегистрирована в Router.
Определяет триггер, описание, флаги и псевдонимы команды.
def __init__(trigger: str,
description: str = None,
flags: Flag | Flags = None,
aliases: list[str] = None)
Параметры:
trigger - строка-триггер, которая при вводе пользователем указывает, что ввод соответствует
команде
description - описание командыflags - обрабатываемые флагиaliases - строковые синонимы для основного триггераfrom argenta.command import Command
from argenta.command.flag import Flag, Flags
import re
# Простая команда без флагов
simple_command = Command("hello", "Приветственная команда")
# Команда с одним флагом
flag_command = Command(
trigger="search",
description="Поиск по ключевому слову",
flags=Flag("keyword", "--")
)
# Команда с несколькими флагами и псевдонимами
complex_command = Command(
trigger="connect",
description="Подключение к серверу",
flags=Flags(
Flag("host", "--"),
Flag("port", "--", re.compile(r"^\d{1,5}$")),
Flag("user", "--")
),
aliases=["conn", "c"]
)
В Argenta имеется развитая система для определения и обработки флагов командной строки. Эта система включает следующие основные классы:
Класс Flag представляет собой сущность флага, регистрируемого для последующей обработки.
def __init__(name: str,
prefix: Literal['-', '--', '---'] = '--',
possible_values: list[str] | Pattern[str] | False = True) -> None
Параметры:
name - имя флагаprefix - префикс флагаpossible_values - возможные значения флага. Если False, то флаг не может иметь
значения
Класс Flags объединяет зарегистрированные флаги.
def __init__(*flags: Flag)
Параметры:
flags - флаги, которые будут зарегистрированыdef get_flags() -> list[Flag]
Возвращает список флагов.
Возвращает:
def add_flag(flag: Flag) -> None
Добавляет флаг в список флагов.
Параметры:
flag - добавляемый флагdef add_flags(flags: list[Flag]) -> None
Добавляет список флагов в список флагов.
Параметры:
flags - список добавляемых флаговdef get_flag(name: str) -> Flag | None
Возвращает сущность флага по его имени или None, если не найден.
Параметры:
name - имя флага для полученияВозвращает:
Класс InputFlag представляет сущность флага введенной команды.
def __init__(name: str,
prefix: Literal['-', '--', '---'] = '--',
value: str = None)
Параметры:
name - имя входного флагаprefix - префикс входного флагаvalue - значение входного флагаdef get_value() -> str | None
Возвращает значение флага.
Возвращает:
Класс PredefinedFlags — это датакласс с предопределенными флагами и наиболее часто используемыми
флагами для быстрого использования.
import re
from argenta.command.flag import Flag, Flags, InputFlags
from argenta.command.flag.defaults import PredefinedFlags
# Создание флага без значения (булев флаг)
verbose_flag = Flag('verbose', '--', False)
# Создание флага со значением из списка
log_level_flag = Flag('log-level', '--', ['debug', 'info', 'warning', 'error'])
# Создание флага со значением, соответствующим регулярному выражению
port_flag = Flag('port', '--', re.compile(r'^\d{1,5}$'))
# Использование предопределенных флагов
host_flag = PredefinedFlags.HOST
# Создание группы флагов
connection_flags = Flags(
host_flag,
port_flag,
Flag('user', '--'),
Flag('password', '--')
)
# Обработка входных флагов в обработчике команды
@router.command(Command("connect", "Подключение к серверу", flags=connection_flags))
def connect_handler(flags: InputFlags):
host = flags.get_flag('host').get_value()
port = flags.get_flag('port').get_value() if flags.get_flag('port') else '22'
user = flags.get_flag('user').get_value() if flags.get_flag('user') else 'root'
print(f"Подключение к {host}:{port} как {user}")
Orchestrator — это класс, который определяет поведение интегрированной системы,
на уровень выше, чем App.
def __init__(arg_parser: ArgParse = False)
Параметры:
arg_parser - парсер и конфигуратор аргументов командной строки при запуске
@staticmethod
def start_polling(app: App) -> None
Запуск цикла обработки пользовательского ввода.
Параметры:
app - запускаемое приложениеdef get_input_args() -> Namespace | None
Возвращает введённые при запуске аргументы.
Возвращает:
from argenta.app import App
from argenta.orchestrator import Orchestrator
from argenta.orchestrator.argparser import ArgParse
from argenta.orchestrator.argparser.arguments import OptionalArgument, BooleanArgument
# Создание парсера аргументов командной строки
arg_parser = ArgParse(
[
OptionalArgument('config', prefix='--'),
BooleanArgument('verbose', prefix='--')
],
name="Мое приложение",
description="Описание моего приложения",
epilog="© 2025 MyApp"
)
# Создание оркестратора с парсером аргументов
orchestrator = Orchestrator(arg_parser)
# Создание и настройка приложения
app = App(initial_message="Добро пожаловать!")
# Получение аргументов командной строки
args = orchestrator.get_input_args()
if args and args.verbose:
print("Включен подробный режим логирования")
# Запуск цикла обработки ввода
orchestrator.start_polling(app)
ArgParse — это класс для конфигурации аргументов командной строки при запуске.
def __init__(processed_args: list[PositionalArgument | OptionalArgument | BooleanArgument],
name: str = 'Argenta',
description: str = 'Argenta available arguments',
epilog: str = 'github.com/koloideal/Argenta | made by kolo') -> None
Параметры:
processed_args - зарегистрированные и обрабатываемые аргументыname - имя экземпляра ArgParsedescription - описание экземпляра ArgParseepilog - эпилог экземпляра ArgParsedef set_args(*args: PositionalArgument | OptionalArgument | BooleanArgument) -> None
Устанавливает аргументы для обработки.
Параметры:
args - обрабатываемые аргументыОбязательный аргумент при запуске.
def __init__(name: str)
Параметры:
name - имя аргумента, не должно начинаться с минуса (-)Необязательный аргумент, должен иметь значение.
def __init__(name: str, prefix: Literal['-', '--', '---'] = '--')
Параметры:
name - имя аргументаprefix - префикс аргументаБулевый аргумент, не требует значения.
def __init__(name: str, prefix: Literal['-', '--', '---'] = '--')
Параметры:
name - имя аргументаprefix - префикс аргументаfrom argenta.orchestrator.argparser import ArgParse
from argenta.orchestrator.argparse.arguments import PositionalArgument, OptionalArgument, BooleanArgument
# Создание парсера аргументов с разными типами аргументов
parser = ArgParse(
[
PositionalArgument('command'),
OptionalArgument('config', '--'),
OptionalArgument('output', '-'),
BooleanArgument('verbose', '--'),
BooleanArgument('help', '-')
],
name="Мое приложение",
description="Пример использования ArgParse",
epilog="Дополнительная информация"
)
Библиотека предоставляет два типа разделительных линий: StaticDividingLine и DynamicDividingLine.
Статическая разделительная линия с фиксированной длиной.
def __init__(unit_part: str = '-', length: int = 25) -> None
Параметры:
unit_part - одиночная часть разделительной линииlength - длина разделительной линииДинамическая разделительная линия, длина которой равна длине самой длинной выводимой строки.
def __init__(unit_part: str = '-') -> None
Параметры:
unit_part - одиночная часть разделительной линииfrom argenta.app import App
from argenta.app.dividing_line import StaticDividingLine, DynamicDividingLine
# Использование статической разделительной линии
app_with_static_line = App(
dividing_line=StaticDividingLine(unit_part='=', length=40)
)
# Использование динамической разделительной линии
app_with_dynamic_line = App(
dividing_line=DynamicDividingLine(unit_part='*')
)
Response — это объект, который передается в обработчик команды и содержит информацию о
статусе обработки флагов и сами флаги, разделенные на категории в зависимости от результатов валидации.
def __init__(self,
status: Status = None,
valid_flags: ValidInputFlags = ValidInputFlags(),
undefined_flags: UndefinedInputFlags = UndefinedInputFlags(),
invalid_value_flags: InvalidValueInputFlags = InvalidValueInputFlags()):
status - статус валидации флагов (тип Status)valid_flags - корректные входные флагиundefined_flags - неопределенные (незарегистрированные) входные флагиinvalid_value_flags - флаги с некорректными значениямиstatus - содержит один из статусов обработки флагов из перечисления Status:ALL_FLAGS_VALID - все флаги корректныUNDEFINED_FLAGS - присутствуют незарегистрированные флагиINVALID_VALUE_FLAGS - присутствуют флаги с некорректными значениямиUNDEFINED_AND_INVALID_FLAGS - присутствуют и незарегистрированные флаги, и флаги с
некорректными значениями
valid_flags - коллекция корректных флагов (ValidInputFlags)undefined_flags - коллекция неопределенных флагов (UndefinedInputFlags)invalid_value_flags - коллекция флагов с некорректными значениями (InvalidValueInputFlags)@router.command(Command(
trigger="connect",
description="Подключение к серверу",
flags=Flags(
Flag("host", "--"),
Flag("port", "--", re.compile(r"^\d{1,5}$")),
Flag("user", "--")
)
))
def connect_handler(response: Response):
# Работа с корректными флагами
print("Корректные флаги:")
for flag in response.valid_flags:
print(f"{flag.get_name()}: {flag.get_value()}")
# Проверка наличия незарегистрированных флагов
if response.undefined_flags:
print("\nОбнаружены незарегистрированные флаги:")
for flag in response.undefined_flags:
print(f"{flag.get_name()}: {flag.get_value()}")
# Проверка наличия флагов с некорректными значениями
if response.invalid_value_flags:
print("\nОбнаружены флаги с некорректными значениями:")
for flag in response.invalid_value_flags:
print(f"{flag.get_name()}: {flag.get_value()}")
# Использование статуса для принятия решений
if response.status == Status.ALL_FLAGS_VALID:
print("\nВсе флаги корректны, выполняем операцию...")
else:
print("\nПредупреждение: обнаружены проблемы с флагами.")
AutoCompleter — это класс, который настраивает и реализует автодополнение вводимых
команд.
def __init__(history_filename: str = False, autocomplete_button: str = 'tab') -> None
history_filename - имя файла для сохранения истории автодополнителяautocomplete_button - кнопка для автодополненияfrom argenta.app import App
from argenta.app.autocompleter import AutoCompleter
# Создание автодополнителя с историей
completer = AutoCompleter(
history_filename=".myapp_history",
autocomplete_button="tab"
)
# Использование автодополнителя при создании приложения
app = App(
prompt="Введите команду:",
autocompleter=completer
)
Пример реализации маршрутизатора с командами, которые обрабатывают различные типы флагов и используют объект Response.
import re
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.response.status import Status
from argenta.command.flag import Flag, Flags
from argenta.command.flag.defaults import PredefinedFlags
# Создание маршрутизатора
file_router = Router("Операции с файлами")
# Определение флагов для команды копирования
copy_flags = Flags(
Flag('source', '--'),
Flag('destination', '--'),
Flag('recursive', '--', False), # Булевый флаг без значения
Flag('force', '-', False) # Короткий булевый флаг
)
# Регистрация команды копирования
@file_router.command(Command(
trigger="copy",
description="Копирование файлов",
flags=copy_flags,
aliases=["cp"]
))
def copy_files(response: Response):
# Получаем значения корректных флагов
source = None
destination = None
recursive = False
force = False
for flag in response.valid_flags:
if flag.get_name() == "source":
source = flag.get_value()
elif flag.get_name() == "destination":
destination = flag.get_value()
elif flag.get_name() == "recursive":
recursive = True
elif flag.get_name() == "force":
force = True
# Проверка обязательных параметров
if not source or not destination:
print("Ошибка: необходимо указать источник и назначение")
return
print(f"Копирование из {source} в {destination}")
if recursive:
print("Рекурсивное копирование включено")
if force:
print("Принудительное копирование включено")
# Обработка неопределенных флагов
if response.undefined_flags:
print("\nПредупреждение: обнаружены незарегистрированные флаги:")
for flag in response.undefined_flags:
print(f" - {flag.get_name()}" +
(f" = {flag.get_value()}" if flag.get_value() else ""))
# Обработка флагов с некорректными значениями
if response.invalid_value_flags:
print("\nПредупреждение: обнаружены флаги с некорректными значениями:")
for flag in response.invalid_value_flags:
print(f" - {flag.get_name()} = {flag.get_value()}")
# Принятие решения на основе статуса
if response.status != Status.ALL_FLAGS_VALID:
print("\nВыполнение с предупреждениями из-за проблем с флагами.")
Пример приложения с несколькими маршрутизаторами для различных групп функций.
from argenta.app import App
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
# Маршрутизатор для работы с файлами
file_router = Router("Файловые операции")
@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="Системный монитор v1.0",
dividing_line=DynamicDividingLine("*")
)
# Добавляем все маршрутизаторы
app.include_routers(file_router, system_router, network_router)
# Добавляем сообщение при запуске
app.add_message_on_startup("Для просмотра доступных команд нажмите Enter")
# Запускаем приложение
orchestrator = Orchestrator()
orchestrator.start_polling(app)
Пример приложения с пользовательскими обработчиками ошибок для улучшения отзывчивости пользовательского интерфейса.
from argenta.app import App
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.orchestrator import Orchestrator
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
# Создаем консоль Rich для более красивого вывода
console = Console()
# Создаем маршрутизатор
router = Router("Основные команды")
@router.command(Command("hello", "Приветствие"))
def hello(response: Response):
console.print(Panel("Привет, мир!", style="green"))
@router.command(Command("bye", "Прощание"))
def bye(response: Response):
console.print(Panel("До свидания!", style="blue"))
# Создаем приложение
app = App(
prompt="MyApp> ",
initial_message="Тестовое приложение v1.0",
print_func=console.print
)
# Регистрируем маршрутизатор
app.include_router(router)
# Пользовательский обработчик неизвестных команд
def unknown_command_handler(cmd: InputCommand):
text = Text()
text.append("Неизвестная команда: ", style="bold red")
text.append(cmd.get_trigger(), style="italic")
text.append("\nДоступные команды: hello, bye, Q (выход)")
console.print(Panel(text, title="Ошибка", border_style="red"))
# Пользовательский обработчик пустых команд
def empty_command_handler(response: Response):
console.print(Panel("Для выхода введите Q", style="yellow"))
# Пользовательский обработчик неверных флагов
def incorrect_input_syntax_handler(raw_command: str):
console.print(Panel(f"Некорректный синтаксис: {raw_command}", title="Ошибка", style="red"))
# Пользовательский обработчик повторяющихся флагов
def repeated_flag_handler(raw_command: str):
console.print(Panel(f"Флаг указан повторно: {raw_command}", title="Предупреждение", style="yellow"))
# Пользовательский обработчик выхода
def exit_handler():
console.print(Panel("Выход из приложения...", style="blue", title="До свидания"))
# Устанавливаем обработчики в приложение
app.set_unknown_command_handler(unknown_command_handler)
app.set_empty_command_handler(empty_command_handler)
app.set_incorrect_input_syntax_handler(incorrect_input_syntax_handler)
app.set_repeated_input_flags_handler(repeated_flag_handler)
app.set_exit_command_handler(exit_handler)
# Запускаем приложение
orchestrator = Orchestrator()
orchestrator.start_polling(app)
Библиотека Argenta предоставляет набор исключений для обработки различных ситуаций ошибок. Ниже приведен список основных исключений и их назначение:
| Исключение | Описание |
|---|---|
NoRegisteredHandlersException |
У маршрутизатора нет зарегистрированных обработчиков |
TooManyTransferredArgsException |
Передано слишком много аргументов |
RequiredArgumentNotPassedException |
Не передан обязательный аргумент |
IncorrectNumberOfHandlerArgsException |
Передано неверное количество аргументов обработчику |
TriggerContainSpacesException |
В регистрируемом триггере содержатся пробелы |
При разработке приложений с использованием библиотеки Argenta рекомендуется следовать следующим практикам:
@router.command(Command("example", flags=my_flags))
def handler(response: Response):
# 1. Сначала проверяем наличие критичных проблем
if response.status != Status.ALL_FLAGS_VALID:
# Выводим предупреждения
if response.undefined_flags:
print("Предупреждение: неизвестные флаги")
if response.invalid_value_flags:
print("Предупреждение: некорректные значения флагов")
# 2. Продолжаем выполнение с корректными флагами
for flag in response.valid_flags:
print(f'Корректный флаг: {flag.get_string_entity()}')
Важно: Учитывайте, что на Linux системах история автодополнителя работает только в рамках текущего сеанса приложения. Если вам требуется полноценная история между запусками, вам может потребоваться дополнительная настройка или использование внешних инструментов.
Библиотека Argenta включает набор тестов для проверки функциональности. Вы можете запустить тесты следующим образом:
python -m unittest discover
Для более подробного вывода результатов тестирования используйте флаг -v:
python -m unittest discover -v
При разработке собственных приложений на основе Argenta рекомендуется создавать модульные тесты для проверки функциональности ваших обработчиков команд и общей логики приложения.
Python library for creating modular CLI applications
Argenta — is a modern Python library for creating modular CLI applications. The library provides a simple and elegant way to create interactive console applications with rich functionality, including command completion, flag and argument handling, custom error handlers, and much more.
The main goal of Argenta is to simplify the process of creating complex console interfaces by providing a modular architecture and an intuitive API. The library is ideal for creating CLI utilities, administrative interfaces, and other applications that require interaction through the terminal.
pip install argenta
Or with Poetry:
poetry add argenta
Below are examples of creating simple CLI applications using Argenta.
Let's create a minimal application with one command hello, which prints the message "Hello, world!".
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!")
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()
Let's create an application with the ssh command that handles flags --host and
--port.
import re
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.orchestrator import Orchestrator
from argenta.command.flag.defaults import PredefinedFlags
from argenta.command.flag import Flags, Flag, InputFlags
router = Router()
registered_flags = Flags(PredefinedFlags.HOST,
Flag('port', '--', re.compile(r'^[0-9]{1,4}$')))
@router.command(Command("hello"))
def handler(response: Response):
print("Hello, world!")
@router.command(Command(trigger="ssh",
description='connect via ssh',
flags=registered_flags))
def handler_with_flags(response: Response):
# Working with the valid flags
for flag in response.valid_flags:
print(f'Flag name: {flag.get_name()}\n'
f'Flag value: {flag.get_value()}')
# Checking status and dealing with undefined Flags
if response.status == Status.UNDEFINED_FLAGS or response.status == Status.UNDEFINED_AND_INVALID_FLAGS:
print("\nUndefined flags:")
for flag in response.undefined_flags:
print(f'Undefined flag: {flag.get_name()}')
# Working with flags with invalid values
if response.status == Status.INVALID_VALUE_FLAGS or response.status == Status.UNDEFINED_AND_INVALID_FLAGS:
print("\nFlags with invalid value:")
for flag in response.invalid_value_flags:
print(f'Invalid value flag: {flag.get_name()} = {flag.get_value()}')
Once you launch this application, you will be able to enter commands like:
ssh --host 192.168.1.10 --port 22
And your handler will receive these flags and their values for further processing. Even if there are incorrect flags or incorrect values, control will still be transferred to the handler.
The Argenta library is built on a modular architecture that provides flexibility and extensibility. Below is a diagram of how the main components interact:
Orchestrator and setting up startup argumentsApp and setting up its parametersRouter and registering handlersRouter) in the applicationorchestrator.start_polling(app)This section describes in detail the main components of the library, their methods and usage.
App is the main application class that manages the lifecycle and behavior of the
CLI. It is responsible for registering routers, handling system events, and configuring the user
interface.
def __init__(prompt: str = 'What do you want to do?',
initial_message: str = 'Argenta',
farewell_message: str = 'See you',
exit_command: Command = Command('Q', 'Exit command'),
system_router_title: str | None = 'System points:',
ignore_command_register: bool = True,
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
repeat_command_groups: bool = True,
override_system_messages: bool = False,
autocompleter: AutoCompleter = AutoCompleter(),
print_func: Callable[[str], None] = Console().print) -> None
prompt - message before entering a commandinitial_message - message when starting the applicationfarewell_message - message when the application endsexit_command - command to exit the applicationsystem_router_title - title of the system routerignore_command_register - ignore case when entering commandsdividing_line - dividing line objectrepeat_command_groups - whether to repeat available commands and their descriptionsoverride_system_messages - whether to override the formatting of system messagesautocompleter - autocompleter objectprint_func - function for outputting system messagesdef set_description_message_pattern(_: Callable[[str, str], str]) -> None
Sets the output template for available commands.
Options:
_ - template for output of available commandsdef set_incorrect_input_syntax_handler(_: Callable[[str], None]) -> None
Installing a handler for incorrect syntax when entering a command.
Options:
_ - invalid flag handlerdef set_repeated_input_flags_handler(_: Callable[[str], None]) -> None
Set the handler for repeating flags when entering a command.
Options:
_ - repeating flag handlerdef set_unknown_command_handler(_: Callable[[str], None]) -> None
Installing a handler for unknown commands.
Options:
_ - unknown command handlerdef set_empty_command_handler(_: Callable[[], None]) -> None
Set up a handler for empty commands on input.
Options:
_ - empty command handlerdef set_exit_command_handler(_: Callable[[], None]) -> None
Installing an exit command handler.
Options:
_ - exit command handlerdef include_router(router: Router) -> None
Registering the router in the application.
Options:
router - registered routerdef include_routers(*routers: Router) -> None
Register multiple routers in the application.
Options:
routers - list of registered routersdef add_message_on_startup(message: str) -> None
Add a message to be displayed when the application is launched.
Options:
message - message to be addedfrom argenta.app import App
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.app.dividing_line import DynamicDividingLine
# Create a router with the command
router = Router("Base commands")
@router.command(Command("hello", "Greetings"))
def hello(response: Response):
print("Hello, world!")
# Create an application with settings
app = App(
prompt="Enter the command:",
initial_message="Welcome to my app!",
farewell_message="GoodBye!",
dividing_line=DynamicDividingLine("="),
ignore_command_register=True
)
# Registering a router
app.include_router(router)
# Setting up a handler for unknown commands
app.set_unknown_command_handler(lambda cmd: print(f"Unknown command: {cmd}"))
Router — this is the class that actually configures and manages command handlers.
It registers commands and associates them with handler functions.
def __init__(title: str = None)
Options:
title - the router title displayed when outputting available commandsdef command(command: Command) -> Callable
Decorator for registering a command handler.
Options:
command - registered commandReturns:
def get_triggers() -> list[str]
Getting registered triggers.
Returns:
def get_aliases() -> list[str]
Getting registered aliases.
Returns:
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
# Create a router with a header
router = Router("File management")
# Register a team without flags
@router.command(Command("list", "List files"))
def list_files(response: Response):
import os
files = os.listdir()
for file in files:
print(file)
# Registering a command with aliases
@router.command(Command("info", "File information", aliases=["stat", "details"]))
def file_info(response: Response):
print("This command shows information about a file.")
Command — is a class representing a command that can and should be registered with Router.
Defines the trigger, description, flags, and aliases of the command.
def __init__(trigger: str,
description: str = None,
flags: Flag | Flags = None,
aliases: list[str] = None)
Options:
trigger - a trigger string that, when entered by the user, indicates that the input matches the command
description - command descriptionflags - flags being processedaliases - string synonyms for main triggerfrom argenta.command import Command
from argenta.command.flag import Flag, Flags
import re
# Simple command without flags
simple_command = Command("hello", "Welcome command")
# Command with single flag
flag_command = Command(
trigger="search",
description="Search by keyword",
flags=Flag("keyword", "--")
)
# Command with multiple flags and aliases
complex_command = Command(
trigger="connect",
description="Connecting to the server",
flags=Flags(
Flag("host", "--"),
Flag("port", "--", re.compile(r"^\d{1,5}$")),
Flag("user", "--")
),
aliases=["conn", "c"]
)
Argenta has a sophisticated system for defining and handling command line flags. This system includes the following main classes:
Class Flag represents the essence of the flag registered for subsequent processing.
def __init__(name: str,
prefix: Literal['-', '--', '---'] = '--',
possible_values: list[str] | Pattern[str] | False = True) -> None
Options:
name - flag nameprefix - flag prefixpossible_values - possible flag values. If false , then the flag cannot have
meanings
The class flags combines registered flags.
def __init__(*flags: Flag)
Параметры:
flags - flags that will be registereddef get_flags() -> list[Flag]
returns the list of flags
Возвращает:
def add_flag(flag: Flag) -> None
adds the flag to the flag list
Параметры:
flag - added flagdef add_flags(flags: list[Flag]) -> None
adds a list of flags to the flag list
Options:
flags - list_of_added_flagsdef get_flag(name: str) -> Flag | None
Returns the essence of the flag by his name or None, if not found.
Options:
name - flag name for receivingReturns:
The class inputflag represents the essence of the flag of the entered team.
def __init__(name: str,
prefix: Literal['-', '--', '---'] = '--',
value: str = None)
Parameters:
name - name of the input flagprefix - prefix of the input flagvalue - value of the input flagdef get_value() -> str | None
Returns the value of the flag.
Returns:
Class PredefinedFlags — This is dadaxlass with predetermined flags and the most commonly used
Flags for fast use.
import re
from argenta.command.flag import Flag, Flags, InputFlags
from argenta.command.flag.defaults import PredefinedFlags
# Creating a flag without value (Boolean flag)
verbose_flag = Flag('verbose', '--', False)
# Creating a flag with a meaning from the list
log_level_flag = Flag('log-level', '--', ['debug', 'info', 'warning', 'error'])
# Creation of a flag with a meaning corresponding to the regular expression
port_flag = Flag('port', '--', re.compile(r'^\d{1,5}$'))
# Using predefined flags
host_flag = PredefinedFlags.HOST
# Creation of a group of flags
connection_flags = Flags(
host_flag,
port_flag,
Flag('user', '--'),
Flag('password', '--')
)
# Processing of the input flags in the command processor
@router.command(Command("connect", "Connection to the server", flags=connection_flags))
def connect_handler(flags: InputFlags):
host = flags.get_flag('host').get_value()
port = flags.get_flag('port').get_value() if flags.get_flag('port') else '22'
user = flags.get_flag('user').get_value() if flags.get_flag('user') else 'root'
print(f"Connection to {host}:{port} as {user}")
Orchestrator — this is a class that determines the behavior of an integrated system,
to a level higher than App.
def __init__(arg_parser: ArgParse = False)
Parameters:
arg_parser - Parser and configurator of command line arguments when starting
@staticmethod
def start_polling(app: App) -> None
Launching the user input processing cycle.
Parameters:
app - launched applicationdef get_input_args() -> Namespace | None
Returns the arguments introduced at launch.
Returns:
from argenta.app import App
from argenta.orchestrator import Orchestrator
from argenta.orchestrator.argparser import ArgParse
from argenta.orchestrator.argparser.arguments import OptionalArgument, BooleanArgument
# Creation parser of the arguments of the command line
arg_parser = ArgParse(
[
OptionalArgument('config', prefix='--'),
BooleanArgument('verbose', prefix='--')
],
name="My application",
description="Description of my application",
epilog="© 2025 MyApp"
)
# Creation of an orchestra with a parser of arguments
orchestrator = Orchestrator(arg_parser)
# Creating and configuration of the application
app = App(initial_message="You are welcome!")
# Getting command line arguments
args = orchestrator.get_input_args()
if args and args.verbose:
print("Detailed logistics mode is included")
# Launching the input processing cycle
orchestrator.start_polling(app)
ArgParse — This is a class for configuration of command line arguments at launch.
def __init__(processed_args: list[PositionalArgument | OptionalArgument | BooleanArgument],
name: str = 'Argenta',
description: str = 'Argenta available arguments',
epilog: str = 'github.com/koloideal/Argenta | made by kolo') -> None
Parameters:
processed_args - registered and processed argumentsname - the namedescription - the descriptionepilog - the epilogdef set_args(*args: PositionalArgument | OptionalArgument | BooleanArgument) -> None
Installs arguments for processing.
Parameters:
args - Processed argumentsMandatory argument at launch.
def __init__(name: str)
Parameters:
name - The name of the argument should not begin with the minus (-)An optional argument should be important.
def __init__(name: str, prefix: Literal['-', '--', '---'] = '--')
Parameters:
name - the name of the argumentprefix - prefix argumentBoolean argument does not require value.
def __init__(name: str, prefix: Literal['-', '--', '---'] = '--')
Parameteres:
name - the name of the argumentprefix - prefix argumentfrom argenta.orchestrator.argparser import ArgParse
from argenta.orchestrator.argparse.arguments import PositionalArgument, OptionalArgument, BooleanArgument
# Creation of a parser of arguments with different types of arguments
parser = ArgParse(
[
PositionalArgument('command'),
OptionalArgument('config', '--'),
OptionalArgument('output', '-'),
BooleanArgument('verbose', '--'),
BooleanArgument('help', '-')
],
name="My application",
description="An example of using ArgParse",
epilog="Additional information"
)
The library provides two types of dividing lines: StaticDividingLine and DynamicDividingLine.
Static dividing line with a fixed length.
def __init__(unit_part: str = '-', length: int = 25) -> None
Parameters:
unit_part - single part of the dividing linelength - the length of the dividing lineThe dynamic dividing line, the length of which is equal to the length of the longest lines.
def __init__(unit_part: str = '-') -> None
Parameters:
unit_part - single part of the dividing linefrom argenta.app import App
from argenta.app.dividing_line import StaticDividingLine, DynamicDividingLine
# Using a static dividing line
app_with_static_line = App(
dividing_line=StaticDividingLine(unit_part='=', length=40)
)
# Using a dynamic dividing line
app_with_dynamic_line = App(
dividing_line=DynamicDividingLine(unit_part='*')
)
Response — This is an object that is transferred to the command handler and contains information about
The status of flag processing and the flags themselves, divided into categories, depending on the results of validation.
def __init__(self,
status: Status = None,
valid_flags: ValidInputFlags = ValidInputFlags(),
undefined_flags: UndefinedInputFlags = UndefinedInputFlags(),
invalid_value_flags: InvalidValueInputFlags = InvalidValueInputFlags()):
status - flag validation status (type Status)valid_flags - correct input flagsundefined_flags - uncertain (unregistered) input flagsinvalid_value_flags - flags with incorrect valuesstatus - Contains one of the statuses of the flag processing from the Status:ALL_FLAGS_VALID - all flags are correctUNDEFINED_FLAGS - unregistered flags are presentINVALID_VALUE_FLAGS - there are flags with incorrect valuesUNDEFINED_AND_INVALID_FLAGS - there are unregistered flags and flags with
incorrect values
valid_flags - A collection of correct flags (ValidInputFlags)undefined_flags - A collection of vague flags (UndefinedInputFlags)invalid_value_flags - Collection of flags with incorrect values (InvalidValueInputFlags)@router.command(Command(
trigger="connect",
description="Connection to the server",
flags=Flags(
Flag("host", "--"),
Flag("port", "--", re.compile(r"^\d{1,5}$")),
Flag("user", "--")
)
))
def connect_handler(response: Response):
# Working with correct flags
print("Correct flags:")
for flag in response.valid_flags:
print(f"{flag.get_name()}: {flag.get_value()}")
# Checking the availability of unregistered flags
if response.undefined_flags:
print("\nUnregistered flags were discovered:")
for flag in response.undefined_flags:
print(f"{flag.get_name()}: {flag.get_value()}")
# Checking the presence of flags with incorrect values
if response.invalid_value_flags:
print("\nFlags with incorrect values were found:")
for flag in response.invalid_value_flags:
print(f"{flag.get_name()}: {flag.get_value()}")
# Using status for decision making
if response.status == Status.ALL_FLAGS_VALID:
print("\nAll flags are correct, we perform the operation ...")
else:
print("\nWarning: Flag problems were discovered.")
AutoCompleter — This is a class that configures and implements autocompletion of input commands.
def __init__(history_filename: str = False, autocomplete_button: str = 'tab') -> None
history_filename - file name for saving the history of the auto-complementautocomplete_button - button for auto completionfrom argenta.app import App
from argenta.app.autocompleter import AutoCompleter
# Create an auto-complement with a history
completer = AutoCompleter(
history_filename=".myapp_history",
autocomplete_button="tab"
)
# Use auto-complement when creating an app
app = App(
prompt="Enter the command:",
autocompleter=completer
)
An example of a router implementation with commands that handle different types of flags and use the Response object.
import re
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.response.status import Status
from argenta.command.flag import Flag, Flags
from argenta.command.flag.defaults import PredefinedFlags
# Creating a router
file_router = Router("File operations")
# Defining flags for a copy command
copy_flags = Flags(
Flag('source', '--'),
Flag('destination', '--'),
Flag('recursive', '--', False), # Boolean flag with no value
Flag('force', '-', False) # Short boolean flag
)
# Register a copy command
@file_router.command(Command(
trigger="copy",
description="Copying files",
flags=copy_flags,
aliases=["cp"]
))
def copy_files(response: Response):
# Get the values of the correct flags
source = None
destination = None
recursive = False
force = False
for flag in response.valid_flags:
if flag.get_name() == "source":
source = flag.get_value()
elif flag.get_name() == "destination":
destination = flag.get_value()
elif flag.get_name() == "recursive":
recursive = True
elif flag.get_name() == "force":
force = True
# Checking required settings
if not source or not destination:
print("Error: Source and destination must be specified")
return
print(f"Copy from {source} to {destination}")
if recursive:
print("Recursive copying is enabled")
if force:
print("Force copy enabled")
# Handling undefined flags
if response.undefined_flags:
print("\nWarning: Unregistered flags detected:")
for flag in response.undefined_flags:
print(f" - {flag.get_name()}" +
(f" = {flag.get_value()}" if flag.get_value() else ""))
# Handling flags with invalid values
if response.invalid_value_flags:
print("\nWarning: flags with incorrect values have been detected:")
for flag in response.invalid_value_flags:
print(f" - {flag.get_name()} = {flag.get_value()}")
# Status based decision making
if response.status != Status.ALL_FLAGS_VALID:
print("\nExecution with warnings due to issues with flags.")
A sample application with multiple routers for different groups of functions.
from argenta.app import App
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
# File Router
file_router = Router("File Operations")
@file_router.command(Command("list", "File list"))
def list_files(response: Response):
files = os.listdir()
for file in files:
print(file)
@file_router.command(Command("size", "File size"))
def file_size(response: Response):
file_name = input("Enter a file name: ")
if os.path.exists(file_name):
size = os.path.getsize(file_name)
print(f"File size {file_name}: {size} byte")
else:
print(f"File {file_name} not found")
# Router for system operations
system_router = Router("System operations")
@system_router.command(Command("info", "System information"))
def system_info(response: Response):
print(f"System: {platform.system()}")
print(f"Version: {platform.version()}")
print(f"Architecture: {platform.architecture()}")
print(f"Processor: {platform.processor()}")
@system_router.command(Command("memory", "Memory information"))
def memory_info(response: Response):
memory = psutil.virtual_memory()
print(f"Total memory: {memory.total / (1024**3):.2f} ГБ")
print(f"Available: {memory.available / (1024**3):.2f} ГБ")
print(f"Used: {memory.used / (1024**3):.2f} ГБ ({memory.percent}%)")
# Network operations Router
network_router = Router("Network Operations")
@network_router.command(Command("ping", "Checking Host Availability"))
def ping_host(response: Response):
host = input("Enter the hostname: ")
print(f"Ping {host}...")
subprocess.run(["ping", "-c", "4", host])
@network_router.command(Command("ip", "Show IP Addresses"))
def show_ip(response: Response):
hostname = socket.gethostname()
print(f"Имя хоста: {hostname}")
print(f"IP-адрес: {socket.gethostbyname(hostname)}")
# Creating an application and registering Routers
app = App(
prompt="System> ",
initial_message="Performance Monitor v1.0",
dividing_line=DynamicDividingLine("*")
)
# Add all routers
app.include_routers(file_router, system_router, network_router)
# Launching the application
orchestrator = Orchestrator()
orchestrator.start_polling(app)
Sample app with custom error handlers to improve responsiveness UI.
from argenta.app import App
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
from argenta.orchestrator import Orchestrator
from rich.console import Console
from rich.panel import Panel
from rich.text import Text
# Creating a Rich console for a more beautiful output
console = Console()
# Creating a router
router = Router("Core commands")
@router.command(Command("hello", "Greeting"))
def hello(response: Response):
console.print(Panel("Hello World!", style="green"))
@router.command(Command("bye", "Farewell"))
def bye(response: Response):
console.print(Panel("Goodbye!", style="blue"))
# Creating an app
app = App(
prompt="MyApp> ",
initial_message="Test application v1.0",
print_func=console.print
)
# Registering the router
app.include_router(router)
# Custom unknown command handler
def unknown_command_handler(cmd: InputCommand):
text = Text()
text.append("Unknown command: ", style="bold red")
text.append(cmd.get_trigger(), style="italic")
text.append("\nAvailable commands: hello, bye, Q (exit)")
console.print(Panel(text, title="Error", border_style="red"))
# Custom empty command handler
def empty_command_handler(response: Response):
console.print(Panel("Enter Q to exit", style="yellow"))
# Custom invalid flag handler
def incorrect_input_syntax_handler(raw_command: str):
console.print(Panel(f"Incorrect syntax: {raw_command}", title="Error", style="red"))
# Custom duplicate flag handler
def repeated_flag_handler(raw_command: str):
console.print(Panel(f"Flag re-specified: {raw_command}", title="Warning", style="yellow"))
# Custom output handler
def exit_handler():
console.print(Panel("Log out of the app...", style="blue", title="GoodBye"))
# Installing handlers in the application
app.set_unknown_command_handler(unknown_command_handler)
app.set_empty_command_handler(empty_command_handler)
app.set_incorrect_input_syntax_handler(incorrect_input_syntax_handler)
app.set_repeated_input_flags_handler(repeated_flag_handler)
app.set_exit_command_handler(exit_handler)
# Launching the application
orchestrator = Orchestrator()
orchestrator.start_polling(app)
The Argenta library provides a set of exceptions for handling various error situations. Below is a list of the main exceptions and their purpose:
| Exception | Definition |
|---|---|
NoRegisteredHandlersException |
The router has no registered handlers |
TooManyTransferredArgsException |
Too many arguments are passed |
RequiredArgumentNotPassedException |
Required argument not passed |
IncorrectNumberOfHandlerArgsException |
Incorrect number of arguments passed to the handler |
TriggerContainSpacesException |
The registered trigger contains spaces |
When developing applications using the Argenta library, we recommend that you follow these guidelines practices:
@router.command(Command("example", flags=my_flags))
def handler(response: Response):
# 1. First, check for critical issues
if response.status != Status.ALL_FLAGS_VALID:
# Displaying warnings
if response.undefined_flags:
print("Warning: unknown flags")
if response.invalid_value_flags:
print("Warning: invalid flag values")
# 2. Continuing with the correct flags
for flag in response.valid_flags:
print(f'Correct flag: {flag.get_string_entity()}')
Important: Keep in mind that on Linux systems, the history of autocompletion works only within the framework of the current application session. If you need a full history between runs, you may need to Additional customization or use of external tools.
The Argenta library includes a set of tests to verify functionality. You can run the tests as follows:
python -m unittest discover
For a more detailed display of test results, use the -v:
python -m unittest discover -v
When developing your own Argenta-based applications, we recommend that you create unit tests to validate the functionality of your command handlers and the general logic of the application.