new models, a model is passed to the command handler instead of a dictionary, removal of checks for intersection of processed triggers in handlers and much, much more

This commit is contained in:
2025-03-31 19:14:42 +03:00
parent 2918bc9f81
commit 5c6fa5151a
24 changed files with 283 additions and 279 deletions
+1 -1
View File
@@ -1,3 +1,3 @@
__all__ = ["Command"]
from .entity import Command
from .models import Command
+1 -2
View File
@@ -1,5 +1,4 @@
from argenta.command.flag.input_flag.entity import InputFlag
from argenta.command.flag.registered_flag.entity import Flag
from argenta.command.flag.models import InputFlag, Flag
class UnprocessedInputFlagException(Exception):
-41
View File
@@ -1,41 +0,0 @@
from typing import Literal, Pattern
class BaseFlag:
def __init__(self, flag_name: str,
flag_prefix: Literal['-', '--', '---'] = '--',
possible_flag_values: list[str] | Pattern[str] | False = True):
self._flag_name = flag_name
self._flag_prefix = flag_prefix
self.possible_flag_values = possible_flag_values
def get_string_entity(self):
string_entity: str = self._flag_prefix + self._flag_name
return string_entity
def get_flag_name(self):
return self._flag_name
def get_flag_prefix(self):
return self._flag_prefix
def validate_input_flag_value(self, input_flag_value: str | None):
if self.possible_flag_values is False:
if input_flag_value is None:
return True
else:
return False
elif 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
elif isinstance(self.possible_flag_values, list):
if input_flag_value in self.possible_flag_values:
return True
else:
return False
else:
return True
+21
View File
@@ -0,0 +1,21 @@
from dataclasses import dataclass
from argenta.command.flag.models import Flag
import re
@dataclass
class DefaultFlags:
HELP = Flag(name='help', possible_values=False)
SHORT_HELP = Flag(name='h', prefix='-', possible_values=False)
INFO = Flag(name='info', possible_values=False)
SHORT_INFO = Flag(name='i', prefix='-', possible_values=False)
ALL = Flag(name='all', possible_values=False)
SHORT_ALL = Flag(name='a', prefix='-', possible_values=False)
HOST = Flag(name='host', possible_values=re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'))
SHORT_HOST = Flag(name='h', prefix='-', possible_values=re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'))
PORT = Flag(name='port', possible_values=re.compile(r'^\d{1,5}$'))
SHORT_PORT = Flag(name='p', prefix='-', possible_values=re.compile(r'^\d{1,5}$'))
@@ -1,4 +0,0 @@
__all__ = ["FlagsGroup"]
from .entity import FlagsGroup
@@ -1,36 +0,0 @@
from argenta.command.flag.input_flag.entity import InputFlag
from argenta.command.flag.registered_flag import Flag
class FlagsGroup:
def __init__(self, *flags: Flag | InputFlag):
self._flags: list[Flag | InputFlag] = [] if not flags else flags
def get_flags(self) -> list[Flag | InputFlag]:
return self._flags
def add_flag(self, flag: Flag | InputFlag):
self._flags.append(flag)
def add_flags(self, flags: list[Flag | InputFlag]):
self._flags.extend(flags)
def unparse_to_dict(self):
result_dict: dict[str, dict] = {}
for flag in self.get_flags():
result_dict[flag.get_flag_name()] = {
'name': flag.get_flag_name(),
'string_entity': flag.get_string_entity(),
'prefix': flag.get_flag_prefix(),
'value': flag.get_value()
}
return result_dict
def __iter__(self):
return iter(self._flags)
def __next__(self):
return next(iter(self))
def __getitem__(self, item):
return self._flags[item]
-18
View File
@@ -1,18 +0,0 @@
from re import Pattern
from typing import Literal
from argenta.command.flag.base_flag.entity import BaseFlag
class InputFlag(BaseFlag):
def __init__(self, flag_name: str,
flag_prefix: Literal['-', '--', '---'] = '--',
possible_flag_values: list[str] | Pattern[str] | False = True):
super().__init__(flag_name, flag_prefix, possible_flag_values)
self._flag_value = None
def get_value(self):
return self._flag_value
def set_value(self, value):
self._flag_value = value
+133
View File
@@ -0,0 +1,133 @@
from typing import Literal, Pattern
from abc import ABC, abstractmethod
class BaseFlag:
def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--',
possible_values: list[str] | Pattern[str] | False = True):
self._name = name
self._prefix = prefix
self.possible_values = possible_values
def get_string_entity(self):
string_entity: str = self._prefix + self._name
return string_entity
def get_name(self):
return self._name
def get_prefix(self):
return self._prefix
class InputFlag(BaseFlag):
def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--',
possible_values: list[str] | Pattern[str] | False = True):
super().__init__(name, prefix, possible_values)
self._flag_value = None
def get_value(self):
return self._flag_value
def set_value(self, value):
self._flag_value = value
class Flag(BaseFlag):
def validate_input_flag_value(self, input_flag_value: str | None):
if self.possible_values is False:
if input_flag_value is None:
return True
else:
return False
elif isinstance(self.possible_values, Pattern):
is_valid = bool(self.possible_values.match(input_flag_value))
if bool(is_valid):
return True
else:
return False
elif isinstance(self.possible_values, list):
if input_flag_value in self.possible_values:
return True
else:
return False
else:
return True
class BaseFlags(ABC):
__slots__ = ('_flags',)
@abstractmethod
def get_flags(self):
pass
@abstractmethod
def add_flag(self, flag: Flag | InputFlag):
pass
@abstractmethod
def add_flags(self, flags: list[Flag] | list[InputFlag]):
pass
@abstractmethod
def get_flag(self, name: str):
pass
def __iter__(self):
return iter(self._flags)
def __next__(self):
return next(iter(self))
def __getitem__(self, item):
return self._flags[item]
class Flags(BaseFlags, ABC):
def __init__(self, *flags: Flag):
self._flags = flags if flags else []
def get_flags(self) -> list[Flag]:
return self._flags
def add_flag(self, flag: Flag):
self._flags.append(flag)
def add_flags(self, flags: list[Flag]):
self._flags.extend(flags)
def get_flag(self, name: str) -> Flag | None:
if name in [flag.get_name() for flag in self._flags]:
return list(filter(lambda flag: flag.get_name() == name, self._flags))[0]
else:
return None
class InputFlags(BaseFlags, ABC):
def __init__(self, *flags: InputFlag):
self._flags = flags if flags else []
def get_flags(self) -> list[InputFlag]:
return self._flags
def add_flag(self, flag: InputFlag):
self._flags.append(flag)
def add_flags(self, flags: list[InputFlag]):
self._flags.extend(flags)
def get_flag(self, name: str) -> InputFlag | None:
if name in [flag.get_name() for flag in self._flags]:
return list(filter(lambda flag: flag.get_name() == name, self._flags))[0]
else:
return None
@@ -1,5 +0,0 @@
__all__ = ["Flag", "FlagsGroup"]
from .entity import Flag
from argenta.command.flag.flags_group import FlagsGroup
@@ -1,21 +0,0 @@
from dataclasses import dataclass
from argenta.command.flag.registered_flag import Flag
import re
@dataclass
class DefaultFlags:
HELP = Flag(flag_name='help', possible_flag_values=False)
SHORT_HELP = Flag(flag_name='h', flag_prefix='-', possible_flag_values=False)
INFO = Flag(flag_name='info', possible_flag_values=False)
SHORT_INFO = Flag(flag_name='i', flag_prefix='-', possible_flag_values=False)
ALL = Flag(flag_name='all', possible_flag_values=False)
SHORT_ALL = Flag(flag_name='a', flag_prefix='-', possible_flag_values=False)
HOST = Flag(flag_name='host', possible_flag_values=re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'))
SHORT_HOST = Flag(flag_name='h', flag_prefix='-', possible_flag_values=re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'))
PORT = Flag(flag_name='port', possible_flag_values=re.compile(r'^\d{1,5}$'))
SHORT_PORT = Flag(flag_name='p', flag_prefix='-', possible_flag_values=re.compile(r'^\d{1,5}$'))
@@ -1,5 +0,0 @@
from argenta.command.flag.base_flag.entity import BaseFlag
class Flag(BaseFlag):
pass
@@ -1,40 +1,40 @@
from argenta.command.flag.registered_flag.entity import Flag
from argenta.command.flag.input_flag.entity import InputFlag
from argenta.command.flag.flags_group import FlagsGroup
from .exceptions import (UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException)
from argenta.command.flag.models import Flag, InputFlag, Flags, InputFlags
from argenta.command.exceptions import (UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException)
from typing import Generic, TypeVar, cast, Literal
CommandType = TypeVar('CommandType')
BaseCommandType = TypeVar('BaseCommandType')
class Command(Generic[CommandType]):
class BaseCommand(Generic[BaseCommandType]):
def __init__(self, trigger: str,
description: str = None,
flags: Flag | FlagsGroup = None):
flags: Flag | Flags = None):
self._trigger = trigger
self._description = f'description for "{self._trigger}" command' if not description else description
self._registered_flags: FlagsGroup | None = flags if isinstance(flags, FlagsGroup) else FlagsGroup(flags) if isinstance(flags, Flag) else flags
self._input_flags: FlagsGroup | None = None
def get_trigger(self) -> str:
return self._trigger
def get_description(self) -> str:
return self._description
def get_registered_flags(self) -> FlagsGroup | None:
class Command(BaseCommand):
def __init__(self, trigger: str,
description: str = None,
flags: Flag | Flags = None):
super().__init__(trigger, description)
self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags()
def get_registered_flags(self) -> Flags:
return self._registered_flags
def validate_input_flag(self, flag: InputFlag):
registered_flags: FlagsGroup | None = self.get_registered_flags()
registered_flags: Flags | None = self.get_registered_flags()
if registered_flags:
if isinstance(registered_flags, Flag):
if registered_flags.get_string_entity() == flag.get_string_entity():
@@ -50,21 +50,29 @@ class Command(Generic[CommandType]):
return False
def _set_input_flags(self, input_flags: FlagsGroup):
class InputCommand(BaseCommand):
def __init__(self, trigger: str,
description: str = None,
input_flags: InputFlag | InputFlags = None):
super().__init__(trigger, description)
self._input_flags: InputFlags = input_flags if isinstance(input_flags, InputFlags) else InputFlags(input_flags) if isinstance(input_flags, InputFlag) else InputFlags()
def _set_input_flags(self, input_flags: InputFlags):
self._input_flags = input_flags
def get_input_flags(self) -> FlagsGroup:
def get_input_flags(self) -> InputFlags:
return self._input_flags
@staticmethod
def parse_input_command(raw_command: str) -> CommandType:
def parse_input_command(raw_command: str) -> BaseCommandType:
if not raw_command:
raise EmptyInputCommandException()
list_of_tokens = raw_command.split()
command = list_of_tokens[0]
list_of_tokens.pop(0)
flags: FlagsGroup = FlagsGroup()
input_flags: InputFlags = InputFlags()
current_flag_name = None
current_flag_value = None
for k, _ in enumerate(list_of_tokens):
@@ -80,19 +88,19 @@ class Command(Generic[CommandType]):
else:
current_flag_value = _
if current_flag_name:
if not len(list_of_tokens) == k+1:
if not list_of_tokens[k+1].startswith('-'):
if not len(list_of_tokens) == k + 1:
if not list_of_tokens[k + 1].startswith('-'):
continue
flag_prefix_last_symbol_index = current_flag_name.rfind('-')
flag_prefix = current_flag_name[:flag_prefix_last_symbol_index+1]
flag_name = current_flag_name[flag_prefix_last_symbol_index+1:]
input_flag = InputFlag(flag_name=flag_name,
flag_prefix=cast(Literal['-', '--', '---'], flag_prefix))
flag_prefix = current_flag_name[:flag_prefix_last_symbol_index + 1]
flag_name = current_flag_name[flag_prefix_last_symbol_index + 1:]
input_flag = InputFlag(name=flag_name,
prefix=cast(Literal['-', '--', '---'], flag_prefix))
input_flag.set_value(current_flag_value)
all_flags = [x.get_string_entity() for x in flags.get_flags()]
all_flags = [x.get_string_entity() for x in input_flags.get_flags()]
if input_flag.get_string_entity() not in all_flags:
flags.add_flag(input_flag)
input_flags.add_flag(input_flag)
else:
raise RepeatedInputFlagsException(input_flag)
@@ -100,12 +108,10 @@ class Command(Generic[CommandType]):
current_flag_value = None
if any([current_flag_name, current_flag_value]):
raise UnprocessedInputFlagException()
if len(flags.get_flags()) == 0:
return Command(trigger=command)
if len(input_flags.get_flags()) == 0:
return InputCommand(trigger=command)
else:
input_command = Command(trigger=command)
input_command._set_input_flags(flags)
input_command = InputCommand(trigger=command)
input_command._set_input_flags(input_flags)
return input_command