mirror of
https://github.com/koloideal/Argenta.git
synced 2026-06-10 10:05:28 +03:00
feat: impl docs (#4)
The entire public api is covered with documentation in two languages - Russian and English. the library now supports the latest three versions of python - 3.12, 3.13 and 3.14 minor design changes: now, when a Boolean flag is entered, its value is an empty string, not None. tests have been adapted to the supported versions of python, readmi has been redesigned in two languages, German is no longer available.
This commit is contained in:
@@ -1,12 +1,8 @@
|
||||
__all__ = [
|
||||
"Command",
|
||||
"PossibleValues",
|
||||
"PredefinedFlags",
|
||||
"InputCommand",
|
||||
"Flags",
|
||||
"Flag"
|
||||
]
|
||||
|
||||
from argenta.command.models import Command, InputCommand
|
||||
from argenta.command.flag import defaults as PredefinedFlags
|
||||
from argenta.command.flag import (Flag, Flags, PossibleValues)
|
||||
from argenta.command.flag import Flag as Flag
|
||||
from argenta.command.flag import Flags as Flags
|
||||
from argenta.command.flag import InputFlag as InputFlag
|
||||
from argenta.command.flag import InputFlags as InputFlags
|
||||
from argenta.command.flag import PossibleValues as PossibleValues
|
||||
from argenta.command.flag.defaults import PredefinedFlags as PredefinedFlags
|
||||
from argenta.command.models import Command as Command
|
||||
from argenta.command.models import InputCommand as InputCommand
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
from argenta.command.flag.models import Flag, InputFlag
|
||||
__all__ = [
|
||||
"InputCommandException",
|
||||
"UnprocessedInputFlagException",
|
||||
"RepeatedInputFlagsException",
|
||||
"EmptyInputCommandException",
|
||||
]
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import override
|
||||
|
||||
from argenta.command.flag.models import Flag, InputFlag
|
||||
|
||||
|
||||
class InputCommandException(ABC, Exception):
|
||||
"""
|
||||
Private. Base exception class for all exceptions raised when parse input command
|
||||
"""
|
||||
|
||||
@override
|
||||
@abstractmethod
|
||||
def __str__(self) -> str:
|
||||
@@ -17,6 +26,7 @@ class UnprocessedInputFlagException(InputCommandException):
|
||||
"""
|
||||
Private. Raised when an unprocessed input flag is detected
|
||||
"""
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return "Unprocessed Input Flags"
|
||||
@@ -34,16 +44,14 @@ class RepeatedInputFlagsException(InputCommandException):
|
||||
@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: '{string_entity}'"
|
||||
)
|
||||
return f"Repeated Input Flags\nDuplicate flag was detected in the input: '{string_entity}'"
|
||||
|
||||
|
||||
class EmptyInputCommandException(InputCommandException):
|
||||
"""
|
||||
Private. Raised when an empty input command is detected
|
||||
"""
|
||||
|
||||
@override
|
||||
def __str__(self) -> str:
|
||||
return "Input Command is empty"
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
__all__ = [
|
||||
"Flag",
|
||||
"InputFlag",
|
||||
"Flags",
|
||||
"PossibleValues",
|
||||
"ValidationStatus"
|
||||
]
|
||||
|
||||
|
||||
from argenta.command.flag.models import Flag, InputFlag, PossibleValues, ValidationStatus
|
||||
from argenta.command.flag.flags.models import Flags
|
||||
from argenta.command.flag.flags.models import Flags as Flags
|
||||
from argenta.command.flag.flags.models import InputFlags as InputFlags
|
||||
from argenta.command.flag.models import Flag as Flag
|
||||
from argenta.command.flag.models import InputFlag as InputFlag
|
||||
from argenta.command.flag.models import PossibleValues as PossibleValues
|
||||
from argenta.command.flag.models import ValidationStatus as ValidationStatus
|
||||
|
||||
@@ -1,27 +1,29 @@
|
||||
from typing import Literal
|
||||
from argenta.command.flag.models import Flag, PossibleValues
|
||||
import re
|
||||
__all__ = ["PredefinedFlags"]
|
||||
|
||||
import re
|
||||
from typing import Literal
|
||||
|
||||
from argenta.command.flag.models import Flag, PossibleValues
|
||||
|
||||
DEFAULT_PREFIX: Literal["-", "--", "---"] = "-"
|
||||
|
||||
HELP = Flag(name="help", possible_values=PossibleValues.NEITHER)
|
||||
SHORT_HELP = Flag(name="H", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
|
||||
INFO = Flag(name="info", possible_values=PossibleValues.NEITHER) # noqa: WPS110
|
||||
SHORT_INFO = Flag(name="I", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
class PredefinedFlags:
|
||||
HELP = Flag(name="help", possible_values=PossibleValues.NEITHER)
|
||||
SHORT_HELP = Flag(name="H", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
|
||||
ALL = Flag(name="all", possible_values=PossibleValues.NEITHER)
|
||||
SHORT_ALL = Flag(name="A", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
INFO = Flag(name="info", possible_values=PossibleValues.NEITHER) # noqa: WPS110
|
||||
SHORT_INFO = Flag(name="I", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
|
||||
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}$"),
|
||||
)
|
||||
ALL = Flag(name="all", possible_values=PossibleValues.NEITHER)
|
||||
SHORT_ALL = Flag(name="A", prefix=DEFAULT_PREFIX, possible_values=PossibleValues.NEITHER)
|
||||
|
||||
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}$"))
|
||||
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}$"),
|
||||
)
|
||||
|
||||
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,10 +1,2 @@
|
||||
__all__ = [
|
||||
"Flags",
|
||||
"InputFlags"
|
||||
]
|
||||
|
||||
|
||||
from argenta.command.flag.flags.models import (
|
||||
Flags,
|
||||
InputFlags
|
||||
)
|
||||
from argenta.command.flag.flags.models import Flags as Flags
|
||||
from argenta.command.flag.flags.models import InputFlags as InputFlags
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
from argenta.command.flag.models import InputFlag, Flag
|
||||
from typing import Generic, TypeVar, override
|
||||
from collections.abc import Iterator
|
||||
__all__ = ["Flags", "InputFlags"]
|
||||
|
||||
from collections.abc import Iterator
|
||||
from typing import Generic, TypeVar, override
|
||||
|
||||
from argenta.command.flag.models import Flag, InputFlag
|
||||
|
||||
FlagType = TypeVar("FlagType")
|
||||
|
||||
@@ -30,6 +32,9 @@ class BaseFlags(Generic[FlagType]):
|
||||
:return: None
|
||||
"""
|
||||
self.flags.extend(flags)
|
||||
|
||||
def __len__(self) -> int:
|
||||
return len(self.flags)
|
||||
|
||||
def __iter__(self) -> Iterator[FlagType]:
|
||||
return iter(self.flags)
|
||||
@@ -52,7 +57,7 @@ class Flags(BaseFlags[Flag]):
|
||||
: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, Flags):
|
||||
@@ -82,9 +87,9 @@ class InputFlags(BaseFlags[InputFlag]):
|
||||
: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:
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if not isinstance(other, InputFlags):
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -103,4 +108,3 @@ class InputFlags(BaseFlags[InputFlag]):
|
||||
return False
|
||||
else:
|
||||
raise TypeError
|
||||
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
__all__ = ["PossibleValues", "ValidationStatus", "Flag", "InputFlag"]
|
||||
|
||||
from enum import Enum
|
||||
from re import Pattern
|
||||
from typing import Literal, override
|
||||
|
||||
|
||||
PREFIX_TYPE = Literal["-", "--", "---"]
|
||||
|
||||
|
||||
class PossibleValues(Enum):
|
||||
NEITHER = 'NEITHER'
|
||||
ALL = 'ALL'
|
||||
NEITHER = "NEITHER"
|
||||
ALL = "ALL"
|
||||
|
||||
|
||||
class ValidationStatus(Enum):
|
||||
VALID = 'VALID'
|
||||
INVALID = 'INVALID'
|
||||
UNDEFINED = 'UNDEFINED'
|
||||
VALID = "VALID"
|
||||
INVALID = "INVALID"
|
||||
UNDEFINED = "UNDEFINED"
|
||||
|
||||
|
||||
class Flag:
|
||||
def __init__(
|
||||
self, name: str, *,
|
||||
prefix: Literal["-", "--", "---"] = "--",
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
prefix: PREFIX_TYPE = "--",
|
||||
possible_values: list[str] | Pattern[str] | PossibleValues = PossibleValues.ALL,
|
||||
) -> None:
|
||||
"""
|
||||
@@ -28,26 +35,29 @@ class Flag:
|
||||
:return: None
|
||||
"""
|
||||
self.name: str = name
|
||||
self.prefix: Literal["-", "--", "---"] = prefix
|
||||
self.prefix: PREFIX_TYPE = prefix
|
||||
self.possible_values: list[str] | Pattern[str] | PossibleValues = possible_values
|
||||
|
||||
def validate_input_flag_value(self, input_flag_value: str | None) -> bool:
|
||||
def validate_input_flag_value(self, input_flag_value: str) -> 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.NEITHER:
|
||||
return input_flag_value is None
|
||||
return input_flag_value == ''
|
||||
|
||||
if self.possible_values == PossibleValues.ALL:
|
||||
return input_flag_value != ''
|
||||
|
||||
if isinstance(self.possible_values, Pattern):
|
||||
return isinstance(input_flag_value, str) and bool(self.possible_values.match(input_flag_value))
|
||||
return bool(self.possible_values.match(input_flag_value))
|
||||
|
||||
if isinstance(self.possible_values, list):
|
||||
return input_flag_value in self.possible_values
|
||||
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@property
|
||||
def string_entity(self) -> str:
|
||||
"""
|
||||
@@ -56,17 +66,17 @@ class Flag:
|
||||
"""
|
||||
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}>'
|
||||
|
||||
return f"Flag<name={self.name}, prefix={self.prefix}>"
|
||||
|
||||
@override
|
||||
def __eq__(self, other: object) -> bool:
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, Flag):
|
||||
return self.string_entity == other.string_entity
|
||||
else:
|
||||
@@ -75,10 +85,12 @@ class Flag:
|
||||
|
||||
class InputFlag:
|
||||
def __init__(
|
||||
self, name: str, *,
|
||||
prefix: Literal['-', '--', '---'] = '--',
|
||||
input_value: str | None,
|
||||
status: ValidationStatus | None
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
prefix: PREFIX_TYPE = "--",
|
||||
input_value: str,
|
||||
status: ValidationStatus | None,
|
||||
):
|
||||
"""
|
||||
Public. The entity of the flag of the entered command
|
||||
@@ -88,10 +100,10 @@ class InputFlag:
|
||||
:return: None
|
||||
"""
|
||||
self.name: str = name
|
||||
self.prefix: Literal['-', '--', '---'] = prefix
|
||||
self.input_value: str | None = input_value
|
||||
self.prefix: PREFIX_TYPE = prefix
|
||||
self.input_value: str = input_value
|
||||
self.status: ValidationStatus | None = status
|
||||
|
||||
|
||||
@property
|
||||
def string_entity(self) -> str:
|
||||
"""
|
||||
@@ -103,17 +115,15 @@ class InputFlag:
|
||||
|
||||
@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}>'
|
||||
return f"{self.string_entity} {self.input_value}"
|
||||
|
||||
@override
|
||||
def __eq__(self, other: object) -> bool:
|
||||
def __repr__(self) -> str:
|
||||
return f"InputFlag<name={self.name}, prefix={self.prefix}, value={self.input_value}, status={self.status}>"
|
||||
|
||||
@override
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, InputFlag):
|
||||
return (
|
||||
self.name == other.name
|
||||
)
|
||||
return self.name == other.name
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
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,
|
||||
)
|
||||
__all__ = ["Command", "InputCommand"]
|
||||
|
||||
import shlex
|
||||
from typing import Never, Self, cast, Literal
|
||||
|
||||
from argenta.command.exceptions import (
|
||||
EmptyInputCommandException,
|
||||
RepeatedInputFlagsException,
|
||||
UnprocessedInputFlagException,
|
||||
)
|
||||
from argenta.command.flag.flags.models import Flags, InputFlags
|
||||
from argenta.command.flag.models import Flag, InputFlag, ValidationStatus
|
||||
|
||||
ParseFlagsResult = tuple[InputFlags, str | None, str | None]
|
||||
ParseResult = tuple[str, InputFlags]
|
||||
|
||||
MIN_FLAG_PREFIX: str = "-"
|
||||
PREFIX_TYPE = Literal["-", "--", "---"]
|
||||
DEFAULT_WITHOUT_FLAGS: Flags = Flags()
|
||||
DEFAULT_WITHOUT_ALIASES: list[Never] = []
|
||||
|
||||
DEFAULT_WITHOUT_INPUT_FLAGS: InputFlags = InputFlags()
|
||||
|
||||
@@ -20,10 +25,11 @@ DEFAULT_WITHOUT_INPUT_FLAGS: InputFlags = InputFlags()
|
||||
class Command:
|
||||
def __init__(
|
||||
self,
|
||||
trigger: str, *,
|
||||
description: str | None = None,
|
||||
trigger: str,
|
||||
*,
|
||||
description: str = "Some useful command",
|
||||
flags: Flag | Flags = DEFAULT_WITHOUT_FLAGS,
|
||||
aliases: list[str] | None = None,
|
||||
aliases: list[str] | list[Never] = DEFAULT_WITHOUT_ALIASES,
|
||||
):
|
||||
"""
|
||||
Public. The command that can and should be registered in the Router
|
||||
@@ -34,12 +40,10 @@ class Command:
|
||||
"""
|
||||
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 []
|
||||
self.description: str = description
|
||||
self.aliases: list[str] | list[Never] = aliases
|
||||
|
||||
def validate_input_flag(
|
||||
self, flag: InputFlag
|
||||
) -> ValidationStatus:
|
||||
def validate_input_flag(self, flag: InputFlag) -> ValidationStatus:
|
||||
"""
|
||||
Private. Validates the input flag
|
||||
:param flag: input flag for validation
|
||||
@@ -57,8 +61,7 @@ class Command:
|
||||
|
||||
|
||||
class InputCommand:
|
||||
def __init__(self, trigger: str, *,
|
||||
input_flags: InputFlag | InputFlags = DEFAULT_WITHOUT_INPUT_FLAGS):
|
||||
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
|
||||
@@ -66,7 +69,9 @@ class InputCommand:
|
||||
:return: None
|
||||
"""
|
||||
self.trigger: str = trigger
|
||||
self.input_flags: InputFlags = input_flags if isinstance(input_flags, InputFlags) else InputFlags([input_flags])
|
||||
self.input_flags: InputFlags = (
|
||||
input_flags if isinstance(input_flags, InputFlags) else InputFlags([input_flags])
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def parse(cls, raw_command: str) -> Self:
|
||||
@@ -75,77 +80,50 @@ class InputCommand:
|
||||
:param raw_command: raw input command
|
||||
:return: model of the input command, after parsing as InputCommand
|
||||
"""
|
||||
trigger, input_flags = CommandParser(raw_command).parse_raw_command()
|
||||
|
||||
return cls(trigger=trigger, input_flags=input_flags)
|
||||
tokens = shlex.split(raw_command)
|
||||
|
||||
|
||||
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()
|
||||
|
||||
input_flags, crnt_flag_name, crnt_flag_val = self._parse_flags(self.raw_command.split()[1:])
|
||||
|
||||
if any([crnt_flag_name, crnt_flag_val]):
|
||||
raise UnprocessedInputFlagException()
|
||||
else:
|
||||
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
|
||||
|
||||
if not tokens:
|
||||
raise EmptyInputCommandException
|
||||
|
||||
command = tokens[0]
|
||||
flags: InputFlags = InputFlags()
|
||||
|
||||
i = 1
|
||||
while i < len(tokens):
|
||||
token = tokens[i]
|
||||
|
||||
if token.startswith("---"):
|
||||
prefix = "---"
|
||||
name = token[3:]
|
||||
elif token.startswith("--"):
|
||||
prefix = "--"
|
||||
name = token[2:]
|
||||
elif token.startswith("-"):
|
||||
prefix = "-"
|
||||
name = token[1:]
|
||||
else:
|
||||
raise UnprocessedInputFlagException
|
||||
|
||||
if not name:
|
||||
raise UnprocessedInputFlagException
|
||||
|
||||
if i + 1 < len(tokens) and not tokens[i + 1].startswith("-"):
|
||||
input_value = tokens[i + 1]
|
||||
i += 2
|
||||
else:
|
||||
input_value = ""
|
||||
i += 1
|
||||
|
||||
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,
|
||||
name=name,
|
||||
prefix=cast(PREFIX_TYPE, prefix), # pyright: ignore[reportUnnecessaryCast]
|
||||
input_value=input_value,
|
||||
status=None
|
||||
)
|
||||
|
||||
if input_flag in self._parsed_input_flags:
|
||||
|
||||
if input_flag in 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
|
||||
flags.add_flag(input_flag)
|
||||
|
||||
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
|
||||
return cls(command, input_flags=flags)
|
||||
|
||||
Reference in New Issue
Block a user