25 Commits

Author SHA1 Message Date
kolo 8fbf651223 v0.3.5 2025-03-03 14:26:37 +03:00
kolo 459c16ec87 v0.3.5 2025-03-03 14:26:13 +03:00
kolo d9c74310c3 start make tests 2025-03-03 00:10:07 +03:00
kolo 5e6cdc342e v0.3.4 2025-03-02 00:54:34 +03:00
kolo a378163431 v0.3.3 2025-03-01 16:44:33 +03:00
kolo fd4f2e1570 v0.3.2 2025-03-01 00:25:01 +03:00
kolo 3f7c577c29 0.3.1 2025-02-28 16:19:22 +03:00
kolo b72fcc6a11 fix 2025-02-27 01:16:39 +03:00
kolo 158e5eb75a fix 2025-02-25 00:37:11 +03:00
kolo 46cb2f70c5 fix 2025-02-24 19:04:20 +03:00
kolo ef242a5732 fix 2025-02-23 18:53:11 +03:00
kolo f87f102ced v0.3.0 2025-02-23 13:26:12 +03:00
kolo ddf2a2fb10 v0.3.0 2025-02-23 13:25:51 +03:00
kolo eac5358ead work on v0.3.0 2025-02-22 12:14:25 +03:00
kolo 76c18ddbff v0.3.0-alpha 2025-02-21 18:05:40 +03:00
kolo 4eeb4eb182 work on v0.3.0 2025-02-21 17:50:53 +03:00
kolo 905698384a work on v0.3.0 2025-02-21 01:46:03 +03:00
kolo 79ccfbb3b1 work on v0.3.0 2025-02-20 22:10:29 +03:00
kolo a63c46a78b work on v0.3.0 2025-02-19 23:49:38 +03:00
kolo a3a7cbf2e6 work on v0.3.0 2025-02-18 23:35:36 +03:00
kolo a9e545f3d8 work on v0.3.0 2025-02-17 20:57:15 +03:00
kolo 37b62fd69b fix 2025-02-17 00:33:05 +03:00
kolo 0ae86d0b2b work on v0.3.0 2025-02-15 12:05:42 +03:00
kolo ebfd5a80b3 work on v0.3.0 2025-02-13 23:26:01 +03:00
kolo 250704fc88 some fix 2025-02-13 19:31:30 +03:00
43 changed files with 700 additions and 716 deletions
+2 -11
View File
@@ -1,15 +1,6 @@
.venv .venv
.idea .idea
argenta/router/__pycache__/
argenta/app/__pycache__/
argenta/__pycache__/
dist dist
poetry.lock poetry.lock
tests/__pycache__ *__pycache__/
tests/mock_default_app/handlers/__pycache__/
tests/mock_default_app/__pycache__/
tests/mock_app/local_data_func/__pycache__/
tests/mock_app/handlers/handlers_implementation/__pycache__/
tests/mock_app/handlers/__pycache__/
tests/mock_app/business_logic/__pycache__/
tests/mock_app/__pycache__/
+2 -6
View File
@@ -1,7 +1,3 @@
from .entity import App from .entity import App
from .exceptions import (HandlerForUnknownCommandsOnNonMainRouterException, from .exceptions import (InvalidDescriptionMessagePatternException,
InvalidDescriptionMessagePatternException, InvalidRouterInstanceException)
InvalidRouterInstanceException,
OnlyOneMainRouterIsAllowedException,
MissingMainRouterException,
MissingHandlerForUnknownCommandsException)
+88 -61
View File
@@ -1,14 +1,18 @@
from typing import Callable from typing import Callable
from inspect import getfullargspec
import re
from ..command.entity import Command
from ..router.entity import Router from ..router.entity import Router
from ..command.exceptions import (UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException)
from .exceptions import (InvalidRouterInstanceException, from .exceptions import (InvalidRouterInstanceException,
InvalidDescriptionMessagePatternException, InvalidDescriptionMessagePatternException,
OnlyOneMainRouterIsAllowedException,
MissingMainRouterException,
MissingHandlerForUnknownCommandsException,
HandlerForUnknownCommandsOnNonMainRouterException,
NoRegisteredRoutersException, NoRegisteredRoutersException,
NoRegisteredHandlersException, NoRegisteredHandlersException,
RepeatedCommandInDifferentRoutersException) RepeatedCommandInDifferentRoutersException,
IncorrectNumberOfHandlerArgsException)
class App: class App:
@@ -16,6 +20,7 @@ class App:
prompt: str = 'Enter a command', prompt: str = 'Enter a command',
initial_message: str = '\nHello, I am Argenta\n', initial_message: str = '\nHello, I am Argenta\n',
farewell_message: str = '\nGoodBye\n', farewell_message: str = '\nGoodBye\n',
invalid_input_flags_message: str = 'Invalid input flags',
exit_command: str = 'Q', exit_command: str = 'Q',
exit_command_description: str = 'Exit command', exit_command_description: str = 'Exit command',
exit_command_title: str = 'System points:', exit_command_title: str = 'System points:',
@@ -33,21 +38,24 @@ class App:
self.ignore_exit_command_register = ignore_exit_command_register self.ignore_exit_command_register = ignore_exit_command_register
self.farewell_message = farewell_message self.farewell_message = farewell_message
self.initial_message = initial_message self.initial_message = initial_message
self.invalid_input_flags_message = invalid_input_flags_message
self.line_separate = line_separate self.line_separate = line_separate
self.command_group_description_separate = command_group_description_separate self.command_group_description_separate = command_group_description_separate
self.ignore_command_register = ignore_command_register self.ignore_command_register = ignore_command_register
self.repeat_command_groups = repeat_command_groups self.repeat_command_groups = repeat_command_groups
self._routers: list[Router] = [] self._routers: list[Router] = []
self._registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = []
self._app_main_router: Router | None = None
self._description_message_pattern: str = '[{command}] *=*=* {description}' self._description_message_pattern: str = '[{command}] *=*=* {description}'
self._registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | Command]] | Router]] = []
self._invalid_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'Incorrect flag syntax: "{raw_command}"')
self._repeated_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'Repeated input flags: "{raw_command}"')
self._empty_input_command_handler: Callable[[], None] = lambda: print_func(f'Empty input command')
self._unknown_command_handler: Callable[[Command], None] = lambda command: print_func(f"Unknown command: {command.get_string_entity()}")
def start_polling(self) -> None: def start_polling(self) -> None:
self._validate_number_of_routers() self._validate_number_of_routers()
self._validate_included_routers() self._validate_included_routers()
self._validate_main_router()
self._validate_all_router_commands() self._validate_all_router_commands()
self.print_func(self.initial_message) self.print_func(self.initial_message)
@@ -61,19 +69,51 @@ class App:
self._print_command_group_description() self._print_command_group_description()
self.print_func(self.prompt) self.print_func(self.prompt)
command: str = input() raw_command: str = input()
self._checking_command_for_exit_command(command) try:
input_command: Command = Command.parse_input_command(raw_command=raw_command)
except UnprocessedInputFlagException:
self.print_func(self.line_separate)
self._invalid_input_flags_handler(raw_command)
self.print_func(self.line_separate) self.print_func(self.line_separate)
is_unknown_command: bool = self._check_is_command_unknown(command) if not self.repeat_command_groups:
self.print_func(self.prompt)
continue
except RepeatedInputFlagsException:
self.print_func(self.line_separate)
self._repeated_input_flags_handler(raw_command)
self.print_func(self.line_separate)
if not self.repeat_command_groups:
self.print_func(self.prompt)
continue
except EmptyInputCommandException:
self.print_func(self.line_separate)
self._empty_input_command_handler()
self.print_func(self.line_separate)
if not self.repeat_command_groups:
self.print_func(self.prompt)
continue
self._check_command_for_exit_command(input_command.get_string_entity())
self.print_func(self.line_separate)
is_unknown_command: bool = self._check_is_command_unknown(input_command)
if is_unknown_command: if is_unknown_command:
self.print_func(self.line_separate)
self.print_func(self.command_group_description_separate)
if not self.repeat_command_groups: if not self.repeat_command_groups:
self.print_func(self.prompt) self.print_func(self.prompt)
continue continue
for router in self._routers: for router in self._routers:
router.input_command_handler(command) router.input_command_handler(input_command)
self.print_func(self.line_separate) self.print_func(self.line_separate)
self.print_func(self.command_group_description_separate) self.print_func(self.command_group_description_separate)
@@ -90,41 +130,47 @@ class App:
def set_description_message_pattern(self, pattern: str) -> None: def set_description_message_pattern(self, pattern: str) -> None:
try: first_check = re.match(r'.*{command}.*', pattern)
pattern.format(command='command', second_check = re.match(r'.*{description}.*', pattern)
description='description')
except KeyError: if bool(first_check) and bool(second_check):
raise InvalidDescriptionMessagePatternException(pattern)
self._description_message_pattern: str = pattern self._description_message_pattern: str = pattern
else:
raise InvalidDescriptionMessagePatternException(pattern)
def get_main_router(self) -> Router: def set_invalid_input_flags_handler(self, handler: Callable[[str], None]) -> None:
return self._app_main_router args = getfullargspec(handler).args
if len(args) != 1:
raise IncorrectNumberOfHandlerArgsException()
else:
self._invalid_input_flags_handler = handler
def get_all_app_commands(self) -> list[str]: def set_repeated_input_flags_handler(self, handler: Callable[[str], None]) -> None:
all_commands: list[str] = [] args = getfullargspec(handler).args
for router in self._routers: if len(args) != 1:
all_commands.extend(router.get_all_commands()) raise IncorrectNumberOfHandlerArgsException()
else:
return all_commands self._repeated_input_flags_handler = handler
def include_router(self, router: Router, is_main: True | False = False) -> None: def set_unknown_command_handler(self, handler: Callable[[str], None]) -> None:
args = getfullargspec(handler).args
if len(args) != 1:
raise IncorrectNumberOfHandlerArgsException()
else:
self._unknown_command_handler = handler
def include_router(self, router: Router) -> None:
if not isinstance(router, Router): if not isinstance(router, Router):
raise InvalidRouterInstanceException() raise InvalidRouterInstanceException()
if is_main:
if not self._app_main_router:
self._app_main_router = router
router.set_router_as_main()
else:
raise OnlyOneMainRouterIsAllowedException(self._app_main_router.get_name())
router.set_ignore_command_register(self.ignore_command_register) router.set_ignore_command_register(self.ignore_command_register)
self._routers.append(router) self._routers.append(router)
command_entities: list[dict[str, Callable[[], None] | str]] = router.get_command_entities() command_entities: list[dict[str, Callable[[], None] | Command]] = router.get_command_entities()
self._registered_router_entities.append({'name': router.get_name(), self._registered_router_entities.append({'name': router.get_name(),
'title': router.get_title(), 'title': router.get_title(),
'entity': router, 'entity': router,
@@ -142,23 +188,6 @@ class App:
raise NoRegisteredHandlersException(router.get_name()) raise NoRegisteredHandlersException(router.get_name())
def _validate_main_router(self):
if not self._app_main_router:
if len(self._routers) > 1:
raise MissingMainRouterException()
else:
router = self._routers[0]
router.set_router_as_main()
self._app_main_router = router
if not self._app_main_router.unknown_command_func:
raise MissingHandlerForUnknownCommandsException()
for router in self._routers:
if router.unknown_command_func and self._app_main_router is not router:
raise HandlerForUnknownCommandsOnNonMainRouterException()
def _validate_all_router_commands(self) -> None: def _validate_all_router_commands(self) -> None:
for idx in range(len(self._registered_router_entities)): for idx in range(len(self._registered_router_entities)):
current_router: Router = self._registered_router_entities[idx]['entity'] current_router: Router = self._registered_router_entities[idx]['entity']
@@ -175,7 +204,7 @@ class App:
raise RepeatedCommandInDifferentRoutersException() raise RepeatedCommandInDifferentRoutersException()
def _checking_command_for_exit_command(self, command: str): def _check_command_for_exit_command(self, command: str):
if command.lower() == self.exit_command.lower(): if command.lower() == self.exit_command.lower():
if self.ignore_exit_command_register: if self.ignore_exit_command_register:
self.print_func(self.farewell_message) self.print_func(self.farewell_message)
@@ -186,19 +215,17 @@ class App:
exit(0) exit(0)
def _check_is_command_unknown(self, command: str): def _check_is_command_unknown(self, command: Command):
registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = self._registered_router_entities registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | Command]] | Router]] = self._registered_router_entities
for router_entity in registered_router_entities: for router_entity in registered_router_entities:
for command_entity in router_entity['commands']: for command_entity in router_entity['commands']:
if command_entity['command'].lower() == command.lower(): if command_entity['command'].get_string_entity().lower() == command.get_string_entity().lower():
if self.ignore_command_register: if self.ignore_command_register:
return False return False
else: else:
if command_entity['command'] == command: if command_entity['command'].get_string_entity() == command.get_string_entity():
return False return False
self._app_main_router.unknown_command_handler(command) self._unknown_command_handler(command)
self.print_func(self.line_separate)
self.print_func(self.command_group_description_separate)
return True return True
@@ -207,8 +234,8 @@ class App:
self.print_func(router_entity['title']) self.print_func(router_entity['title'])
for command_entity in router_entity['commands']: for command_entity in router_entity['commands']:
self.print_func(self._description_message_pattern.format( self.print_func(self._description_message_pattern.format(
command=command_entity['command'], command=command_entity['command'].get_string_entity(),
description=command_entity['description'] description=command_entity['command'].get_description()
) )
) )
self.print_func(self.command_group_description_separate) self.print_func(self.command_group_description_separate)
+5 -26
View File
@@ -13,32 +13,6 @@ class InvalidDescriptionMessagePatternException(Exception):
f"Your pattern: {self.pattern}") f"Your pattern: {self.pattern}")
class OnlyOneMainRouterIsAllowedException(Exception):
def __init__(self, existing_main_router):
self.existing_main_router = existing_main_router
def __str__(self):
return ("Only One Main Router Allowed\n"
f"Existing main router is: {self.existing_main_router}")
class MissingMainRouterException(Exception):
def __str__(self):
return ("Missing Main Router\n"
"One of the registered routers must be the main one")
class MissingHandlerForUnknownCommandsException(Exception):
def __str__(self):
return ("Missing Handlers For Unknown Commands On The Main Router\n"
"The main router must have a declared handler for unknown commands")
class HandlerForUnknownCommandsOnNonMainRouterException(Exception):
def __str__(self):
return '\nThe handler for unknown commands can only be declared for the main router'
class NoRegisteredRoutersException(Exception): class NoRegisteredRoutersException(Exception):
def __str__(self): def __str__(self):
return "No Registered Router Found" return "No Registered Router Found"
@@ -54,3 +28,8 @@ class NoRegisteredHandlersException(Exception):
class RepeatedCommandInDifferentRoutersException(Exception): class RepeatedCommandInDifferentRoutersException(Exception):
def __str__(self): def __str__(self):
return "Commands in different handlers cannot be repeated" return "Commands in different handlers cannot be repeated"
class IncorrectNumberOfHandlerArgsException(Exception):
def __str__(self):
return "Incorrect Input Flags Handler has incorrect number of arguments"
+1
View File
@@ -0,0 +1 @@
from .entity import Command
+121
View File
@@ -0,0 +1,121 @@
from .params.flag.entity import Flag
from .params.flag.flags_group.entity import FlagsGroup
from .exceptions import (InvalidCommandInstanceException,
InvalidDescriptionInstanceException,
InvalidFlagsInstanceException,
UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException)
from typing import Generic, TypeVar
T = TypeVar('T')
class Command(Generic[T]):
def __init__(self, command: str,
description: str = None,
flags: Flag | FlagsGroup | None = None):
self._command = command
self._description = f'description for "{self._command}" 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_string_entity(self):
return self._command
def get_description(self):
return self._description
def get_registered_flags(self):
return self._registered_flags
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._registered_flags, FlagsGroup)), not self._registered_flags]):
raise InvalidFlagsInstanceException
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():
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 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]':
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()
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
+34
View File
@@ -0,0 +1,34 @@
from .params.flag.entity import Flag
class InvalidCommandInstanceException(Exception):
def __str__(self):
return "Invalid Command Instance"
class InvalidDescriptionInstanceException(Exception):
def __str__(self):
return "Invalid Description Instance"
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 EmptyInputCommandException(Exception):
def __str__(self):
return "Input Command is empty"
+2
View File
@@ -0,0 +1,2 @@
from .entity import Flag
from .flags_group.entity import FlagsGroup
+51
View File
@@ -0,0 +1,51 @@
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] | Pattern[str] = False):
self._flag_name = flag_name
self._flag_prefix = flag_prefix
self.possible_flag_values = possible_flag_values
self.ignore_flag_value_register = ignore_flag_value_register
self._value = None
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 get_value(self):
return self._value
def set_value(self, value):
self._value = value
def validate_input_flag_value(self, input_flag_value: str):
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
else:
return False
else:
if input_flag_value in self.possible_flag_values:
return True
else:
return False
return True
@@ -0,0 +1,24 @@
from argenta.command.params.flag.entity import Flag
class FlagsGroup:
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):
self._flags.append(flag)
def add_flags(self, flags: list[Flag]):
self._flags.extend(flags)
def __iter__(self):
return iter(self._flags)
def __next__(self):
return next(iter(self))
def __getitem__(self, item):
return self._flags[item]
+1 -3
View File
@@ -1,4 +1,2 @@
from .entity import Router from .entity import Router
from .exceptions import (UnknownCommandHandlerHasAlreadyBeenCreatedException, from .exceptions import InvalidDescriptionInstanceException
InvalidDescriptionInstanceException,
InvalidCommandInstanceException)
+68 -70
View File
@@ -1,8 +1,13 @@
from typing import Callable, Any from typing import Callable, Any
from ..router.exceptions import (InvalidCommandInstanceException, from inspect import getfullargspec
UnknownCommandHandlerHasAlreadyBeenCreatedException,
InvalidDescriptionInstanceException, from ..command.entity import Command
RepeatedCommandException) from ..command.params.flag.entity import Flag
from ..command.params.flag.flags_group.entity import FlagsGroup
from ..router.exceptions import (RepeatedCommandException, RepeatedFlagNameException,
TooManyTransferredArgsException,
RequiredArgumentNotPassedException,
IncorrectNumberOfHandlerArgsException)
class Router: class Router:
@@ -13,83 +18,90 @@ class Router:
self.title = title self.title = title
self.name = name self.name = name
self._command_entities: list[dict[str, Callable[[], None] | str]] = [] self._command_entities: list[dict[str, Callable[[], None] | Command]] = []
self.unknown_command_func: Callable[[str], None] | None = None self._ignore_command_register: bool = False
self._is_main_router: bool = False
self.ignore_command_register: bool = False self._not_valid_flag_handler: Callable[[Flag], None] = lambda flag: print(f"Undefined or incorrect input flag: '{flag.get_string_entity()} {flag.get_value()}'")
def command(self, command: str, description: str = None) -> Callable[[Any], Any]: def command(self, command: Command) -> Callable[[Any], Any]:
processed_description = Router._validate_description(command, description) command.validate_commands_params()
self._validate_command(command) self._validate_command(command)
def command_decorator(func): def command_decorator(func):
Router._validate_func_args(command, func)
self._command_entities.append({'handler_func': func, self._command_entities.append({'handler_func': func,
'command': command, 'command': command})
'description': processed_description})
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return func(*args, **kwargs) return func(*args, **kwargs)
return wrapper return wrapper
return command_decorator return command_decorator
def set_invalid_input_flag_handler(self, func):
def unknown_command(self, func): processed_args = getfullargspec(func).args
if self.unknown_command_func is not None: if len(processed_args) != 1:
raise UnknownCommandHandlerHasAlreadyBeenCreatedException() raise IncorrectNumberOfHandlerArgsException()
else:
self.unknown_command_func: Callable = func self._not_valid_flag_handler = func
def wrapper(*args, **kwargs):
return func(*args, **kwargs) def input_command_handler(self, input_command: Command):
return wrapper 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:
def input_command_handler(self, input_command): if input_command_name.lower() == command_entity['command'].get_string_entity().lower():
for command_entity in self._command_entities: if command_entity['command'].get_registered_flags():
if input_command.lower() == command_entity['command'].lower(): if input_command_flags:
if self.ignore_command_register: for flag in input_command_flags:
return command_entity['handler_func']() is_valid = command_entity['command'].validate_input_flag(flag)
if not is_valid:
self._not_valid_flag_handler(flag)
return
return command_entity['handler_func'](input_command_flags)
else:
return command_entity['handler_func'](FlagsGroup(None))
else:
if input_command_flags:
self._not_valid_flag_handler(input_command_flags[0])
return
else: else:
if input_command == command_entity['command']:
return command_entity['handler_func']() return command_entity['handler_func']()
def unknown_command_handler(self, unknown_command): def _validate_command(self, command: Command):
self.unknown_command_func(unknown_command) command_name: str = command.get_string_entity()
if command_name in self.get_all_commands():
def _validate_command(self, command: str):
if not isinstance(command, str):
raise InvalidCommandInstanceException()
if command in self.get_all_commands():
raise RepeatedCommandException() raise RepeatedCommandException()
if self.ignore_command_register: if self._ignore_command_register:
if command.lower() in [x.lower() for x in self.get_all_commands()]: if command_name.lower() in [x.lower() for x in self.get_all_commands()]:
raise RepeatedCommandException() raise RepeatedCommandException()
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):
raise RepeatedFlagNameException()
@staticmethod @staticmethod
def _validate_description(command: str, description: str): def _validate_func_args(command: Command, func: Callable):
if not isinstance(description, str): registered_args = command.get_registered_flags()
if description is None: transferred_args = getfullargspec(func).args
description = f'description for "{command}" command' if registered_args and transferred_args:
else: if len(transferred_args) != 1:
raise InvalidDescriptionInstanceException() raise TooManyTransferredArgsException()
return description elif registered_args and not transferred_args:
raise RequiredArgumentNotPassedException()
elif not registered_args and transferred_args:
def set_router_as_main(self): raise TooManyTransferredArgsException()
if self.name == 'subordinate':
self.name = 'main'
self._is_main_router = True
def set_ignore_command_register(self, ignore_command_register: bool): def set_ignore_command_register(self, ignore_command_register: bool):
self.ignore_command_register = ignore_command_register self._ignore_command_register = ignore_command_register
def get_command_entities(self) -> list[dict[str, Callable[[], None] | str]]: def get_command_entities(self) -> list[dict[str, Callable[[], None] | Command]]:
return self._command_entities return self._command_entities
@@ -101,23 +113,9 @@ class Router:
return self.title return self.title
def get_router_info(self) -> dict:
return {
'title': self.title,
'name': self.name,
'ignore_command_register': self.ignore_command_register,
'attributes': {
'command_entities': self._command_entities,
'unknown_command_func': self.unknown_command_func,
'is_main_router': self._is_main_router
}
}
def get_all_commands(self) -> list[str]: def get_all_commands(self) -> list[str]:
all_commands: list[str] = [] all_commands: list[str] = []
for command_entity in self._command_entities: for command_entity in self._command_entities:
all_commands.append(command_entity['command']) all_commands.append(command_entity['command'].get_string_entity())
return all_commands return all_commands
+25 -10
View File
@@ -1,18 +1,33 @@
class InvalidCommandInstanceException(Exception):
def __str__(self):
return "Invalid Command Instance"
class InvalidDescriptionInstanceException(Exception): class InvalidDescriptionInstanceException(Exception):
def __str__(self): def __str__(self):
return "Invalid Description Instance" return "Invalid Description Instance"
class UnknownCommandHandlerHasAlreadyBeenCreatedException(Exception):
def __str__(self):
return "Only one unknown command handler can be declared"
class RepeatedCommandException(Exception): class RepeatedCommandException(Exception):
def __str__(self): def __str__(self):
return "Commands in handler cannot be repeated" return "Commands in handler cannot be repeated"
class RepeatedFlagNameException(Exception):
def __str__(self):
return "Repeated flag name in register command"
class TooManyTransferredArgsException(Exception):
def __str__(self):
return "Too many transferred arguments"
class RequiredArgumentNotPassedException(Exception):
def __str__(self):
return "Required argument not passed"
class NotValidInputFlagHandlerHasBeenAlreadyCreatedException(Exception):
def __str__(self):
return "Invalid Input Flag Handler has already been created"
class IncorrectNumberOfHandlerArgsException(Exception):
def __str__(self):
return "Incorrect Input Flags Handler has incorrect number of arguments"
+9
View File
@@ -0,0 +1,9 @@
import re
def set_description_message_pattern(pattern: str) -> None:
first_check = re.match(r'.*command.*', pattern)
second_check = re.match(r'.*{description}.*', pattern)
set_description_message_pattern('Invalid des{ommand}cription pattern')
+56
View File
@@ -0,0 +1,56 @@
import re
from rich.console import Console
from argenta.command.entity import Command
from argenta.command.params.flag.entity import Flag
from argenta.command.params.flag.flags_group.entity import FlagsGroup
from argenta.router import Router
work_router: Router = Router(title='Work nts:')
settings_router: Router = Router(title='Settings points:')
console = Console()
flagi = FlagsGroup(flags=[
Flag(flag_name='host',
flag_prefix='--',
possible_flag_values=re.compile(r'^192.168.\d{1,3}.\d{1,3}$')),
Flag(flag_name='port',
flag_prefix='--', )
])
@work_router.command(Command(command='0', description='Get Help'))
def command_help():
print('Help command')
'''flags = args.get_flags()
for flag in flags:
print(f'name: "{flag.get_string_entity()}", value: "{flag.get_value()}"')'''
#help_command()
@work_router.command(Command(command='P', description='Start Solving', flags=flagi))
def command_start_solving(argrrtrts: FlagsGroup | None):
print('Solving...')
flags = argrrtrts.get_flags()
for flag in flags:
print(f'name: "{flag.get_string_entity()}", value: "{flag.get_value()}"')
#start_solving_command()
@settings_router.command(Command(command='G', description='Update WordMath'))
def command_update():
print('uefi')
# upgrade_command()
def invalid_input_flag(flag: Flag):
print(f'Invalid inpuuuuuuuuuuuuuuuuuuuuuuuut flag: "{flag.get_string_entity()} {flag.get_value()}"')
work_router.set_invalid_input_flag_handler(invalid_input_flag)
@@ -1,14 +1,18 @@
from tests.mock_app.handlers.routers import work_router, settings_router from tests.mock_app.handlers.routers import work_router, settings_router
from argenta.app.entity import App
from art import text2art from art import text2art
from rich.console import Console from rich.console import Console
from argenta.app import App
from argenta.router import Router
from argenta.command import Command
from argenta.command.params.flag import Flag, FlagsGroup
app: App = App(prompt='[italic white bold]What do you want to do(enter number of action)?', app: App = App(prompt='[italic white bold]What do you want to do(enter number of action)?',
line_separate='[bold green]\n---------------------------------------------\n', line_separate=f'\n{"[bold green]-[/bold green][bold red]-[/bold red]"*25}\n',
print_func=Console().print, print_func=Console().print,
command_group_description_separate='', command_group_description_separate='',
repeat_command_groups=False) repeat_command_groups=True)
def main(): def main():
@@ -18,12 +22,16 @@ def main():
ascii_goodbye_message: str = text2art('GoodBye', font='small') ascii_goodbye_message: str = text2art('GoodBye', font='small')
goodbye_message: str = f'[bold red]\n{ascii_goodbye_message}{' '*12}made by kolo\n' goodbye_message: str = f'[bold red]\n{ascii_goodbye_message}{' '*12}made by kolo\n'
app.include_router(work_router, is_main=True) app.include_router(work_router)
app.include_router(settings_router) app.include_router(settings_router)
app.set_initial_message(initial_greeting) app.set_initial_message(initial_greeting)
app.set_farewell_message(goodbye_message) app.set_farewell_message(goodbye_message)
app.set_invalid_input_flags_handler(lambda raw_command: print(f"Invalid input flags: {raw_command}"))
app.set_unknown_command_handler(lambda command: print(f"Unknown command: {command.get_string_entity()}"))
app.set_repeated_input_flags_handler(lambda raw_command: print(f"Repeated input flags: {raw_command}"))
app.set_description_message_pattern('[bold red][{command}][/bold red] [blue]*=*=*[/blue] [bold yellow italic]{description}') app.set_description_message_pattern('[bold red][{command}][/bold red] [blue]*=*=*[/blue] [bold yellow italic]{description}')
app.start_polling() app.start_polling()
Generated
-442
View File
@@ -1,442 +0,0 @@
# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand.
[[package]]
name = "art"
version = "6.4"
description = "ASCII Art Library For Python"
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "art-6.4-py3-none-any.whl", hash = "sha256:4e58b6f0a0bb8574efb311eff24bdd28bf889c0c526ccbbb5410c644340a301c"},
{file = "art-6.4.tar.gz", hash = "sha256:417fea674bff8cea7ed058291ad1b81a6032dfce5152f28e629fa4a798a2c14c"},
]
[package.extras]
dev = ["bandit (>=1.5.1)", "coverage (>=4.1)", "pydocstyle (>=3.0.0)", "vulture (>=1.0)"]
[[package]]
name = "certifi"
version = "2025.1.31"
description = "Python package for providing Mozilla's CA Bundle."
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"},
{file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"},
]
[[package]]
name = "charset-normalizer"
version = "3.4.1"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"},
{file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"},
{file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"},
{file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"},
{file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"},
{file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"},
{file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"},
{file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"},
{file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"},
{file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"},
{file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"},
{file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"},
{file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"},
{file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"},
{file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"},
{file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"},
{file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"},
{file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"},
{file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"},
{file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"},
{file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"},
{file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"},
{file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"},
{file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"},
{file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"},
{file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"},
{file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"},
{file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"},
{file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"},
{file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"},
{file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"},
{file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"},
{file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"},
]
[[package]]
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
groups = ["dev"]
markers = "platform_system == \"Windows\""
files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
[[package]]
name = "idna"
version = "3.10"
description = "Internationalized Domain Names in Applications (IDNA)"
optional = false
python-versions = ">=3.6"
groups = ["dev"]
files = [
{file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"},
{file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"},
]
[package.extras]
all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"]
[[package]]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
{file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
]
[package.dependencies]
mdurl = ">=0.1,<1.0"
[package.extras]
benchmarking = ["psutil", "pytest", "pytest-benchmark"]
code-style = ["pre-commit (>=3.0,<4.0)"]
compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
linkify = ["linkify-it-py (>=1,<3)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
{file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
]
[[package]]
name = "numexpr"
version = "2.10.2"
description = "Fast numerical expression evaluator for NumPy"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "numexpr-2.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5b0e82d2109c1d9e63fcd5ea177d80a11b881157ab61178ddbdebd4c561ea46"},
{file = "numexpr-2.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3fc2b8035a0c2cdc352e58c3875cb668836018065cbf5752cb531015d9a568d8"},
{file = "numexpr-2.10.2-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0db5ff5183935d1612653559c319922143e8fa3019007696571b13135f216458"},
{file = "numexpr-2.10.2-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15f59655458056fdb3a621b1bb8e071581ccf7e823916c7568bb7c9a3e393025"},
{file = "numexpr-2.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ce8cccf944339051e44a49a124a06287fe3066d0acbff33d1aa5aee10a96abb7"},
{file = "numexpr-2.10.2-cp310-cp310-win32.whl", hash = "sha256:ba85371c9a8d03e115f4dfb6d25dfbce05387002b9bc85016af939a1da9624f0"},
{file = "numexpr-2.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:deb64235af9eeba59fcefa67e82fa80cfc0662e1b0aa373b7118a28da124d51d"},
{file = "numexpr-2.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6b360eb8d392483410fe6a3d5a7144afa298c9a0aa3e9fe193e89590b47dd477"},
{file = "numexpr-2.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d9a42f5c24880350d88933c4efee91b857c378aaea7e8b86221fff569069841e"},
{file = "numexpr-2.10.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:83fcb11988b57cc25b028a36d285287d706d1f536ebf2662ea30bd990e0de8b9"},
{file = "numexpr-2.10.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4213a92efa9770bc28e3792134e27c7e5c7e97068bdfb8ba395baebbd12f991b"},
{file = "numexpr-2.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebdbef5763ca057eea0c2b5698e4439d084a0505d9d6e94f4804f26e8890c45e"},
{file = "numexpr-2.10.2-cp311-cp311-win32.whl", hash = "sha256:3bf01ec502d89944e49e9c1b5cc7c7085be8ca2eb9dd46a0eafd218afbdbd5f5"},
{file = "numexpr-2.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:e2d0ae24b0728e4bc3f1d3f33310340d67321d36d6043f7ce26897f4f1042db0"},
{file = "numexpr-2.10.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b5323a46e75832334f1af86da1ef6ff0add00fbacdd266250be872b438bdf2be"},
{file = "numexpr-2.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a42963bd4c62d8afa4f51e7974debfa39a048383f653544ab54f50a2f7ec6c42"},
{file = "numexpr-2.10.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5191ba8f2975cb9703afc04ae845a929e193498c0e8bcd408ecb147b35978470"},
{file = "numexpr-2.10.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97298b14f0105a794bea06fd9fbc5c423bd3ff4d88cbc618860b83eb7a436ad6"},
{file = "numexpr-2.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f9d7805ccb6be2d3b0f7f6fad3707a09ac537811e8e9964f4074d28cb35543db"},
{file = "numexpr-2.10.2-cp312-cp312-win32.whl", hash = "sha256:cb845b2d4f9f8ef0eb1c9884f2b64780a85d3b5ae4eeb26ae2b0019f489cd35e"},
{file = "numexpr-2.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:57b59cbb5dcce4edf09cd6ce0b57ff60312479930099ca8d944c2fac896a1ead"},
{file = "numexpr-2.10.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a37d6a51ec328c561b2ca8a2bef07025642eca995b8553a5267d0018c732976d"},
{file = "numexpr-2.10.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:81d1dde7dd6166d8ff5727bb46ab42a6b0048db0e97ceb84a121334a404a800f"},
{file = "numexpr-2.10.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5b3f814437d5a10797f8d89d2037cca2c9d9fa578520fc911f894edafed6ea3e"},
{file = "numexpr-2.10.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9309f2e43fe6e4560699ef5c27d7a848b3ff38549b6b57194207cf0e88900527"},
{file = "numexpr-2.10.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ebb73b93f5c4d6994f357fa5a47a9f7a5485577e633b3c46a603cb01445bbb19"},
{file = "numexpr-2.10.2-cp313-cp313-win32.whl", hash = "sha256:ec04c9a3c050c175348801e27c18c68d28673b7bfb865ef88ce333be523bbc01"},
{file = "numexpr-2.10.2-cp313-cp313-win_amd64.whl", hash = "sha256:d7a3fc83c959288544db3adc70612475d8ad53a66c69198105c74036182d10dd"},
{file = "numexpr-2.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0495f8111c3633e265248709b8b3b521bbfa646ba384909edd10e2b9a588a83a"},
{file = "numexpr-2.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2aa05ac71bee3b1253e73173c4d7fa96a09a18970c0226f1c2c07a71ffe988dc"},
{file = "numexpr-2.10.2-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3a23c3002ab330056fbdd2785871937a6f2f2fa85d06c8d0ff74ea8418119d1"},
{file = "numexpr-2.10.2-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a018a7d81326f4c73d8b5aee61794d7d8514512f43957c0db61eb2a8a86848c7"},
{file = "numexpr-2.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:037859b17a0abe2b489d4c2cfdadd2bf458ec80dd83f338ea5544c7987e06b85"},
{file = "numexpr-2.10.2-cp39-cp39-win32.whl", hash = "sha256:eb278ccda6f893a312aa0452701bb17d098b7b14eb7c9381517d509cce0a39a3"},
{file = "numexpr-2.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:734b64c6d6a597601ce9d0ef7b666e678ec015b446f1d1412c23903c021436c3"},
{file = "numexpr-2.10.2.tar.gz", hash = "sha256:b0aff6b48ebc99d2f54f27b5f73a58cb92fde650aeff1b397c71c8788b4fff1a"},
]
[package.dependencies]
numpy = ">=1.23.0"
[[package]]
name = "numpy"
version = "2.2.2"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
files = [
{file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"},
{file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"},
{file = "numpy-2.2.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715"},
{file = "numpy-2.2.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a"},
{file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97"},
{file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957"},
{file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d"},
{file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd"},
{file = "numpy-2.2.2-cp310-cp310-win32.whl", hash = "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160"},
{file = "numpy-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014"},
{file = "numpy-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189"},
{file = "numpy-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323"},
{file = "numpy-2.2.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac"},
{file = "numpy-2.2.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e"},
{file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c"},
{file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f"},
{file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826"},
{file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8"},
{file = "numpy-2.2.2-cp311-cp311-win32.whl", hash = "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50"},
{file = "numpy-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2"},
{file = "numpy-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467"},
{file = "numpy-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a"},
{file = "numpy-2.2.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825"},
{file = "numpy-2.2.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37"},
{file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748"},
{file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0"},
{file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278"},
{file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba"},
{file = "numpy-2.2.2-cp312-cp312-win32.whl", hash = "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283"},
{file = "numpy-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb"},
{file = "numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc"},
{file = "numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369"},
{file = "numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd"},
{file = "numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be"},
{file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84"},
{file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff"},
{file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0"},
{file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de"},
{file = "numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9"},
{file = "numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369"},
{file = "numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391"},
{file = "numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39"},
{file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317"},
{file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49"},
{file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2"},
{file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7"},
{file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb"},
{file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648"},
{file = "numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4"},
{file = "numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576"},
{file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495"},
{file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df"},
{file = "numpy-2.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a"},
{file = "numpy-2.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60"},
{file = "numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f"},
]
[[package]]
name = "pygments"
version = "2.19.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
{file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
]
[package.extras]
windows-terminal = ["colorama (>=0.4.6)"]
[[package]]
name = "requests"
version = "2.32.3"
description = "Python HTTP for Humans."
optional = false
python-versions = ">=3.8"
groups = ["dev"]
files = [
{file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"},
{file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"},
]
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = ">=2,<4"
idna = ">=2.5,<4"
urllib3 = ">=1.21.1,<3"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "rich"
version = "13.9.4"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
optional = false
python-versions = ">=3.8.0"
groups = ["dev"]
files = [
{file = "rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90"},
{file = "rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098"},
]
[package.dependencies]
markdown-it-py = ">=2.2.0"
pygments = ">=2.13.0,<3.0.0"
[package.extras]
jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "setuptools"
version = "75.8.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "setuptools-75.8.0-py3-none-any.whl", hash = "sha256:e3982f444617239225d675215d51f6ba05f845d4eec313da4418fdbb56fb27e3"},
{file = "setuptools-75.8.0.tar.gz", hash = "sha256:c5afc8f407c626b8313a86e10311dd3f661c6cd9c09d4bf8c15c0e11f9f2b0e6"},
]
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"]
core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
enabler = ["pytest-enabler (>=2.2)"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"]
[[package]]
name = "tqdm"
version = "4.67.1"
description = "Fast, Extensible Progress Meter"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
files = [
{file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"},
{file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"},
]
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
dev = ["nbval", "pytest (>=6)", "pytest-asyncio (>=0.24)", "pytest-cov", "pytest-timeout"]
discord = ["requests"]
notebook = ["ipywidgets (>=6)"]
slack = ["slack-sdk"]
telegram = ["requests"]
[[package]]
name = "urllib3"
version = "2.3.0"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.9"
groups = ["dev"]
files = [
{file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"},
{file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"},
]
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"]
h2 = ["h2 (>=4,<5)"]
socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"]
zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "word2number"
version = "1.1"
description = "Convert number words eg. three hundred and forty two to numbers (342)."
optional = false
python-versions = "*"
groups = ["dev"]
files = [
{file = "word2number-1.1.zip", hash = "sha256:70e27a5d387f67b04c71fbb7621c05930b19bfd26efd6851e6e0f9969dcde7d0"},
]
[metadata]
lock-version = "2.1"
python-versions = ">=3.11"
content-hash = "6d71ae12bf4fc44dab312c9626aff93bbc4f5928bf439f144c07de5d9c93d43f"
+13 -5
View File
@@ -1,14 +1,24 @@
[project] [project]
name = "argenta" name = "argenta"
version = "0.2.2" version = "0.3.5"
description = "python library for creating cli apps" description = "python library for creating custom shells"
authors = [ authors = [
{name = "kolo", email = "kolo.is.main@gmail.com"} {name = "kolo", email = "kolo.is.main@gmail.com"}
] ]
license = {text = "MIT"} license = {text = "MIT"}
readme = "README.md" readme = "README.md"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ dependencies = [] # no dependencies
[tool.ruff]
exclude = [
".idea",
"venv",
".git",
"poetry.lock",
".__pycache__",
"tests"
] ]
@@ -23,6 +33,4 @@ numpy = "^2.2.2"
word2number = "^1.1" word2number = "^1.1"
numexpr = "^2.10.2" numexpr = "^2.10.2"
requests = "^2.32.3" requests = "^2.32.3"
tqdm = "^4.67.1"
setuptools = "^75.8.0"
-32
View File
@@ -1,32 +0,0 @@
from rich.console import Console
from argenta.router import Router
from ..handlers.handlers_implementation.help_command import help_command
from ..handlers.handlers_implementation.solving_command import start_solving_command
from ..handlers.handlers_implementation.upgrade_command import upgrade_command
work_router: Router = Router(title='Work points:')
settings_router: Router = Router(title='Settings points:')
console = Console()
@work_router.command(command='0', description='Get Help')
def command_help():
help_command()
@work_router.command(command='1', description='Start Solving')
def command_start_solving():
start_solving_command()
@settings_router.command(command='U', description='Update WordMath')
def command_update():
upgrade_command()
@work_router.unknown_command
def command_unknown_command(command):
console.print(f'[bold red]Unknown command: [/bold red]{command}')
@@ -1,29 +0,0 @@
from pprint import pprint
from rich.console import Console
from argenta.router import Router
work_router: Router = Router(name='work')
settings_router: Router = Router(name='settings')
console = Console()
@work_router.command(command='a')
def command_help():
console.print('[bold red]command help [/bold red]')
@work_router.command(command='B', description='tester')
def command_start_solving():
console.print('[bold red]command start [/bold red]')
@settings_router.command(command='b')
def command_settings():
console.print('[bold red]command settings [/bold red]')
@work_router.unknown_command
def command_unknown_command(command):
console.print(f'[bold red]Unknown command: [/bold red]{command}')
-18
View File
@@ -1,18 +0,0 @@
from pprint import pprint
from tests.mock_default_app.handlers.routers import work_router, settings_router
from argenta.app.entity import App
from art import text2art
app: App = App(ignore_command_register=False,
line_separate='\n-------------------------------\n')
def main():
app.include_router(work_router, is_main=True)
app.include_router(settings_router)
app.start_polling()
if __name__ == "__main__":
main()
+20
View File
@@ -0,0 +1,20 @@
from argenta.app import App
from argenta.app.exceptions import (InvalidDescriptionMessagePatternException,
NoRegisteredRoutersException)
import unittest
class TestApp(unittest.TestCase):
def test_set_invalid_description_message_pattern(self):
with self.assertRaises(InvalidDescriptionMessagePatternException):
App().set_description_message_pattern('Invalid description pattern')
def test_set_invalid_description_message_pattern2(self):
with self.assertRaises(InvalidDescriptionMessagePatternException):
App().set_description_message_pattern('Invalid {desription} description {comand} pattern')
def test_no_registered_router(self):
with self.assertRaises(NoRegisteredRoutersException):
App()._validate_number_of_routers()
+32
View File
@@ -0,0 +1,32 @@
from argenta.command import Command
from argenta.command.params.flag import Flag, FlagsGroup
from argenta.command.exceptions import (UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException)
import unittest
class TestCommand(unittest.TestCase):
def test_parse_correct_raw_command(self):
self.assertEqual(Command.parse_input_command('ssh --host 192.168.0.3').get_string_entity(), 'ssh')
def test_parse_raw_command_with_flag_name_without_value(self):
with self.assertRaises(UnprocessedInputFlagException):
Command.parse_input_command('ssh --host')
def test_parse_raw_command_without_flag_name_with_value(self):
with self.assertRaises(UnprocessedInputFlagException):
Command.parse_input_command('ssh 192.168.0.3')
def test_parse_raw_command_with_repeated_flag_name(self):
with self.assertRaises(RepeatedInputFlagsException):
Command.parse_input_command('ssh --host 192.168.0.3 --host 172.198.0.43')
def test_parse_empty_raw_command(self):
with self.assertRaises(EmptyInputCommandException):
Command.parse_input_command('')
def test_get_command_description(self):
self.assertEqual(Command(command='test', description='test description').get_description(), 'test description')
+75
View File
@@ -0,0 +1,75 @@
from argenta.command.params.flag import Flag
import unittest
import re
class TestFlag(unittest.TestCase):
def test_get_string_entity(self):
self.assertEqual(Flag(flag_name='test').get_string_entity(),
'-test')
def test_get_string_entity2(self):
self.assertEqual(Flag(flag_name='test',
flag_prefix='---').get_string_entity(),
'---test')
def test_get_flag_name(self):
self.assertEqual(Flag(flag_name='test').get_flag_name(),
'test')
def test_get_flag_prefix(self):
self.assertEqual(Flag(flag_name='test').get_flag_prefix(),
'-')
def test_get_flag_prefix2(self):
self.assertEqual(Flag(flag_name='test',
flag_prefix='--').get_flag_prefix(),
'--')
def test_get_flag_value_without_set(self):
self.assertEqual(Flag(flag_name='test').get_value(),
None)
def test_get_flag_value_with_set(self):
flag = Flag(flag_name='test')
flag.set_value('example')
self.assertEqual(flag.get_value(), 'example')
def test_validate_incorrect_flag_value_with_list_of_possible_flag_values(self):
flag = Flag(flag_name='test', possible_flag_values=['1', '2', '3'])
self.assertEqual(flag.validate_input_flag_value('bad value'), False)
def test_validate_correct_flag_value_with_list_of_possible_flag_values(self):
flag = Flag(flag_name='test', possible_flag_values=['1', '2', '3'])
self.assertEqual(flag.validate_input_flag_value('1'), True)
def test_validate_incorrect_flag_value_with_pattern_of_possible_flag_values(self):
flag = Flag(flag_name='test', possible_flag_values=re.compile(r'192.168.\d+.\d+'))
self.assertEqual(flag.validate_input_flag_value('152.123.9.8'), False)
def test_validate_correct_flag_value_with_pattern_of_possible_flag_values(self):
flag = Flag(flag_name='test', possible_flag_values=re.compile(r'192.168.\d+.\d+'))
self.assertEqual(flag.validate_input_flag_value('192.168.9.8'), True)
+26
View File
@@ -0,0 +1,26 @@
from argenta.command.params.flag import Flag, FlagsGroup
import unittest
class TestFlagsGroup(unittest.TestCase):
def test_get_flags(self):
flags = FlagsGroup()
list_of_flags = [
Flag('test1'),
Flag('test2'),
Flag('test3'),
]
flags.add_flags(list_of_flags)
self.assertEqual(flags.get_flags(),
list_of_flags)
def test_add_flag(self):
flags = FlagsGroup()
flags.add_flag(Flag('test'))
self.assertEqual(len(flags.get_flags()), 1)
def test_add_flags(self):
flags = FlagsGroup()
flags.add_flags([Flag('test'), Flag('test2')])
self.assertEqual(len(flags.get_flags()), 2)
+34
View File
@@ -0,0 +1,34 @@
from argenta.command.params.flag import FlagsGroup, Flag
from argenta.router import Router
from argenta.command import Command
import unittest
class TestRouter(unittest.TestCase):
def test_get_router_name(self):
self.assertEqual(Router(name='test name').get_name(), 'test name')
def test_get_router_title(self):
self.assertEqual(Router(title='test title').get_title(), 'test title')
def test_input_correct_command(self):
router = Router()
@router.command(Command(command='test'))
def test():
return 'correct result'
self.assertEqual(router.input_command_handler(Command(command='test')), 'correct result')
def test_input_command_with_invalid_flag(self):
router = Router()
router.set_invalid_input_flag_handler(lambda x: x)
@router.command(Command(command='test'))
def test():
return 'correct result'
input_command = Command(command='test')
input_command.set_input_flags(FlagsGroup([Flag('host')]))
self.assertEqual(router.input_command_handler(input_command), None)