diff --git a/argenta/app/entity.py b/argenta/app/entity.py index 6861cae..500243e 100644 --- a/argenta/app/entity.py +++ b/argenta/app/entity.py @@ -3,13 +3,12 @@ from inspect import getfullargspec from ..command.entity import Command from ..router.entity import Router -from ..command.input_comand.entity import InputCommand -from ..command.input_comand.exceptions import (UnprocessedInputFlagException, - InvalidInputFlagsHandlerHasBeenAlreadyCreatedException, - IncorrectNumberOfHandlerArgsException, - UnknownCommandHandlerHasBeenAlreadyCreatedException, - RepeatedInputFlagsException, - RepeatedInputFlagsHandlerHasBeenAlreadyCreatedException) +from ..command.exceptions import (UnprocessedInputFlagException, + InvalidInputFlagsHandlerHasBeenAlreadyCreatedException, + IncorrectNumberOfHandlerArgsException, + UnknownCommandHandlerHasBeenAlreadyCreatedException, + RepeatedInputFlagsException, + RepeatedInputFlagsHandlerHasBeenAlreadyCreatedException) from .exceptions import (InvalidRouterInstanceException, InvalidDescriptionMessagePatternException, NoRegisteredRoutersException, @@ -74,7 +73,7 @@ class App: raw_command: str = input() try: - input_command: InputCommand = InputCommand.parse(raw_command=raw_command) + input_command: Command = Command.parse_input_command(raw_command=raw_command) except UnprocessedInputFlagException: self.print_func(self.line_separate) if self._invalid_input_flags_handler: diff --git a/argenta/command/entity.py b/argenta/command/entity.py index f0db2c9..b9c5fe2 100644 --- a/argenta/command/entity.py +++ b/argenta/command/entity.py @@ -2,11 +2,15 @@ from .params.flag.entity import Flag from .params.flag.flags_group.entity import FlagsGroup from .exceptions import (InvalidCommandInstanceException, InvalidDescriptionInstanceException, - InvalidFlagsInstanceException) -from .params.flag.input_flag.entity import InputFlag + InvalidFlagsInstanceException, UnprocessedInputFlagException, RepeatedInputFlagsException) + +from typing import Generic, TypeVar -class Command: +T = TypeVar('T') + + +class Command(Generic[T]): def __init__(self, command: str, description: str | None = None, flags: Flag | FlagsGroup | None = None): @@ -14,11 +18,13 @@ class Command: self._description = description self._flags: FlagsGroup | None = flags if isinstance(flags, FlagsGroup) else FlagsGroup([flags]) if isinstance(flags, Flag) else flags - self._input_flags: InputFlag | FlagsGroup | None = None + self._input_flags: FlagsGroup | None = None + def get_string_entity(self): return self._command + def get_description(self): if not self._description: description = f'description for "{self._command}" command' @@ -26,22 +32,22 @@ class Command: else: return self._description - def get_flags(self): + + def get_registered_flags(self): return self._flags - def set_command(self, command: str): - self._command = command def validate_commands_params(self): if not isinstance(self._command, str): raise InvalidCommandInstanceException(self._command) if not isinstance(self._description, str): raise InvalidDescriptionInstanceException() - if not any([(isinstance(self._flags, Flag), isinstance(self._flags, FlagsGroup)), not self._flags]): + if not any([(isinstance(self._flags, FlagsGroup)), not self._flags]): raise InvalidFlagsInstanceException - def validate_input_flag(self, flag: InputFlag): - registered_flags: FlagsGroup | Flag | None = self._flags + + def validate_input_flag(self, flag: Flag): + registered_flags: FlagsGroup | None = self.get_registered_flags() if registered_flags: if isinstance(registered_flags, Flag): if registered_flags.get_string_entity() == flag.get_string_entity(): @@ -57,4 +63,58 @@ class Command: return False + def set_input_flags(self, input_flags: FlagsGroup): + self._input_flags = input_flags + + def get_input_flags(self) -> FlagsGroup: + return self._input_flags + + @staticmethod + def parse_input_command(raw_command: str) -> 'Command[T]': + list_of_tokens = raw_command.split() + command = list_of_tokens[0] + list_of_tokens.pop(0) + + flags: FlagsGroup = FlagsGroup() + current_flag_name = None + current_flag_value = None + for _ in list_of_tokens: + if _.startswith('-'): + flag_prefix_last_symbol_index = _.rfind('-') + if current_flag_name or len(_) < 2 or len(_[:flag_prefix_last_symbol_index]) > 3: + raise UnprocessedInputFlagException() + else: + current_flag_name = _ + else: + if not current_flag_name: + raise UnprocessedInputFlagException() + else: + current_flag_value = _ + if current_flag_name and current_flag_value: + flag_prefix_last_symbol_index = current_flag_name.rfind('-') + flag_prefix = current_flag_name[:flag_prefix_last_symbol_index] + flag_name = current_flag_name[flag_prefix_last_symbol_index:] + + input_flag = Flag(flag_name=flag_name, + flag_prefix=flag_prefix) + input_flag.set_value(current_flag_value) + + all_flags = [x.get_string_entity() for x in flags.get_flags()] + if input_flag.get_string_entity() not in all_flags: + flags.add_flag(input_flag) + else: + raise RepeatedInputFlagsException(input_flag) + + current_flag_name = None + current_flag_value = None + if any([current_flag_name, current_flag_value]): + raise UnprocessedInputFlagException() + if len(flags.get_flags()) == 0: + return Command(command=command) + else: + input_command = Command(command=command) + input_command.set_input_flags(flags) + return input_command + + diff --git a/argenta/command/exceptions.py b/argenta/command/exceptions.py index 0c513be..c617ecd 100644 --- a/argenta/command/exceptions.py +++ b/argenta/command/exceptions.py @@ -1,3 +1,6 @@ +from .params.flag.entity import Flag + + class InvalidCommandInstanceException(Exception): def __str__(self): return "Invalid Command Instance" @@ -11,3 +14,36 @@ class InvalidDescriptionInstanceException(Exception): class InvalidFlagsInstanceException(Exception): def __str__(self): return "Invalid Flags Instance" + + +class UnprocessedInputFlagException(Exception): + def __str__(self): + return "Unprocessed Input Flags" + + +class RepeatedInputFlagsException(Exception): + def __init__(self, flag: Flag): + self.flag = flag + def __str__(self): + return ("Repeated Input Flags\n" + f"Duplicate flag was detected in the input: '{self.flag.get_string_entity()}'") + + +class InvalidInputFlagsHandlerHasBeenAlreadyCreatedException(Exception): + def __str__(self): + return "Invalid Input Flags Handler has already been created" + + +class RepeatedInputFlagsHandlerHasBeenAlreadyCreatedException(Exception): + def __str__(self): + return "Repeated Input Flags Handler has already been created" + + +class UnknownCommandHandlerHasBeenAlreadyCreatedException(Exception): + def __str__(self): + return "Unknown Command Handler has already been created" + + +class IncorrectNumberOfHandlerArgsException(Exception): + def __str__(self): + return "Incorrect Input Flags Handler has incorrect number of arguments" diff --git a/argenta/command/input_comand/__init__.py b/argenta/command/input_comand/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/argenta/command/input_comand/entity.py b/argenta/command/input_comand/entity.py deleted file mode 100644 index 4492f91..0000000 --- a/argenta/command/input_comand/entity.py +++ /dev/null @@ -1,66 +0,0 @@ -from ..input_comand.exceptions import UnprocessedInputFlagException, RepeatedInputFlagsException -from ..entity import Command -from ..params.flag.flags_group.entity import FlagsGroup -from ..params.flag.input_flag.entity import InputFlag - -from typing import Generic, TypeVar - - -T = TypeVar('T') - - -class InputCommand(Command, Generic[T]): - def set_input_flags(self, input_flags: FlagsGroup): - self._input_flags = input_flags - - def get_input_flags(self) -> FlagsGroup: - return self._input_flags - - @staticmethod - def parse(raw_command: str) -> 'InputCommand[T]': - list_of_tokens = raw_command.split() - command = list_of_tokens[0] - list_of_tokens.pop(0) - - flags: FlagsGroup = FlagsGroup() - current_flag_name = None - current_flag_value = None - for _ in list_of_tokens: - if _.startswith('-'): - flag_prefix_last_symbol_index = _.rfind('-') - if current_flag_name or len(_) < 2 or len(_[:flag_prefix_last_symbol_index]) > 3: - raise UnprocessedInputFlagException() - else: - current_flag_name = _ - else: - if not current_flag_name: - raise UnprocessedInputFlagException() - else: - current_flag_value = _ - if current_flag_name and current_flag_value: - flag_prefix_last_symbol_index = current_flag_name.rfind('-') - flag_prefix = current_flag_name[:flag_prefix_last_symbol_index] - flag_name = current_flag_name[flag_prefix_last_symbol_index:] - - input_flag = InputFlag(flag_name=flag_name, - flag_prefix=flag_prefix) - input_flag.set_value(current_flag_value) - - all_flags = [x.get_string_entity() for x in flags.get_flags()] - if input_flag.get_string_entity() not in all_flags: - flags.add_flag(input_flag) - else: - raise RepeatedInputFlagsException(input_flag) - - current_flag_name = None - current_flag_value = None - if any([current_flag_name, current_flag_value]): - raise UnprocessedInputFlagException() - if len(flags.get_flags()) == 0: - return InputCommand(command=command) - else: - input_command = InputCommand(command=command) - input_command.set_input_flags(flags) - return input_command - - diff --git a/argenta/command/input_comand/exceptions.py b/argenta/command/input_comand/exceptions.py deleted file mode 100644 index 6b89860..0000000 --- a/argenta/command/input_comand/exceptions.py +++ /dev/null @@ -1,37 +0,0 @@ -from ..params.flag.input_flag.entity import InputFlag - - -class UnprocessedInputFlagException(Exception): - def __str__(self): - return "Unprocessed Input Flags" - - -class RepeatedInputFlagsException(Exception): - def __init__(self, flag: InputFlag): - self.flag = flag - def __str__(self): - return ("Repeated Input Flags\n" - f"Duplicate flag was detected in the input: '{self.flag.get_string_entity()}'") - - -class InvalidInputFlagsHandlerHasBeenAlreadyCreatedException(Exception): - def __str__(self): - return "Invalid Input Flags Handler has already been created" - - -class RepeatedInputFlagsHandlerHasBeenAlreadyCreatedException(Exception): - def __str__(self): - return "Repeated Input Flags Handler has already been created" - - -class UnknownCommandHandlerHasBeenAlreadyCreatedException(Exception): - def __str__(self): - return "Unknown Command Handler has already been created" - - -class IncorrectNumberOfHandlerArgsException(Exception): - def __str__(self): - return "Incorrect Input Flags Handler has incorrect number of arguments" - - - diff --git a/argenta/command/params/flag/entity.py b/argenta/command/params/flag/entity.py index ddf7e64..21e7499 100644 --- a/argenta/command/params/flag/entity.py +++ b/argenta/command/params/flag/entity.py @@ -1,11 +1,11 @@ -from typing import Literal +from typing import Literal, Pattern class Flag: def __init__(self, flag_name: str, flag_prefix: Literal['-', '--', '---'] = '-', ignore_flag_value_register: bool = False, - possible_flag_values: list[str] = False): + possible_flag_values: list[str] | Pattern[str] = False): self._flag_name = flag_name self._flag_prefix = flag_prefix self.possible_flag_values = possible_flag_values @@ -30,7 +30,13 @@ class Flag: self._value = value def validate_input_flag_value(self, input_flag_value: str): - if self.possible_flag_values: + if isinstance(self.possible_flag_values, Pattern): + is_valid = bool(self.possible_flag_values.match(input_flag_value)) + if bool(is_valid): + return True + else: + return False + if isinstance(self.possible_flag_values, list): if self.ignore_flag_value_register: if input_flag_value.lower() in [x.lower() for x in self.possible_flag_values]: return True diff --git a/argenta/command/params/flag/flags_group/entity.py b/argenta/command/params/flag/flags_group/entity.py index 6c3f929..ec4156f 100644 --- a/argenta/command/params/flag/flags_group/entity.py +++ b/argenta/command/params/flag/flags_group/entity.py @@ -1,18 +1,17 @@ from argenta.command.params.flag.entity import Flag -from argenta.command.params.flag.input_flag.entity import InputFlag class FlagsGroup: - def __init__(self, flags: list[Flag | InputFlag] = None): - self._flags: list[Flag | InputFlag] = [] if not flags else flags + def __init__(self, flags: list[Flag] = None): + self._flags: list[Flag] = [] if not flags else flags def get_flags(self): return self._flags - def add_flag(self, flag: Flag | InputFlag): + def add_flag(self, flag: Flag): self._flags.append(flag) - def add_flags(self, flags: list[Flag | InputFlag]): + def add_flags(self, flags: list[Flag]): self._flags.extend(flags) def __iter__(self): diff --git a/argenta/command/params/flag/input_flag/__init__.py b/argenta/command/params/flag/input_flag/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/argenta/command/params/flag/input_flag/entity.py b/argenta/command/params/flag/input_flag/entity.py deleted file mode 100644 index 47f167b..0000000 --- a/argenta/command/params/flag/input_flag/entity.py +++ /dev/null @@ -1,11 +0,0 @@ -from ...flag.entity import Flag - - -class InputFlag(Flag): - def set_value(self, value: str): - self._value = value - - def get_value(self) -> str: - return self._value - - diff --git a/argenta/router/entity.py b/argenta/router/entity.py index 12aa25b..eea21d4 100644 --- a/argenta/router/entity.py +++ b/argenta/router/entity.py @@ -1,7 +1,6 @@ from typing import Callable, Any from inspect import getfullargspec from ..command.entity import Command -from ..command.input_comand.entity import InputCommand from ..command.params.flag.flags_group.entity import FlagsGroup from ..router.exceptions import (RepeatedCommandException, RepeatedFlagNameException, TooManyTransferredArgsException, @@ -53,12 +52,12 @@ class Router: return wrapper - def input_command_handler(self, input_command: InputCommand): + def input_command_handler(self, input_command: Command): input_command_name: str = input_command.get_string_entity() input_command_flags: FlagsGroup = input_command.get_input_flags() for command_entity in self._command_entities: if input_command_name.lower() == command_entity['command'].get_string_entity().lower(): - if command_entity['command'].get_flags(): + if command_entity['command'].get_registered_flags(): if input_command_flags: for flag in input_command_flags: is_valid = command_entity['command'].validate_input_flag(flag) @@ -90,7 +89,7 @@ class Router: if command_name.lower() in [x.lower() for x in self.get_all_commands()]: raise RepeatedCommandException() - flags: FlagsGroup = command.get_flags() + flags: FlagsGroup = 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): @@ -99,7 +98,7 @@ class Router: @staticmethod def _validate_func_args(command: Command, func: Callable): - registered_args = command.get_flags() + registered_args = command.get_registered_flags() transferred_args = getfullargspec(func).args if registered_args and transferred_args: if len(transferred_args) != 1: @@ -148,6 +147,6 @@ class Router: def get_all_flags(self) -> list[FlagsGroup]: all_flags: list[FlagsGroup] = [] for command_entity in self._command_entities: - all_flags.append(command_entity['command'].get_flags()) + all_flags.append(command_entity['command'].get_registered_flags()) return all_flags diff --git a/argenta/router/exceptions.py b/argenta/router/exceptions.py index d3e3469..f6e91e1 100644 --- a/argenta/router/exceptions.py +++ b/argenta/router/exceptions.py @@ -1,6 +1,3 @@ -from ..command.params.flag.input_flag.entity import InputFlag - - class InvalidDescriptionInstanceException(Exception): def __str__(self): return "Invalid Description Instance" diff --git a/pyproject.toml b/pyproject.toml index fe60837..98241f0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "argenta" -version = "0.3.2" +version = "0.3.3" description = "python library for creating custom shells" authors = [ {name = "kolo",email = "kolo.is.main@gmail.com"} diff --git a/tests/mock_app/handlers/routers.py b/tests/mock_app/handlers/routers.py index b6e545d..180e034 100644 --- a/tests/mock_app/handlers/routers.py +++ b/tests/mock_app/handlers/routers.py @@ -1,3 +1,5 @@ +import re + from rich.console import Console from argenta.command.entity import Command @@ -5,7 +7,6 @@ from argenta.command.params.flag.entity import Flag from argenta.command.params.flag.flags_group.entity import FlagsGroup from argenta.router import Router -from ..handlers.handlers_implementation.help_command import help_command work_router: Router = Router(title='Work points:') settings_router: Router = Router(title='Settings points:') @@ -14,7 +15,8 @@ console = Console() flagi = FlagsGroup(flags=[ Flag(flag_name='host', - flag_prefix='--', ), + flag_prefix='--', + possible_flag_values=re.compile(r'^192.168.\d{1,3}.\d{1,3}$')), Flag(flag_name='port', flag_prefix='--', ) ]) @@ -45,8 +47,8 @@ def command_update(): @work_router.not_valid_input_flag -def invalid_input_flag(command): - print('Invalid inpuuuuuuuuuuuuuuuuuuuuuuuut flag') +def invalid_input_flag(command: Command): + print(f'Invalid inpuuuuuuuuuuuuuuuuuuuuuuuut flag: "{command.get_input_flags()[0].get_value()}"')