From e189f8d9aa61f7103a2c09b1e4c830c6d7edcfa9 Mon Sep 17 00:00:00 2001 From: kolo Date: Mon, 14 Apr 2025 16:38:53 +0300 Subject: [PATCH] big step --- argenta/app/models.py | 135 +++++++++++++------- argenta/command/models.py | 2 +- argenta/router/command_handler/entity.py | 26 +++- argenta/router/command_handlers/__init__.py | 0 argenta/router/command_handlers/entity.py | 21 --- argenta/router/entity.py | 3 +- mock/mock_app/main.py | 8 +- 7 files changed, 117 insertions(+), 78 deletions(-) delete mode 100644 argenta/router/command_handlers/__init__.py delete mode 100644 argenta/router/command_handlers/entity.py diff --git a/argenta/app/models.py b/argenta/app/models.py index a35b685..b35754e 100644 --- a/argenta/app/models.py +++ b/argenta/app/models.py @@ -20,23 +20,24 @@ from argenta.app.registered_routers.entity import RegisteredRouters -class AppInit: +class 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_points_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: str, + initial_message: str, + farewell_message: str, + exit_command: Command, + system_router_title: str | None, + ignore_command_register: bool, + dividing_line: StaticDividingLine | DynamicDividingLine, + repeat_command_groups: bool, + override_system_messages: bool, + autocompleter: AutoCompleter, + print_func: Callable[[str], None]) -> None: + self._prompt = prompt self._print_func = print_func 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._ignore_command_register = ignore_command_register 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._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._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) - -class AppSetters(AppInit): def set_description_message_pattern(self, pattern: Callable[[str, str], str]) -> None: self._description_message_gen: Callable[[str, str], str] = pattern @@ -84,8 +86,6 @@ class AppSetters(AppInit): self._exit_command_handler = handler - -class AppPrinters(AppInit): def _print_command_group_description(self): for registered_router in self._registered_routers: 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)) - -class AppNonStandardHandlers(AppPrinters): def _is_exit_command(self, command: InputCommand): - if command.get_trigger().lower() == self._exit_command.get_trigger().lower(): - if self._ignore_command_register: - system_router.input_command_handler(command) + if self._ignore_command_register: + if command.get_trigger().lower() == self._exit_command.get_trigger().lower(): return True - elif command.get_trigger() == self._exit_command.get_trigger(): - system_router.input_command_handler(command) + elif command.get_trigger().lower() in [x.lower() for x in self._exit_command.get_aliases()]: + 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 False def _is_unknown_command(self, command: InputCommand): - for router_entity in self._registered_routers: - for command_handler in router_entity.get_command_handlers(): - handled_command_trigger = command_handler.get_handled_command().get_trigger() - handled_command_aliases = command_handler.get_handled_command().get_aliases() - if handled_command_trigger.lower() == command.get_trigger().lower() and self._ignore_command_register: - return False - elif handled_command_trigger == command.get_trigger(): - 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 + input_command_trigger = command.get_trigger() + if self._ignore_command_register: + if input_command_trigger.lower() in self._all_registered_triggers_in_lower: + return False + else: + if input_command_trigger in self._all_registered_triggers_in_default_case: + return False + with redirect_stdout(io.StringIO()) as f: self._unknown_command_handler(command) res: str = f.getvalue() @@ -157,18 +153,14 @@ class AppNonStandardHandlers(AppPrinters): self._empty_input_command_handler() - -class AppValidators(AppInit): def _validate_included_routers(self) -> None: for router in self._registered_routers: if not router.get_command_handlers(): raise NoRegisteredHandlersException(router.get_name()) - -class AppSetups(AppValidators, AppPrinters): 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) def exit_command(): @@ -178,6 +170,7 @@ class AppSetups(AppValidators, AppPrinters): system_router.set_ignore_command_register(self._ignore_command_register) self._registered_routers.add_registered_router(system_router) + def _setup_default_view(self): if not self._override_system_messages: 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._unknown_command_handler = lambda command: self._print_func(f"[red bold]Unknown command: {escape(command.get_trigger())}") + def _pre_cycle_setup(self): self._setup_default_view() self._setup_system_router() self._validate_included_routers() - all_triggers: list[str] = [] for router_entity in self._registered_routers: - all_triggers.extend(router_entity.get_triggers()) - all_triggers.extend(router_entity.get_aliases()) - self._autocompleter.initial_setup(all_triggers) + self._all_registered_triggers_in_default_case.extend(router_entity.get_triggers()) + self._all_registered_triggers_in_default_case.extend(router_entity.get_aliases()) + + 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) @@ -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: self._pre_cycle_setup() while True: @@ -233,6 +271,7 @@ class App(AppSetters, AppNonStandardHandlers, AppSetups): continue if self._is_exit_command(input_command): + system_router.input_command_handler(input_command) self._autocompleter.exit_setup() return diff --git a/argenta/command/models.py b/argenta/command/models.py index 22712c3..71897c5 100644 --- a/argenta/command/models.py +++ b/argenta/command/models.py @@ -24,7 +24,7 @@ class Command(BaseCommand): super().__init__(trigger) 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._aliases = aliases + self._aliases = aliases if isinstance(aliases, list) else [] def get_registered_flags(self) -> Flags: return self._registered_flags diff --git a/argenta/router/command_handler/entity.py b/argenta/router/command_handler/entity.py index 0ab2451..9b371d0 100644 --- a/argenta/router/command_handler/entity.py +++ b/argenta/router/command_handler/entity.py @@ -1,6 +1,8 @@ from typing import Callable + from argenta.command import Command -from argenta.command.flag.models import InputFlags +from argenta.command.flag import InputFlags + class CommandHandler: @@ -18,4 +20,24 @@ class CommandHandler: return self._handler def get_handled_command(self): - return self._handled_command \ No newline at end of file + 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)) \ No newline at end of file diff --git a/argenta/router/command_handlers/__init__.py b/argenta/router/command_handlers/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/argenta/router/command_handlers/entity.py b/argenta/router/command_handlers/entity.py deleted file mode 100644 index fc28b42..0000000 --- a/argenta/router/command_handlers/entity.py +++ /dev/null @@ -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)) \ No newline at end of file diff --git a/argenta/router/entity.py b/argenta/router/entity.py index cb070a3..0453784 100644 --- a/argenta/router/entity.py +++ b/argenta/router/entity.py @@ -3,8 +3,7 @@ from inspect import getfullargspec from argenta.command import Command from argenta.command.models import InputCommand -from argenta.router.command_handlers.entity import CommandHandlers -from argenta.router.command_handler.entity import CommandHandler +from argenta.router.command_handler.entity import CommandHandlers, CommandHandler from argenta.command.flag.models import Flag, Flags, InputFlags from argenta.router.exceptions import (RepeatedFlagNameException, TooManyTransferredArgsException, diff --git a/mock/mock_app/main.py b/mock/mock_app/main.py index a340841..199a217 100644 --- a/mock/mock_app/main.py +++ b/mock/mock_app/main.py @@ -1,3 +1,4 @@ +from argenta.command import Command from mock.mock_app.handlers.routers import work_router, settings_router from argenta.app import App @@ -6,14 +7,13 @@ from argenta.app.dividing_line import DynamicDividingLine from argenta.app.autocompleter import AutoCompleter from argenta.orchestrator import Orchestrator from argenta.orchestrator.argparse import ArgParse -from argenta.orchestrator.argparse.arguments import (PositionalArgument, - OptionalArgument, - BooleanArgument) +from argenta.orchestrator.argparse.arguments import BooleanArgument arg_parser = ArgParse(processed_args=[BooleanArgument('repeat')]) 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)