more working

This commit is contained in:
2025-03-31 23:53:49 +03:00
parent 5c6fa5151a
commit cc8135b733
11 changed files with 103 additions and 104 deletions
+7 -8
View File
@@ -56,14 +56,14 @@ if __name__ == '__main__':
import re import re
from argenta.router import Router from argenta.router import Router
from argenta.command import Command from argenta.command import Command
from argenta.command.flag.registered_flag import Flags, Flag from argenta.command.flag import Flags, Flag, InputFlags
router = Router() router = Router()
registered_flags = Flags( registered_flags = Flags(
Flag(flag_name='host', Flag(name='host',
flag_prefix='--', prefix='--',
possible_flag_values=re.compile(r'^192.168.\d{1,3}.\d{1,3}$')), possible_values=re.compile(r'^192.168.\d{1,3}.\d{1,3}$')),
Flag('port', '--', re.compile(r'^[0-9]{1,4}$'))) Flag('port', '--', re.compile(r'^[0-9]{1,4}$')))
@@ -75,10 +75,10 @@ def handler():
@router.command(Command(trigger="ssh", @router.command(Command(trigger="ssh",
description='connect via ssh', description='connect via ssh',
flags=registered_flags)) flags=registered_flags))
def handler_with_flags(flags: dict): def handler_with_flags(flags: InputFlags):
for flag in flags: for flag in flags:
print(f'Flag name: {flag['name']}\n' print(f'Flag name: {flag.get_name()}\n'
f'Flag value: {flag['value']}') f'Flag value: {flag.get_value()}')
``` ```
--- ---
@@ -299,7 +299,6 @@ Router(title: str = 'Commands group title:',
--- ---
### Исключения ### Исключения
- `RepeatedCommandException` - Одна и та же команда зарегистрирована в одном роутере
- `RepeatedFlagNameException` - Повторяющиеся зарегистрированные флаги в команде - `RepeatedFlagNameException` - Повторяющиеся зарегистрированные флаги в команде
- `TooManyTransferredArgsException` - Слишком много зарегистрированных аргументов у обработчика команды - `TooManyTransferredArgsException` - Слишком много зарегистрированных аргументов у обработчика команды
- `RequiredArgumentNotPassedException` - Не зарегистрирован обязательный аргумент у обработчика команды(аргумент, через который будут переданы флаги введённой команды) - `RequiredArgumentNotPassedException` - Не зарегистрирован обязательный аргумент у обработчика команды(аргумент, через который будут переданы флаги введённой команды)
+23 -22
View File
@@ -52,7 +52,7 @@ class App:
self._invalid_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'Incorrect flag syntax: "{raw_command}"') 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._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('Empty input command') self._empty_input_command_handler: Callable[[], None] = lambda: print_func('Empty input command')
self._unknown_command_handler: Callable[[Command], None] = lambda command: print_func(f"Unknown command: {command.get_trigger()}") self._unknown_command_handler: Callable[[InputCommand], None] = lambda command: print_func(f"Unknown command: {command.get_trigger()}")
self._exit_command_handler: Callable[[], None] = lambda: print_func(self.farewell_message) self._exit_command_handler: Callable[[], None] = lambda: print_func(self.farewell_message)
@@ -78,28 +78,12 @@ class App:
raw_command: str = input() raw_command: str = input()
try: try:
input_command: InputCommand = InputCommand.parse_input_command(raw_command=raw_command) input_command: InputCommand = InputCommand.parse(raw_command=raw_command)
except UnprocessedInputFlagException: except (UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException) as error:
self.print_func(self.line_separate) self.print_func(self.line_separate)
self._invalid_input_flags_handler(raw_command) self._error_handler(error, raw_command)
self.print_func(self.line_separate)
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) self.print_func(self.line_separate)
if not self.repeat_command_groups: if not self.repeat_command_groups:
@@ -203,6 +187,7 @@ class App:
for router in routers: for router in routers:
self.include_router(router) self.include_router(router)
def _validate_number_of_routers(self) -> None: def _validate_number_of_routers(self) -> None:
if not self._registered_routers: if not self._registered_routers:
raise NoRegisteredRoutersException() raise NoRegisteredRoutersException()
@@ -259,3 +244,19 @@ class App:
) )
) )
self.print_func(self.command_group_description_separate) self.print_func(self.command_group_description_separate)
def _error_handler(self,
error: UnprocessedInputFlagException |
RepeatedInputFlagsException |
EmptyInputCommandException,
raw_command: str) -> None:
match error:
case UnprocessedInputFlagException():
self._invalid_input_flags_handler(raw_command)
case RepeatedInputFlagsException():
self._repeated_input_flags_handler(raw_command)
case EmptyInputCommandException():
self._empty_input_command_handler()
+4
View File
@@ -0,0 +1,4 @@
__all__ = ('InputFlags', 'InputFlag', 'Flag', 'Flags')
from .models import InputFlags, InputFlag, Flags, Flag
+1 -1
View File
@@ -4,7 +4,7 @@ import re
@dataclass @dataclass
class DefaultFlags: class PredeterminedFlags:
HELP = Flag(name='help', possible_values=False) HELP = Flag(name='help', possible_values=False)
SHORT_HELP = Flag(name='h', prefix='-', possible_values=False) SHORT_HELP = Flag(name='h', prefix='-', possible_values=False)
+13 -6
View File
@@ -4,11 +4,9 @@ from abc import ABC, abstractmethod
class BaseFlag: class BaseFlag:
def __init__(self, name: str, def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--', prefix: Literal['-', '--', '---'] = '--'):
possible_values: list[str] | Pattern[str] | False = True):
self._name = name self._name = name
self._prefix = prefix self._prefix = prefix
self.possible_values = possible_values
def get_string_entity(self): def get_string_entity(self):
string_entity: str = self._prefix + self._name string_entity: str = self._prefix + self._name
@@ -25,9 +23,9 @@ class BaseFlag:
class InputFlag(BaseFlag): class InputFlag(BaseFlag):
def __init__(self, name: str, def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--', prefix: Literal['-', '--', '---'] = '--',
possible_values: list[str] | Pattern[str] | False = True): value: str = None):
super().__init__(name, prefix, possible_values) super().__init__(name, prefix)
self._flag_value = None self._flag_value = value
def get_value(self): def get_value(self):
return self._flag_value return self._flag_value
@@ -38,6 +36,12 @@ class InputFlag(BaseFlag):
class Flag(BaseFlag): class Flag(BaseFlag):
def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--',
possible_values: list[str] | Pattern[str] | False = True):
super().__init__(name, prefix)
self.possible_values = possible_values
def validate_input_flag_value(self, input_flag_value: str | None): def validate_input_flag_value(self, input_flag_value: str | None):
if self.possible_values is False: if self.possible_values is False:
if input_flag_value is None: if input_flag_value is None:
@@ -45,11 +49,14 @@ class Flag(BaseFlag):
else: else:
return False return False
elif isinstance(self.possible_values, Pattern): elif isinstance(self.possible_values, Pattern):
if isinstance(input_flag_value, str):
is_valid = bool(self.possible_values.match(input_flag_value)) is_valid = bool(self.possible_values.match(input_flag_value))
if bool(is_valid): if bool(is_valid):
return True return True
else: else:
return False return False
else:
return False
elif isinstance(self.possible_values, list): elif isinstance(self.possible_values, list):
if input_flag_value in self.possible_values: if input_flag_value in self.possible_values:
+27 -39
View File
@@ -5,30 +5,24 @@ from argenta.command.exceptions import (UnprocessedInputFlagException,
from typing import Generic, TypeVar, cast, Literal from typing import Generic, TypeVar, cast, Literal
BaseCommandType = TypeVar('BaseCommandType') InputCommandType = TypeVar('InputCommandType')
class BaseCommand(Generic[BaseCommandType]): class BaseCommand:
def __init__(self, trigger: str, def __init__(self, trigger: str):
description: str = None,
flags: Flag | Flags = None):
self._trigger = trigger self._trigger = trigger
self._description = f'description for "{self._trigger}" command' if not description else description
def get_trigger(self) -> str: def get_trigger(self) -> str:
return self._trigger return self._trigger
def get_description(self) -> str:
return self._description
class Command(BaseCommand): class Command(BaseCommand):
def __init__(self, trigger: str, def __init__(self, trigger: str,
description: str = None, description: str = None,
flags: Flag | Flags = None): flags: Flag | Flags = None):
super().__init__(trigger, description) super().__init__(trigger)
self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags() self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags()
self._description = f'description for "{self._trigger}" command' if not description else description
def get_registered_flags(self) -> Flags: def get_registered_flags(self) -> Flags:
return self._registered_flags return self._registered_flags
@@ -49,13 +43,15 @@ class Command(BaseCommand):
return True return True
return False return False
def get_description(self) -> str:
return self._description
class InputCommand(BaseCommand):
class InputCommand(BaseCommand, Generic[InputCommandType]):
def __init__(self, trigger: str, def __init__(self, trigger: str,
description: str = None,
input_flags: InputFlag | InputFlags = None): input_flags: InputFlag | InputFlags = None):
super().__init__(trigger, description) super().__init__(trigger)
self._input_flags: InputFlags = input_flags if isinstance(input_flags, InputFlags) else InputFlags(input_flags) if isinstance(input_flags, InputFlag) else InputFlags() self._input_flags: InputFlags = input_flags if isinstance(input_flags, InputFlags) else InputFlags(input_flags) if isinstance(input_flags, InputFlag) else InputFlags()
def _set_input_flags(self, input_flags: InputFlags): def _set_input_flags(self, input_flags: InputFlags):
@@ -65,53 +61,45 @@ class InputCommand(BaseCommand):
return self._input_flags return self._input_flags
@staticmethod @staticmethod
def parse_input_command(raw_command: str) -> BaseCommandType: def parse(raw_command: str) -> InputCommandType:
if not raw_command: if not raw_command:
raise EmptyInputCommandException() raise EmptyInputCommandException()
list_of_tokens = raw_command.split() list_of_tokens = raw_command.split()
command = list_of_tokens[0] command = list_of_tokens.pop(0)
list_of_tokens.pop(0)
input_flags: InputFlags = InputFlags() input_flags: InputFlags = InputFlags()
current_flag_name = None current_flag_name, current_flag_value = None, None
current_flag_value = None
for k, _ in enumerate(list_of_tokens): for k, _ in enumerate(list_of_tokens):
if _.startswith('-'): if _.startswith('-'):
flag_prefix_last_symbol_index = _.rfind('-') if current_flag_name or len(_) < 2 or len(_[:_.rfind('-')]) > 3:
if current_flag_name or len(_) < 2 or len(_[:flag_prefix_last_symbol_index]) > 3:
raise UnprocessedInputFlagException() raise UnprocessedInputFlagException()
else:
current_flag_name = _ current_flag_name = _
else: else:
if not current_flag_name: if not current_flag_name:
raise UnprocessedInputFlagException() raise UnprocessedInputFlagException()
else:
current_flag_value = _ current_flag_value = _
if current_flag_name: if current_flag_name:
if not len(list_of_tokens) == k+1: if not len(list_of_tokens) == k+1:
if not list_of_tokens[k + 1].startswith('-'): if not list_of_tokens[k+1].startswith('-'): continue
continue
flag_prefix_last_symbol_index = current_flag_name.rfind('-')
flag_prefix = current_flag_name[:flag_prefix_last_symbol_index + 1]
flag_name = current_flag_name[flag_prefix_last_symbol_index + 1:]
input_flag = InputFlag(name=flag_name,
prefix=cast(Literal['-', '--', '---'], flag_prefix))
input_flag.set_value(current_flag_value)
all_flags = [x.get_string_entity() for x in input_flags.get_flags()] input_flag = InputFlag(name=current_flag_name[current_flag_name.rfind('-')+1:],
prefix=cast(Literal['-', '--', '---'],
current_flag_name[:current_flag_name.rfind('-')+1]),
value=current_flag_value)
all_flags = [flag.get_string_entity() for flag in input_flags.get_flags()]
if input_flag.get_string_entity() not in all_flags: if input_flag.get_string_entity() not in all_flags:
input_flags.add_flag(input_flag) input_flags.add_flag(input_flag)
else: else:
raise RepeatedInputFlagsException(input_flag) raise RepeatedInputFlagsException(input_flag)
current_flag_name = None current_flag_name, current_flag_value = None, None
current_flag_value = None
if any([current_flag_name, current_flag_value]): if any([current_flag_name, current_flag_value]):
raise UnprocessedInputFlagException() raise UnprocessedInputFlagException()
if len(input_flags.get_flags()) == 0:
return InputCommand(trigger=command)
else: else:
input_command = InputCommand(trigger=command) return InputCommand(trigger=command, input_flags=input_flags)
input_command._set_input_flags(input_flags)
return input_command
+4 -4
View File
@@ -2,8 +2,8 @@ from pprint import pprint
from rich.console import Console from rich.console import Console
from argenta.command import Command from argenta.command import Command
from argenta.command.flag.models import Flags, InputFlags from argenta.command.flag import Flags, InputFlags, Flag
from argenta.command.flag.defaults import DefaultFlags from argenta.command.flag.defaults import PredeterminedFlags
from argenta.router import Router from argenta.router import Router
from .handlers_implementation.help_command import help_command from .handlers_implementation.help_command import help_command
@@ -22,10 +22,10 @@ def command_help():
help_command() help_command()
@work_router.command(Command(trigger='P', description='Start Solving', flags=Flags(DefaultFlags.HOST, DefaultFlags.PORT))) @work_router.command(Command(trigger='P', description='Start Solving', flags=Flags(PredeterminedFlags.HOST, PredeterminedFlags.PORT)))
def command_start_solving(args: InputFlags): def command_start_solving(args: InputFlags):
print('Solving...') print('Solving...')
pprint(args) pprint(args.get_flag('host'))
#start_solving_command() #start_solving_command()
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "argenta" name = "argenta"
version = "0.4.1" version = "0.4.5"
description = "python library for creating custom shells" 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"}
@@ -7,8 +7,8 @@ import re
from argenta.app import App from argenta.app import App
from argenta.command import Command from argenta.command import Command
from argenta.router import Router from argenta.router import Router
from argenta.command.flag.models import Flags from argenta.command.flag.models import Flags, InputFlags
from argenta.command.flag.defaults import DefaultFlags from argenta.command.flag.defaults import PredeterminedFlags
@@ -89,11 +89,11 @@ class TestSystemHandlerNormalWork(unittest.TestCase):
@patch("sys.stdout", new_callable=io.StringIO) @patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_one_correct_flag_an_one_incorrect_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock): def test_input_correct_command_with_one_correct_flag_an_one_incorrect_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router() router = Router()
flags = Flags(DefaultFlags.HOST) flags = Flags(PredeterminedFlags.HOST)
@router.command(Command('test', flags=flags)) @router.command(Command('test', flags=flags))
def test(args: dict): def test(args: InputFlags):
print(f'connecting to host {args["host"]["value"]}') print(f'connecting to host {args.get_flag('host').get_value()}')
app = App() app = App()
app.include_router(router) app.include_router(router)
@@ -185,8 +185,8 @@ class TestSystemHandlerNormalWork(unittest.TestCase):
def test_input_correct_command_with_repeated_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock): def test_input_correct_command_with_repeated_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router() router = Router()
@router.command(Command('test', flags=DefaultFlags.PORT)) @router.command(Command('test', flags=PredeterminedFlags.PORT))
def test(args): def test(args: InputFlags):
print('test command') print('test command')
app = App() app = App()
@@ -8,7 +8,7 @@ from argenta.app import App
from argenta.command.models import Command from argenta.command.models import Command
from argenta.router import Router from argenta.router import Router
from argenta.command.flag.models import Flag, Flags, InputFlags from argenta.command.flag.models import Flag, Flags, InputFlags
from argenta.command.flag.defaults import DefaultFlags from argenta.command.flag.defaults import PredeterminedFlags
@@ -90,7 +90,7 @@ class TestSystemHandlerNormalWork(unittest.TestCase):
@patch("sys.stdout", new_callable=io.StringIO) @patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_default_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock): def test_input_correct_command_with_default_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router() router = Router()
flag = DefaultFlags.SHORT_HELP flag = PredeterminedFlags.SHORT_HELP
@router.command(Command('test', flags=flag)) @router.command(Command('test', flags=flag))
def test(args: dict): def test(args: dict):
@@ -109,7 +109,7 @@ class TestSystemHandlerNormalWork(unittest.TestCase):
@patch("sys.stdout", new_callable=io.StringIO) @patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_default_flag2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock): def test_input_correct_command_with_default_flag2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router() router = Router()
flag = DefaultFlags.INFO flag = PredeterminedFlags.INFO
@router.command(Command('test', flags=flag)) @router.command(Command('test', flags=flag))
def test(args: InputFlags): def test(args: InputFlags):
@@ -129,7 +129,7 @@ class TestSystemHandlerNormalWork(unittest.TestCase):
@patch("sys.stdout", new_callable=io.StringIO) @patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_default_flag3(self, mock_stdout: _io.StringIO, magick_mock: MagicMock): def test_input_correct_command_with_default_flag3(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router() router = Router()
flag = DefaultFlags.HOST flag = PredeterminedFlags.HOST
@router.command(Command('test', flags=flag)) @router.command(Command('test', flags=flag))
def test(args: InputFlags): def test(args: InputFlags):
@@ -148,7 +148,7 @@ class TestSystemHandlerNormalWork(unittest.TestCase):
@patch("sys.stdout", new_callable=io.StringIO) @patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_two_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock): def test_input_correct_command_with_two_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router() router = Router()
flags = Flags(DefaultFlags.HOST, DefaultFlags.PORT) flags = Flags(PredeterminedFlags.HOST, PredeterminedFlags.PORT)
@router.command(Command('test', flags=flags)) @router.command(Command('test', flags=flags))
def test(args: InputFlags): def test(args: InputFlags):
+4 -4
View File
@@ -8,17 +8,17 @@ import unittest
class TestInputCommand(unittest.TestCase): class TestInputCommand(unittest.TestCase):
def test_parse_correct_raw_command(self): def test_parse_correct_raw_command(self):
self.assertEqual(InputCommand.parse_input_command('ssh --host 192.168.0.3').get_trigger(), 'ssh') self.assertEqual(InputCommand.parse('ssh --host 192.168.0.3').get_trigger(), 'ssh')
def test_parse_raw_command_without_flag_name_with_value(self): def test_parse_raw_command_without_flag_name_with_value(self):
with self.assertRaises(UnprocessedInputFlagException): with self.assertRaises(UnprocessedInputFlagException):
InputCommand.parse_input_command('ssh 192.168.0.3') InputCommand.parse('ssh 192.168.0.3')
def test_parse_raw_command_with_repeated_flag_name(self): def test_parse_raw_command_with_repeated_flag_name(self):
with self.assertRaises(RepeatedInputFlagsException): with self.assertRaises(RepeatedInputFlagsException):
InputCommand.parse_input_command('ssh --host 192.168.0.3 --host 172.198.0.43') InputCommand.parse('ssh --host 192.168.0.3 --host 172.198.0.43')
def test_parse_empty_raw_command(self): def test_parse_empty_raw_command(self):
with self.assertRaises(EmptyInputCommandException): with self.assertRaises(EmptyInputCommandException):
InputCommand.parse_input_command('') InputCommand.parse('')