mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 10:05:28 +03:00
pre-release v1.0.0
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
__all__ = ["Command"]
|
||||
|
||||
from argenta.command.models import Command
|
||||
@@ -0,0 +1,35 @@
|
||||
from argenta.command.flag.models import InputFlag, Flag
|
||||
|
||||
|
||||
class BaseInputCommandException(Exception):
|
||||
"""
|
||||
Private. Base exception class for all exceptions raised when parse input command
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class UnprocessedInputFlagException(BaseInputCommandException):
|
||||
"""
|
||||
Private. Raised when an unprocessed input flag is detected
|
||||
"""
|
||||
def __str__(self):
|
||||
return "Unprocessed Input Flags"
|
||||
|
||||
|
||||
class RepeatedInputFlagsException(BaseInputCommandException):
|
||||
"""
|
||||
Private. Raised when repeated input flags are detected
|
||||
"""
|
||||
def __init__(self, flag: 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 EmptyInputCommandException(BaseInputCommandException):
|
||||
"""
|
||||
Private. Raised when an empty input command is detected
|
||||
"""
|
||||
def __str__(self):
|
||||
return "Input Command is empty"
|
||||
@@ -0,0 +1,4 @@
|
||||
__all__ = ('InputFlags', 'InputFlag', 'Flag', 'Flags')
|
||||
|
||||
|
||||
from argenta.command.flag.models import InputFlags, InputFlag, Flags, Flag
|
||||
@@ -0,0 +1,24 @@
|
||||
from dataclasses import dataclass
|
||||
from argenta.command.flag.models import Flag
|
||||
import re
|
||||
|
||||
|
||||
@dataclass
|
||||
class PredefinedFlags:
|
||||
"""
|
||||
Public. A dataclass with predefined flags and most frequently used flags for quick use
|
||||
"""
|
||||
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}$'))
|
||||
@@ -0,0 +1,218 @@
|
||||
from typing import Literal, Pattern
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class BaseFlag(ABC):
|
||||
def __init__(self, name: str,
|
||||
prefix: Literal['-', '--', '---'] = '--') -> None:
|
||||
"""
|
||||
Private. Base class for flags
|
||||
:param name: the name of the flag
|
||||
:param prefix: the prefix of the flag
|
||||
:return: None
|
||||
"""
|
||||
self._name = name
|
||||
self._prefix = prefix
|
||||
|
||||
def get_string_entity(self) -> str:
|
||||
"""
|
||||
Public. Returns a string representation of the flag
|
||||
:return: string representation of the flag as str
|
||||
"""
|
||||
string_entity: str = self._prefix + self._name
|
||||
return string_entity
|
||||
|
||||
def get_name(self) -> str:
|
||||
"""
|
||||
Public. Returns the name of the flag
|
||||
:return: the name of the flag as str
|
||||
"""
|
||||
return self._name
|
||||
|
||||
def get_prefix(self) -> str:
|
||||
"""
|
||||
Public. Returns the prefix of the flag
|
||||
:return: the prefix of the flag as str
|
||||
"""
|
||||
return self._prefix
|
||||
|
||||
|
||||
|
||||
class InputFlag(BaseFlag):
|
||||
def __init__(self, name: str,
|
||||
prefix: Literal['-', '--', '---'] = '--',
|
||||
value: str = None):
|
||||
"""
|
||||
Public. The entity of the flag of the entered command
|
||||
:param name: the name of the input flag
|
||||
:param prefix: the prefix of the input flag
|
||||
:param value: the value of the input flag
|
||||
:return: None
|
||||
"""
|
||||
super().__init__(name, prefix)
|
||||
self._flag_value = value
|
||||
|
||||
def get_value(self) -> str | None:
|
||||
"""
|
||||
Public. Returns the value of the flag
|
||||
:return: the value of the flag as str
|
||||
"""
|
||||
return self._flag_value
|
||||
|
||||
def set_value(self, value):
|
||||
"""
|
||||
Private. Sets the value of the flag
|
||||
:param value: the fag value to set
|
||||
:return: None
|
||||
"""
|
||||
self._flag_value = value
|
||||
|
||||
|
||||
|
||||
class Flag(BaseFlag):
|
||||
def __init__(self, name: str,
|
||||
prefix: Literal['-', '--', '---'] = '--',
|
||||
possible_values: list[str] | Pattern[str] | False = True) -> None:
|
||||
"""
|
||||
Public. The entity of the flag being registered for subsequent processing
|
||||
:param name: The name of the flag
|
||||
:param prefix: The prefix of the flag
|
||||
:param possible_values: The possible values of the flag, if False then the flag cannot have a value
|
||||
:return: None
|
||||
"""
|
||||
super().__init__(name, prefix)
|
||||
self.possible_values = possible_values
|
||||
|
||||
def validate_input_flag_value(self, input_flag_value: str | None):
|
||||
"""
|
||||
Private. Validates the input flag value
|
||||
:param input_flag_value: The input flag value to validate
|
||||
:return: whether the entered flag is valid as bool
|
||||
"""
|
||||
if self.possible_values is False:
|
||||
if input_flag_value is None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
elif isinstance(self.possible_values, Pattern):
|
||||
if isinstance(input_flag_value, str):
|
||||
is_valid = bool(self.possible_values.match(input_flag_value))
|
||||
if bool(is_valid):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
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):
|
||||
"""
|
||||
Private. Base class for groups of flags
|
||||
"""
|
||||
__slots__ = ('_flags',)
|
||||
|
||||
@abstractmethod
|
||||
def get_flags(self):
|
||||
"""
|
||||
Public. Returns a list of flags
|
||||
:return: list of flags
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add_flag(self, flag: Flag | InputFlag):
|
||||
"""
|
||||
Public. Adds a flag to the list of flags
|
||||
:param flag: flag to add
|
||||
:return: None
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def add_flags(self, flags: list[Flag] | list[InputFlag]):
|
||||
"""
|
||||
Public. Adds a list of flags to the list of flags
|
||||
:param flags: list of flags to add
|
||||
:return: None
|
||||
"""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_flag(self, name: str):
|
||||
"""
|
||||
Public. Returns the flag entity by its name or None if not found
|
||||
:param name: the name of the flag to get
|
||||
:return: entity of the flag or None
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Public. A model that combines the registered flags
|
||||
:param flags: the flags that will be registered
|
||||
:return: None
|
||||
"""
|
||||
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):
|
||||
"""
|
||||
Public. A model that combines the input flags of the input command
|
||||
:param flags: all input flags
|
||||
:return: None
|
||||
"""
|
||||
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
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
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
|
||||
|
||||
|
||||
InputCommandType = TypeVar('InputCommandType')
|
||||
|
||||
|
||||
class BaseCommand:
|
||||
def __init__(self, trigger: str) -> None:
|
||||
"""
|
||||
Private. Base class for all commands
|
||||
:param trigger: A string trigger, which, when entered by the user, indicates that the input corresponds to the command
|
||||
"""
|
||||
self._trigger = trigger
|
||||
|
||||
def get_trigger(self) -> str:
|
||||
"""
|
||||
Public. Returns the trigger of the command
|
||||
:return: the trigger of the command as str
|
||||
"""
|
||||
return self._trigger
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
def __init__(self, trigger: str,
|
||||
description: str = None,
|
||||
flags: Flag | Flags = None,
|
||||
aliases: list[str] = None):
|
||||
"""
|
||||
Public. The command that can and should be registered in the Router
|
||||
:param trigger: A string trigger, which, when entered by the user, indicates that the input corresponds to the command
|
||||
:param description: the description of the command
|
||||
:param flags: processed commands
|
||||
:param aliases: string synonyms for the main trigger
|
||||
"""
|
||||
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 if isinstance(aliases, list) else []
|
||||
|
||||
def get_registered_flags(self) -> Flags:
|
||||
"""
|
||||
Private. Returns the registered flags
|
||||
:return: the registered flags as Flags
|
||||
"""
|
||||
return self._registered_flags
|
||||
|
||||
def get_aliases(self) -> list[str] | list:
|
||||
"""
|
||||
Public. Returns the aliases of the command
|
||||
:return: the aliases of the command as list[str] | list
|
||||
"""
|
||||
return self._aliases
|
||||
|
||||
def validate_input_flag(self, flag: InputFlag) -> bool:
|
||||
"""
|
||||
Private. Validates the input flag
|
||||
:param flag: input flag for validation
|
||||
:return: is input flag valid as bool
|
||||
"""
|
||||
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():
|
||||
is_valid = registered_flags.validate_input_flag_value(flag.get_value())
|
||||
if is_valid:
|
||||
return True
|
||||
else:
|
||||
for registered_flag in registered_flags:
|
||||
if registered_flag.get_string_entity() == flag.get_string_entity():
|
||||
is_valid = registered_flag.validate_input_flag_value(flag.get_value())
|
||||
if is_valid:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""
|
||||
Private. Returns the description of the command
|
||||
:return: the description of the command as str
|
||||
"""
|
||||
return self._description
|
||||
|
||||
|
||||
|
||||
class InputCommand(BaseCommand, Generic[InputCommandType]):
|
||||
def __init__(self, trigger: str,
|
||||
input_flags: InputFlag | InputFlags = None):
|
||||
"""
|
||||
Private. The model of the input command, after parsing
|
||||
:param trigger:the trigger of the command
|
||||
:param input_flags: the input flags
|
||||
:return: None
|
||||
"""
|
||||
super().__init__(trigger)
|
||||
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) -> None:
|
||||
"""
|
||||
Private. Sets the input flags
|
||||
:param input_flags: the input flags to set
|
||||
:return: None
|
||||
"""
|
||||
self._input_flags = input_flags
|
||||
|
||||
def get_input_flags(self) -> InputFlags:
|
||||
"""
|
||||
Private. Returns the input flags
|
||||
:return: the input flags as InputFlags
|
||||
"""
|
||||
return self._input_flags
|
||||
|
||||
|
||||
@staticmethod
|
||||
def parse(raw_command: str) -> InputCommandType:
|
||||
"""
|
||||
Private. Parse the raw input command
|
||||
:param raw_command: raw input command
|
||||
:return: model of the input command, after parsing as InputCommand
|
||||
"""
|
||||
if not raw_command:
|
||||
raise EmptyInputCommandException()
|
||||
|
||||
list_of_tokens = raw_command.split()
|
||||
command = list_of_tokens.pop(0)
|
||||
|
||||
input_flags: InputFlags = InputFlags()
|
||||
current_flag_name, current_flag_value = None, None
|
||||
|
||||
for k, _ in enumerate(list_of_tokens):
|
||||
if _.startswith('-'):
|
||||
if current_flag_name or len(_) < 2 or len(_[:_.rfind('-')]) > 3:
|
||||
raise UnprocessedInputFlagException()
|
||||
current_flag_name = _
|
||||
else:
|
||||
if not current_flag_name:
|
||||
raise UnprocessedInputFlagException()
|
||||
current_flag_value = _
|
||||
|
||||
if current_flag_name:
|
||||
if not len(list_of_tokens) == k+1:
|
||||
if not list_of_tokens[k+1].startswith('-'):
|
||||
continue
|
||||
|
||||
input_flag = InputFlag(name=current_flag_name[current_flag_name.rfind('-')+1:],
|
||||
prefix=cast(Literal['-', '--', '---'],
|
||||
current_flag_name[:current_flag_name.rfind('-')+1]),
|
||||
value=current_flag_value)
|
||||
|
||||
all_flags = [flag.get_string_entity() for flag in input_flags.get_flags()]
|
||||
if input_flag.get_string_entity() not in all_flags:
|
||||
input_flags.add_flag(input_flag)
|
||||
else:
|
||||
raise RepeatedInputFlagsException(input_flag)
|
||||
|
||||
current_flag_name, current_flag_value = None, None
|
||||
|
||||
if any([current_flag_name, current_flag_value]):
|
||||
raise UnprocessedInputFlagException()
|
||||
else:
|
||||
return InputCommand(trigger=command, input_flags=input_flags)
|
||||
|
||||
Reference in New Issue
Block a user