This commit is contained in:
2025-04-14 16:38:53 +03:00
parent 3ef8707cfa
commit e189f8d9aa
7 changed files with 117 additions and 78 deletions
+87 -48
View File
@@ -20,23 +20,24 @@ from argenta.app.registered_routers.entity import RegisteredRouters
class AppInit: class BaseApp:
def __init__(self, def __init__(self,
prompt: str = '[italic dim bold]What do you want to do?\n', prompt: str,
initial_message: str = '\nArgenta\n', initial_message: str,
farewell_message: str = '\nSee you\n', farewell_message: str,
exit_command: Command = Command('Q', 'Exit command'), exit_command: Command,
system_points_title: str | None = 'System points:', system_router_title: str | None,
ignore_command_register: bool = True, ignore_command_register: bool,
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(), dividing_line: StaticDividingLine | DynamicDividingLine,
repeat_command_groups: bool = True, repeat_command_groups: bool,
override_system_messages: bool = False, override_system_messages: bool,
autocompleter: AutoCompleter = AutoCompleter(), autocompleter: AutoCompleter,
print_func: Callable[[str], None] = Console().print) -> None: print_func: Callable[[str], None]) -> None:
self._prompt = prompt self._prompt = prompt
self._print_func = print_func self._print_func = print_func
self._exit_command = exit_command self._exit_command = exit_command
self._system_points_title = system_points_title self._system_router_title = system_router_title
self._dividing_line = dividing_line self._dividing_line = dividing_line
self._ignore_command_register = ignore_command_register self._ignore_command_register = ignore_command_register
self._repeat_command_groups_description = repeat_command_groups self._repeat_command_groups_description = repeat_command_groups
@@ -49,7 +50,10 @@ class AppInit:
self._description_message_gen: Callable[[str, str], str] = lambda command, description: f'[{command}] *=*=* {description}' self._description_message_gen: Callable[[str, str], str] = lambda command, description: f'[{command}] *=*=* {description}'
self._registered_routers: RegisteredRouters = RegisteredRouters() self._registered_routers: RegisteredRouters = RegisteredRouters()
self._messages_on_startup = [] self._messages_on_startup: list[str] = []
self._all_registered_triggers_in_lower: list[str] = []
self._all_registered_triggers_in_default_case: list[str] = []
self._invalid_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'Incorrect flag syntax: {raw_command}') self._invalid_input_flags_handler: Callable[[str], None] = 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._repeated_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'Repeated input flags: {raw_command}')
@@ -58,8 +62,6 @@ class AppInit:
self._exit_command_handler: Callable[[], None] = lambda: print_func(self._farewell_message) self._exit_command_handler: Callable[[], None] = lambda: print_func(self._farewell_message)
class AppSetters(AppInit):
def set_description_message_pattern(self, pattern: Callable[[str, str], str]) -> None: def set_description_message_pattern(self, pattern: Callable[[str, str], str]) -> None:
self._description_message_gen: Callable[[str, str], str] = pattern self._description_message_gen: Callable[[str, str], str] = pattern
@@ -84,8 +86,6 @@ class AppSetters(AppInit):
self._exit_command_handler = handler self._exit_command_handler = handler
class AppPrinters(AppInit):
def _print_command_group_description(self): def _print_command_group_description(self):
for registered_router in self._registered_routers: for registered_router in self._registered_routers:
if registered_router.get_title(): if registered_router.get_title():
@@ -113,33 +113,29 @@ class AppPrinters(AppInit):
self._print_func(self._dividing_line.get_full_dynamic_line(max_length_line, self._override_system_messages)) self._print_func(self._dividing_line.get_full_dynamic_line(max_length_line, self._override_system_messages))
class AppNonStandardHandlers(AppPrinters):
def _is_exit_command(self, command: InputCommand): def _is_exit_command(self, command: InputCommand):
if command.get_trigger().lower() == self._exit_command.get_trigger().lower(): if self._ignore_command_register:
if self._ignore_command_register: if command.get_trigger().lower() == self._exit_command.get_trigger().lower():
system_router.input_command_handler(command)
return True return True
elif command.get_trigger() == self._exit_command.get_trigger(): elif command.get_trigger().lower() in [x.lower() for x in self._exit_command.get_aliases()]:
system_router.input_command_handler(command) return True
else:
if command.get_trigger() == self._exit_command.get_trigger():
return True
elif command.get_trigger() in self._exit_command.get_aliases():
return True return True
return False return False
def _is_unknown_command(self, command: InputCommand): def _is_unknown_command(self, command: InputCommand):
for router_entity in self._registered_routers: input_command_trigger = command.get_trigger()
for command_handler in router_entity.get_command_handlers(): if self._ignore_command_register:
handled_command_trigger = command_handler.get_handled_command().get_trigger() if input_command_trigger.lower() in self._all_registered_triggers_in_lower:
handled_command_aliases = command_handler.get_handled_command().get_aliases() return False
if handled_command_trigger.lower() == command.get_trigger().lower() and self._ignore_command_register: else:
return False if input_command_trigger in self._all_registered_triggers_in_default_case:
elif handled_command_trigger == command.get_trigger(): return False
return False
elif handled_command_aliases:
if (command.get_trigger().lower() in [x.lower() for x in handled_command_aliases]) and self._ignore_command_register:
return False
elif command.get_trigger() in handled_command_trigger:
return False
with redirect_stdout(io.StringIO()) as f: with redirect_stdout(io.StringIO()) as f:
self._unknown_command_handler(command) self._unknown_command_handler(command)
res: str = f.getvalue() res: str = f.getvalue()
@@ -157,18 +153,14 @@ class AppNonStandardHandlers(AppPrinters):
self._empty_input_command_handler() self._empty_input_command_handler()
class AppValidators(AppInit):
def _validate_included_routers(self) -> None: def _validate_included_routers(self) -> None:
for router in self._registered_routers: for router in self._registered_routers:
if not router.get_command_handlers(): if not router.get_command_handlers():
raise NoRegisteredHandlersException(router.get_name()) raise NoRegisteredHandlersException(router.get_name())
class AppSetups(AppValidators, AppPrinters):
def _setup_system_router(self): def _setup_system_router(self):
system_router.set_title(self._system_points_title) system_router.set_title(self._system_router_title)
@system_router.command(self._exit_command) @system_router.command(self._exit_command)
def exit_command(): def exit_command():
@@ -178,6 +170,7 @@ class AppSetups(AppValidators, AppPrinters):
system_router.set_ignore_command_register(self._ignore_command_register) system_router.set_ignore_command_register(self._ignore_command_register)
self._registered_routers.add_registered_router(system_router) self._registered_routers.add_registered_router(system_router)
def _setup_default_view(self): def _setup_default_view(self):
if not self._override_system_messages: if not self._override_system_messages:
self._initial_message = f'\n[bold red]{text2art(self._initial_message, font='tarty1')}\n\n' self._initial_message = f'\n[bold red]{text2art(self._initial_message, font='tarty1')}\n\n'
@@ -191,16 +184,20 @@ class AppSetups(AppValidators, AppPrinters):
self._empty_input_command_handler = lambda: self._print_func('[red bold]Empty input command') self._empty_input_command_handler = lambda: self._print_func('[red bold]Empty input command')
self._unknown_command_handler = lambda command: self._print_func(f"[red bold]Unknown command: {escape(command.get_trigger())}") self._unknown_command_handler = lambda command: self._print_func(f"[red bold]Unknown command: {escape(command.get_trigger())}")
def _pre_cycle_setup(self): def _pre_cycle_setup(self):
self._setup_default_view() self._setup_default_view()
self._setup_system_router() self._setup_system_router()
self._validate_included_routers() self._validate_included_routers()
all_triggers: list[str] = []
for router_entity in self._registered_routers: for router_entity in self._registered_routers:
all_triggers.extend(router_entity.get_triggers()) self._all_registered_triggers_in_default_case.extend(router_entity.get_triggers())
all_triggers.extend(router_entity.get_aliases()) self._all_registered_triggers_in_default_case.extend(router_entity.get_aliases())
self._autocompleter.initial_setup(all_triggers)
self._all_registered_triggers_in_lower.extend([x.lower() for x in router_entity.get_triggers()])
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._print_func(self._initial_message) self._print_func(self._initial_message)
@@ -214,7 +211,48 @@ class AppSetups(AppValidators, AppPrinters):
class App(AppSetters, AppNonStandardHandlers, AppSetups): class App(BaseApp):
def __init__(self,
prompt: str = '[italic dim bold]What do you want to do?\n',
initial_message: str = '\nArgenta\n',
farewell_message: str = '\nSee you\n',
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:
"""
Public. The essence of the application itself.
Configures and manages all aspects of the behavior and presentation of the user interacting with the user
:param prompt: displayed before entering the command
:param initial_message: displayed at the start of the app
:param farewell_message: displayed at the end of the app
:param exit_command: the entity of the command that will be terminated when entered
:param system_router_title: system router title
:param ignore_command_register: whether to ignore the case of the entered commands
:param dividing_line: the entity of the dividing line
:param repeat_command_groups: whether to repeat the available commands and their description
:param override_system_messages: whether to redefine the default formatting of system messages
:param autocompleter: the entity of the autocompleter
:param print_func: system messages text output function
:return: None
"""
super().__init__(prompt=prompt,
initial_message=initial_message,
farewell_message=farewell_message,
exit_command=exit_command,
system_router_title=system_router_title,
ignore_command_register=ignore_command_register,
dividing_line=dividing_line,
repeat_command_groups=repeat_command_groups,
override_system_messages=override_system_messages,
autocompleter=autocompleter,
print_func=print_func)
def run_polling(self) -> None: def run_polling(self) -> None:
self._pre_cycle_setup() self._pre_cycle_setup()
while True: while True:
@@ -233,6 +271,7 @@ class App(AppSetters, AppNonStandardHandlers, AppSetups):
continue continue
if self._is_exit_command(input_command): if self._is_exit_command(input_command):
system_router.input_command_handler(input_command)
self._autocompleter.exit_setup() self._autocompleter.exit_setup()
return return
+1 -1
View File
@@ -24,7 +24,7 @@ class Command(BaseCommand):
super().__init__(trigger) super().__init__(trigger)
self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags() self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags()
self._description = f'Description for "{self._trigger}" command' if not description else description self._description = f'Description for "{self._trigger}" command' if not description else description
self._aliases = aliases self._aliases = aliases if isinstance(aliases, list) else []
def get_registered_flags(self) -> Flags: def get_registered_flags(self) -> Flags:
return self._registered_flags return self._registered_flags
+23 -1
View File
@@ -1,6 +1,8 @@
from typing import Callable from typing import Callable
from argenta.command import Command from argenta.command import Command
from argenta.command.flag.models import InputFlags from argenta.command.flag import InputFlags
class CommandHandler: class CommandHandler:
@@ -19,3 +21,23 @@ class CommandHandler:
def get_handled_command(self): def get_handled_command(self):
return self._handled_command return self._handled_command
class CommandHandlers:
def __init__(self, command_handlers: list[CommandHandler] = None):
self.command_handlers = command_handlers if command_handlers else []
def get_command_handlers(self) -> list[CommandHandler]:
return self.command_handlers
def add_command_handler(self, command_handler: CommandHandler):
self.command_handlers.append(command_handler)
def add_command_handlers(self, *command_handlers: CommandHandler):
self.command_handlers.extend(command_handlers)
def __iter__(self):
return iter(self.command_handlers)
def __next__(self):
return next(iter(self.command_handlers))
-21
View File
@@ -1,21 +0,0 @@
from argenta.router.command_handler.entity import CommandHandler
class CommandHandlers:
def __init__(self, command_handlers: list[CommandHandler] = None):
self.command_handlers = command_handlers if command_handlers else []
def get_command_handlers(self) -> list[CommandHandler]:
return self.command_handlers
def add_command_handler(self, command_handler: CommandHandler):
self.command_handlers.append(command_handler)
def add_command_handlers(self, *command_handlers: CommandHandler):
self.command_handlers.extend(command_handlers)
def __iter__(self):
return iter(self.command_handlers)
def __next__(self):
return next(iter(self.command_handlers))
+1 -2
View File
@@ -3,8 +3,7 @@ from inspect import getfullargspec
from argenta.command import Command from argenta.command import Command
from argenta.command.models import InputCommand from argenta.command.models import InputCommand
from argenta.router.command_handlers.entity import CommandHandlers from argenta.router.command_handler.entity import CommandHandlers, CommandHandler
from argenta.router.command_handler.entity import CommandHandler
from argenta.command.flag.models import Flag, Flags, InputFlags from argenta.command.flag.models import Flag, Flags, InputFlags
from argenta.router.exceptions import (RepeatedFlagNameException, from argenta.router.exceptions import (RepeatedFlagNameException,
TooManyTransferredArgsException, TooManyTransferredArgsException,
+4 -4
View File
@@ -1,3 +1,4 @@
from argenta.command import Command
from mock.mock_app.handlers.routers import work_router, settings_router from mock.mock_app.handlers.routers import work_router, settings_router
from argenta.app import App from argenta.app import App
@@ -6,14 +7,13 @@ from argenta.app.dividing_line import DynamicDividingLine
from argenta.app.autocompleter import AutoCompleter from argenta.app.autocompleter import AutoCompleter
from argenta.orchestrator import Orchestrator from argenta.orchestrator import Orchestrator
from argenta.orchestrator.argparse import ArgParse from argenta.orchestrator.argparse import ArgParse
from argenta.orchestrator.argparse.arguments import (PositionalArgument, from argenta.orchestrator.argparse.arguments import BooleanArgument
OptionalArgument,
BooleanArgument)
arg_parser = ArgParse(processed_args=[BooleanArgument('repeat')]) arg_parser = ArgParse(processed_args=[BooleanArgument('repeat')])
app: App = App(dividing_line=DynamicDividingLine(), app: App = App(dividing_line=DynamicDividingLine(),
autocompleter=AutoCompleter('./mock/.hist')) autocompleter=AutoCompleter('./mock/.hist'),
exit_command=Command('w', aliases=['test']))
orchestrator: Orchestrator = Orchestrator(arg_parser) orchestrator: Orchestrator = Orchestrator(arg_parser)