mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-11 18:45:29 +03:00
ref: typehints, enum instead of raw string, abc and other (#1)
Full code coverage with annotations, fixing errors in various linters: ruff, wps, etc. Fixing errors in type checkers: ty, mypy, pyright. Formatting and bringing code to a consistent style, applying best practices in various aspects.
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
__all__ = ["Command"]
|
||||
__all__ = [
|
||||
"Command",
|
||||
"PossibleValues",
|
||||
"PredefinedFlags",
|
||||
"InputCommand",
|
||||
"Flags",
|
||||
"Flag"
|
||||
]
|
||||
|
||||
from argenta.command.models import Command
|
||||
from argenta.command.models import Command, InputCommand
|
||||
from argenta.command.flag import defaults as PredefinedFlags
|
||||
from argenta.command.flag import (Flag, Flags, PossibleValues)
|
||||
|
||||
@@ -1,42 +1,49 @@
|
||||
from argenta.command.flag.models import Flag, InputFlag
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import override
|
||||
|
||||
|
||||
class BaseInputCommandException(Exception):
|
||||
class InputCommandException(ABC, Exception):
|
||||
"""
|
||||
Private. Base exception class for all exceptions raised when parse input command
|
||||
"""
|
||||
|
||||
pass
|
||||
@override
|
||||
@abstractmethod
|
||||
def __str__(self) -> str:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class UnprocessedInputFlagException(BaseInputCommandException):
|
||||
class UnprocessedInputFlagException(InputCommandException):
|
||||
"""
|
||||
Private. Raised when an unprocessed input flag is detected
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return "Unprocessed Input Flags"
|
||||
|
||||
|
||||
class RepeatedInputFlagsException(BaseInputCommandException):
|
||||
class RepeatedInputFlagsException(InputCommandException):
|
||||
"""
|
||||
Private. Raised when repeated input flags are detected
|
||||
"""
|
||||
|
||||
def __init__(self, flag: Flag | InputFlag):
|
||||
self.flag = flag
|
||||
self.flag: Flag | InputFlag = flag
|
||||
super().__init__()
|
||||
|
||||
def __str__(self):
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
string_entity: str = self.flag.string_entity
|
||||
return (
|
||||
"Repeated Input Flags\n"
|
||||
f"Duplicate flag was detected in the input: '{self.flag.get_string_entity()}'"
|
||||
f"Duplicate flag was detected in the input: '{string_entity}'"
|
||||
)
|
||||
|
||||
|
||||
class EmptyInputCommandException(BaseInputCommandException):
|
||||
class EmptyInputCommandException(InputCommandException):
|
||||
"""
|
||||
Private. Raised when an empty input command is detected
|
||||
"""
|
||||
|
||||
def __str__(self):
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return "Input Command is empty"
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
__all__ = [
|
||||
"Flag",
|
||||
"InputFlag",
|
||||
"UndefinedInputFlags",
|
||||
"ValidInputFlags",
|
||||
"InvalidValueInputFlags",
|
||||
"Flags", "PossibleValues"
|
||||
"Flags",
|
||||
"PossibleValues",
|
||||
"ValidationStatus"
|
||||
]
|
||||
|
||||
|
||||
from argenta.command.flag.models import Flag, InputFlag, PossibleValues
|
||||
from argenta.command.flag.flags.models import (
|
||||
UndefinedInputFlags,
|
||||
ValidInputFlags,
|
||||
Flags,
|
||||
InvalidValueInputFlags,
|
||||
)
|
||||
from argenta.command.flag.models import Flag, InputFlag, PossibleValues, ValidationStatus
|
||||
from argenta.command.flag.flags.models import Flags
|
||||
|
||||
@@ -1,32 +1,27 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
from argenta.command.flag.models import Flag, PossibleValues
|
||||
import re
|
||||
import re
|
||||
|
||||
|
||||
DEFAULT_PREFIX: Literal["-", "--", "---"] = "-"
|
||||
|
||||
@dataclass
|
||||
class PredefinedFlags:
|
||||
"""
|
||||
Public. A dataclass with predefined flags and most frequently used flags for quick use
|
||||
"""
|
||||
HELP = Flag(name="help", possible_values=PossibleValues.NEITHER)
|
||||
SHORT_HELP = Flag(name="H", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
|
||||
HELP = Flag(name="help", possible_values=PossibleValues.DISABLE)
|
||||
SHORT_HELP = Flag(name="H", prefix="-", possible_values=PossibleValues.DISABLE)
|
||||
INFO = Flag(name="info", possible_values=PossibleValues.NEITHER) # noqa: WPS110
|
||||
SHORT_INFO = Flag(name="I", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
|
||||
INFO = Flag(name="info", possible_values=PossibleValues.DISABLE)
|
||||
SHORT_INFO = Flag(name="I", prefix="-", possible_values=PossibleValues.DISABLE)
|
||||
ALL = Flag(name="all", possible_values=PossibleValues.NEITHER)
|
||||
SHORT_ALL = Flag(name="A", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
|
||||
ALL = Flag(name="all", possible_values=PossibleValues.DISABLE)
|
||||
SHORT_ALL = Flag(name="A", prefix="-", possible_values=PossibleValues.DISABLE)
|
||||
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=DEFAULT_PREFIX,
|
||||
possible_values=re.compile(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$"),
|
||||
)
|
||||
|
||||
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}$"))
|
||||
PORT = Flag(name="port", possible_values=re.compile(r"^\d{1,5}$"))
|
||||
SHORT_PORT = Flag(name="P", prefix=DEFAULT_PREFIX, possible_values=re.compile(r"^\d{1,5}$"))
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
__all__ = [
|
||||
"Flags",
|
||||
"InputFlags",
|
||||
"UndefinedInputFlags",
|
||||
"InvalidValueInputFlags",
|
||||
"ValidInputFlags",
|
||||
"InputFlags"
|
||||
]
|
||||
|
||||
|
||||
from argenta.command.flag.flags.models import (
|
||||
Flags,
|
||||
InputFlags,
|
||||
UndefinedInputFlags,
|
||||
InvalidValueInputFlags,
|
||||
ValidInputFlags,
|
||||
InputFlags
|
||||
)
|
||||
|
||||
@@ -1,90 +1,106 @@
|
||||
from argenta.command.flag.models import InputFlag, Flag
|
||||
from typing import Generic, TypeVar
|
||||
from typing import Generic, TypeVar, override
|
||||
from collections.abc import Iterator
|
||||
|
||||
|
||||
FlagType = TypeVar("FlagType")
|
||||
|
||||
|
||||
class BaseFlags(Generic[FlagType]):
|
||||
def __init__(self, *flags: FlagType):
|
||||
def __init__(self, flags: list[FlagType] | None = None) -> None:
|
||||
"""
|
||||
Public. A model that combines the registered flags
|
||||
:param flags: the flags that will be registered
|
||||
:return: None
|
||||
"""
|
||||
self._flags = flags if flags else []
|
||||
self.flags: list[FlagType] = flags if flags else []
|
||||
|
||||
def get_flags(self) -> list[FlagType]:
|
||||
"""
|
||||
Public. Returns a list of flags
|
||||
:return: list of flags as list[FlagType]
|
||||
"""
|
||||
return self._flags
|
||||
|
||||
def add_flag(self, flag: FlagType):
|
||||
def add_flag(self, flag: FlagType) -> None:
|
||||
"""
|
||||
Public. Adds a flag to the list of flags
|
||||
:param flag: flag to add
|
||||
:return: None
|
||||
"""
|
||||
self._flags.append(flag)
|
||||
self.flags.append(flag)
|
||||
|
||||
def add_flags(self, flags: list[FlagType]):
|
||||
def add_flags(self, flags: list[FlagType]) -> None:
|
||||
"""
|
||||
Public. Adds a list of flags to the list of flags
|
||||
:param flags: list of flags to add
|
||||
:return: None
|
||||
"""
|
||||
self._flags.extend(flags)
|
||||
self.flags.extend(flags)
|
||||
|
||||
def get_flag(self, name: str) -> FlagType | None:
|
||||
def __iter__(self) -> Iterator[FlagType]:
|
||||
return iter(self.flags)
|
||||
|
||||
def __next__(self) -> FlagType:
|
||||
return next(iter(self))
|
||||
|
||||
def __getitem__(self, flag_index: int) -> FlagType:
|
||||
return self.flags[flag_index]
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return bool(self.flags)
|
||||
|
||||
|
||||
class Flags(BaseFlags[Flag]):
|
||||
def get_flag_by_name(self, name: str) -> Flag | None:
|
||||
"""
|
||||
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
|
||||
"""
|
||||
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
|
||||
return next((flag for flag in self.flags if flag.name == name), None)
|
||||
|
||||
@override
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, Flags):
|
||||
return NotImplemented
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._flags)
|
||||
if len(self.flags) != len(other.flags):
|
||||
return False
|
||||
|
||||
def __next__(self):
|
||||
return next(iter(self))
|
||||
flag_pairs: zip[tuple[Flag, Flag]] = zip(self.flags, other.flags)
|
||||
return all(s_flag == o_flag for s_flag, o_flag in flag_pairs)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self._flags[item]
|
||||
|
||||
def __bool__(self):
|
||||
return bool(self._flags)
|
||||
|
||||
def __eq__(self, other):
|
||||
if len(self.get_flags()) != len(other.get_flags()):
|
||||
def __contains__(self, flag_to_check: object) -> bool:
|
||||
if isinstance(flag_to_check, Flag):
|
||||
for flag in self.flags:
|
||||
if flag == flag_to_check:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
for flag, other_flag in zip(self.get_flags(), other.get_flags()):
|
||||
if not flag == other_flag:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class Flags(BaseFlags[Flag]):
|
||||
pass
|
||||
raise TypeError
|
||||
|
||||
|
||||
class InputFlags(BaseFlags[InputFlag]):
|
||||
pass
|
||||
def get_flag_by_name(self, name: str) -> InputFlag | None:
|
||||
"""
|
||||
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
|
||||
"""
|
||||
return next((flag for flag in self.flags if flag.name == name), None)
|
||||
|
||||
@override
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, InputFlags):
|
||||
raise NotImplementedError
|
||||
|
||||
if len(self.flags) != len(other.flags):
|
||||
return False
|
||||
|
||||
class ValidInputFlags(InputFlags):
|
||||
pass
|
||||
paired_flags: zip[tuple[InputFlag, InputFlag]] = zip(self.flags, other.flags)
|
||||
|
||||
return all(my_flag == other_flag for my_flag, other_flag in paired_flags)
|
||||
|
||||
class UndefinedInputFlags(InputFlags):
|
||||
pass
|
||||
def __contains__(self, ingressable_item: object) -> bool:
|
||||
if isinstance(ingressable_item, InputFlag):
|
||||
for flag in self.flags:
|
||||
if flag == ingressable_item:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
|
||||
class InvalidValueInputFlags(InputFlags):
|
||||
pass
|
||||
|
||||
@@ -1,57 +1,22 @@
|
||||
from enum import Enum
|
||||
from typing import Literal, Pattern
|
||||
|
||||
from re import Pattern
|
||||
from typing import Literal, override
|
||||
|
||||
|
||||
class PossibleValues(Enum):
|
||||
DISABLE: Literal[False] = False
|
||||
ALL: Literal[True] = True
|
||||
|
||||
def __eq__(self, other: bool) -> bool:
|
||||
return self.value == other
|
||||
NEITHER = 'NEITHER'
|
||||
ALL = 'ALL'
|
||||
|
||||
|
||||
class BaseFlag:
|
||||
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
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return self.get_string_entity() == other.get_string_entity()
|
||||
class ValidationStatus(Enum):
|
||||
VALID = 'VALID'
|
||||
INVALID = 'INVALID'
|
||||
UNDEFINED = 'UNDEFINED'
|
||||
|
||||
|
||||
class Flag(BaseFlag):
|
||||
class Flag:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
self, name: str, *,
|
||||
prefix: Literal["-", "--", "---"] = "--",
|
||||
possible_values: list[str] | Pattern[str] | PossibleValues = PossibleValues.ALL,
|
||||
) -> None:
|
||||
@@ -62,45 +27,58 @@ class Flag(BaseFlag):
|
||||
: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
|
||||
self.name: str = name
|
||||
self.prefix: Literal["-", "--", "---"] = prefix
|
||||
self.possible_values: list[str] | Pattern[str] | PossibleValues = possible_values
|
||||
|
||||
def validate_input_flag_value(self, input_flag_value: str | None):
|
||||
def validate_input_flag_value(self, input_flag_value: str | None) -> bool:
|
||||
"""
|
||||
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 == PossibleValues.DISABLE:
|
||||
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
|
||||
if self.possible_values == PossibleValues.NEITHER:
|
||||
return input_flag_value is None
|
||||
|
||||
elif isinstance(self.possible_values, list):
|
||||
if input_flag_value in self.possible_values:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
if isinstance(self.possible_values, Pattern):
|
||||
return isinstance(input_flag_value, str) and bool(self.possible_values.match(input_flag_value))
|
||||
|
||||
if isinstance(self.possible_values, list):
|
||||
return input_flag_value in self.possible_values
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def 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
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return self.string_entity
|
||||
|
||||
@override
|
||||
def __repr__(self) -> str:
|
||||
return f'Flag<name={self.name}, prefix={self.prefix}>'
|
||||
|
||||
@override
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Flag):
|
||||
return self.string_entity == other.string_entity
|
||||
else:
|
||||
return True
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class InputFlag(BaseFlag):
|
||||
class InputFlag:
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
prefix: Literal["-", "--", "---"] = "--",
|
||||
value: str | None = None,
|
||||
self, name: str, *,
|
||||
prefix: Literal['-', '--', '---'] = '--',
|
||||
input_value: str | None,
|
||||
status: ValidationStatus | None
|
||||
):
|
||||
"""
|
||||
Public. The entity of the flag of the entered command
|
||||
@@ -109,26 +87,33 @@ class InputFlag(BaseFlag):
|
||||
:param value: the value of the input flag
|
||||
:return: None
|
||||
"""
|
||||
super().__init__(name, prefix)
|
||||
self._flag_value = value
|
||||
self.name: str = name
|
||||
self.prefix: Literal['-', '--', '---'] = prefix
|
||||
self.input_value: str | None = input_value
|
||||
self.status: ValidationStatus | None = status
|
||||
|
||||
@property
|
||||
def 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_value(self) -> str | None:
|
||||
"""
|
||||
Public. Returns the value of the flag
|
||||
:return: the value of the flag as str
|
||||
"""
|
||||
return self._flag_value
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return f'{self.string_entity} {self.input_value}'
|
||||
|
||||
@override
|
||||
def __repr__(self) -> str:
|
||||
return f'InputFlag<name={self.name}, prefix={self.prefix}, value={self.input_value}, status={self.status}>'
|
||||
|
||||
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
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
return (
|
||||
self.get_string_entity() == other.get_string_entity()
|
||||
and self.get_value() == other.get_value()
|
||||
)
|
||||
@override
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, InputFlag):
|
||||
return (
|
||||
self.name == other.name
|
||||
)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
+100
-144
@@ -1,35 +1,28 @@
|
||||
from argenta.command.flag.models import Flag, InputFlag
|
||||
from argenta.command.flag.models import Flag, InputFlag, ValidationStatus
|
||||
from argenta.command.flag.flags.models import InputFlags, Flags
|
||||
from argenta.command.exceptions import (
|
||||
UnprocessedInputFlagException,
|
||||
RepeatedInputFlagsException,
|
||||
EmptyInputCommandException,
|
||||
)
|
||||
from typing import cast, Literal
|
||||
from typing import Never, Self, cast, Literal
|
||||
|
||||
|
||||
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
|
||||
ParseFlagsResult = tuple[InputFlags, str | None, str | None]
|
||||
ParseResult = tuple[str, InputFlags]
|
||||
|
||||
def get_trigger(self) -> str:
|
||||
"""
|
||||
Public. Returns the trigger of the command
|
||||
:return: the trigger of the command as str
|
||||
"""
|
||||
return self._trigger
|
||||
MIN_FLAG_PREFIX: str = "-"
|
||||
DEFAULT_WITHOUT_FLAGS: Flags = Flags()
|
||||
|
||||
DEFAULT_WITHOUT_INPUT_FLAGS: InputFlags = InputFlags()
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
class Command:
|
||||
def __init__(
|
||||
self,
|
||||
trigger: str,
|
||||
trigger: str, *,
|
||||
description: str | None = None,
|
||||
flags: Flag | Flags | None = None,
|
||||
flags: Flag | Flags = DEFAULT_WITHOUT_FLAGS,
|
||||
aliases: list[str] | None = None,
|
||||
):
|
||||
"""
|
||||
@@ -39,157 +32,120 @@ class Command(BaseCommand):
|
||||
: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 = "Very useful 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
|
||||
self.registered_flags: Flags = flags if isinstance(flags, Flags) else Flags([flags])
|
||||
self.trigger: str = trigger
|
||||
self.description: str = description if description else "Command without description"
|
||||
self.aliases: list[str] = aliases if aliases else []
|
||||
|
||||
def validate_input_flag(
|
||||
self, flag: InputFlag
|
||||
) -> Literal["Undefined", "Valid", "Invalid"]:
|
||||
) -> ValidationStatus:
|
||||
"""
|
||||
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 "Valid"
|
||||
else:
|
||||
return "Invalid"
|
||||
registered_flags: Flags = self.registered_flags
|
||||
for registered_flag in registered_flags:
|
||||
if registered_flag.string_entity == flag.string_entity:
|
||||
is_valid = registered_flag.validate_input_flag_value(flag.input_value)
|
||||
if is_valid:
|
||||
return ValidationStatus.VALID
|
||||
else:
|
||||
return "Undefined"
|
||||
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 "Valid"
|
||||
else:
|
||||
return "Invalid"
|
||||
return "Undefined"
|
||||
return "Undefined"
|
||||
|
||||
def get_description(self) -> str:
|
||||
"""
|
||||
Private. Returns the description of the command
|
||||
:return: the description of the command as str
|
||||
"""
|
||||
return self._description
|
||||
return ValidationStatus.INVALID
|
||||
return ValidationStatus.UNDEFINED
|
||||
|
||||
|
||||
class InputCommand(BaseCommand):
|
||||
def __init__(self, trigger: str, input_flags: InputFlag | InputFlags | None = None):
|
||||
class InputCommand:
|
||||
def __init__(self, trigger: str, *,
|
||||
input_flags: InputFlag | InputFlags = DEFAULT_WITHOUT_INPUT_FLAGS):
|
||||
"""
|
||||
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()
|
||||
)
|
||||
self.trigger: str = trigger
|
||||
self.input_flags: InputFlags = input_flags if isinstance(input_flags, InputFlags) else InputFlags([input_flags])
|
||||
|
||||
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) -> "InputCommand":
|
||||
@classmethod
|
||||
def parse(cls, raw_command: str) -> Self:
|
||||
"""
|
||||
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:
|
||||
trigger, input_flags = CommandParser(raw_command).parse_raw_command()
|
||||
|
||||
return cls(trigger=trigger, input_flags=input_flags)
|
||||
|
||||
|
||||
class CommandParser:
|
||||
def __init__(self, raw_command: str) -> None:
|
||||
self.raw_command: str = raw_command
|
||||
self._parsed_input_flags: InputFlags = InputFlags()
|
||||
|
||||
def parse_raw_command(self) -> ParseResult:
|
||||
if not self.raw_command:
|
||||
raise EmptyInputCommandException()
|
||||
|
||||
list_of_tokens = raw_command.split()
|
||||
command = list_of_tokens.pop(0)
|
||||
input_flags, crnt_flag_name, crnt_flag_val = self._parse_flags(self.raw_command.split()[1:])
|
||||
|
||||
input_flags: InputFlags = InputFlags()
|
||||
current_flag_name, current_flag_value = None, None
|
||||
|
||||
for k, _ in enumerate(list_of_tokens):
|
||||
if _.startswith("-"):
|
||||
if len(_) < 2 or len(_[: _.rfind("-")]) > 3:
|
||||
raise UnprocessedInputFlagException()
|
||||
current_flag_name = _
|
||||
else:
|
||||
if not current_flag_name or current_flag_value:
|
||||
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]):
|
||||
if any([crnt_flag_name, crnt_flag_val]):
|
||||
raise UnprocessedInputFlagException()
|
||||
else:
|
||||
return InputCommand(trigger=command, input_flags=input_flags)
|
||||
return (self.raw_command.split()[0], input_flags)
|
||||
|
||||
def _parse_flags(self, _tokens: list[str] | list[Never]) -> ParseFlagsResult:
|
||||
crnt_flg_name, crnt_flg_val = None, None
|
||||
for index, token in enumerate(_tokens):
|
||||
crnt_flg_name, crnt_flg_val = _parse_single_token(token, crnt_flg_name, crnt_flg_val)
|
||||
|
||||
if not crnt_flg_name or self._is_next_token_value(index, _tokens):
|
||||
continue
|
||||
|
||||
input_flag = InputFlag(
|
||||
name=crnt_flg_name[crnt_flg_name.rfind(MIN_FLAG_PREFIX) + 1:],
|
||||
prefix=cast(
|
||||
Literal["-", "--", "---"],
|
||||
crnt_flg_name[:crnt_flg_name.rfind(MIN_FLAG_PREFIX) + 1],
|
||||
),
|
||||
input_value=crnt_flg_val,
|
||||
status=None
|
||||
)
|
||||
|
||||
if input_flag in self._parsed_input_flags:
|
||||
raise RepeatedInputFlagsException(input_flag)
|
||||
|
||||
self._parsed_input_flags.add_flag(input_flag)
|
||||
crnt_flg_name, crnt_flg_val = None, None
|
||||
|
||||
return (self._parsed_input_flags, crnt_flg_name, crnt_flg_val)
|
||||
|
||||
def _is_next_token_value(self, current_index: int,
|
||||
_tokens: list[str] | list[Never]) -> bool:
|
||||
next_index = current_index + 1
|
||||
if next_index >= len(_tokens):
|
||||
return False
|
||||
|
||||
next_token = _tokens[next_index]
|
||||
return not next_token.startswith(MIN_FLAG_PREFIX)
|
||||
|
||||
def _parse_single_token(
|
||||
token: str,
|
||||
crnt_flag_name: str | None,
|
||||
crnt_flag_val: str | None
|
||||
) -> tuple[str | None, str | None]:
|
||||
if not token.startswith(MIN_FLAG_PREFIX):
|
||||
if not crnt_flag_name or crnt_flag_val:
|
||||
raise UnprocessedInputFlagException
|
||||
return crnt_flag_name, token
|
||||
|
||||
prefix = token[:token.rfind(MIN_FLAG_PREFIX)]
|
||||
if len(token) < 2 or len(prefix) > 2:
|
||||
raise UnprocessedInputFlagException
|
||||
|
||||
new_flag_name = token
|
||||
new_flag_value = None
|
||||
|
||||
return new_flag_name, new_flag_value
|
||||
|
||||
Reference in New Issue
Block a user