6 Commits

Author SHA1 Message Date
kolo 956febc757 v0.4.0 2025-03-15 20:50:44 +03:00
kolo beafdd0afd new system tests 2025-03-15 18:47:26 +03:00
kolo b172e2cdc3 new system tests 2025-03-15 11:42:12 +03:00
kolo baaf0e25f3 some fix 2025-03-15 00:08:39 +03:00
kolo 09c40978a1 fix 2025-03-12 13:01:15 +03:00
kolo 0f4f48555e fix 2025-03-12 00:51:11 +03:00
18 changed files with 454 additions and 37 deletions
+9 -10
View File
@@ -60,12 +60,11 @@ from argenta.command.flag import FlagsGroup, Flag
router = Router() router = Router()
registered_flags = FlagsGroup([ registered_flags = FlagsGroup(
Flag(flag_name='host', Flag(flag_name='host',
flag_prefix='--', flag_prefix='--',
possible_flag_values=re.compile(r'^192.168.\d{1,3}.\d{1,3}$')), possible_flag_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}$')))
])
@router.command(Command("hello")) @router.command(Command("hello"))
@@ -365,8 +364,7 @@ Command(trigger: str,
```python ```python
Flag(flag_name: str, Flag(flag_name: str,
flag_prefix: typing.Literal['-', '--', '---'] = '-', flag_prefix: typing.Literal['-', '--', '---'] = '-',
ignore_flag_value_register: bool = False, possible_flag_values: list[str] | typing.Pattern[str] | False = True)
possible_flag_values: list[str] | typing.Pattern[str] = False)
``` ```
--- ---
@@ -375,9 +373,10 @@ Flag(flag_name: str,
- **name : mean** - **name : mean**
- `flag_name` (`str`): Имя флага - `flag_name` (`str`): Имя флага
- `flag_prefix` (`Literal['-', '--', '---']`): Префикс команды, допустимым значением является от одного до трёх минусов - `flag_prefix` (`Literal['-', '--', '---']`): Префикс команды, допустимым значением является от одного до трёх минусов
- `ignore_flag_value_register` (`bool`): Будет ли игнорироваться регистр значения введённого флага - `possible_flag_values` (`list[str] | Pattern[str] | bool`): Множество допустимых значений флага, может быть задано
- `possible_flag_values` (`list[str] | Pattern[str]`): Множество допустимых значений флага, может быть задано списком с допустимыми значениями или регулярным выражением (рекомендуется `re.compile(r'example exspression')`), при значении
списком с допустимыми значениями или регулярным выражением (рекомендуется `re.compile(r'example exspression')`) аргумента `False` у введённого флага не может быть значения, иначе будет вызвано исключение и обработано соответствующим
еррор-хэндлером
--- ---
@@ -410,14 +409,14 @@ Flag(flag_name: str,
### Конструктор ### Конструктор
```python ```python
FlagsGroup(flags: list[Flag] = None) FlagsGroup(*flagы: Flag)
``` ```
--- ---
**Аргументы:** **Аргументы:**
- **name : mean** - **name : mean**
- `flags` (`list[Flag]`): Список флагов, которые будут объединены в одну группу - `*flags` (`Flag`): Неограниченное количество передаваемых флагов
--- ---
+8 -6
View File
@@ -1,4 +1,3 @@
from pprint import pprint
from typing import Callable from typing import Callable
from inspect import getfullargspec from inspect import getfullargspec
import re import re
@@ -51,7 +50,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(f'Empty input 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()}") self._unknown_command_handler: Callable[[Command], None] = lambda command: print_func(f"Unknown command: {command.get_trigger()}")
def start_polling(self) -> None: def start_polling(self) -> None:
@@ -101,7 +100,9 @@ class App:
self.print_func(self.prompt) self.print_func(self.prompt)
continue continue
self._check_command_for_exit_command(input_command.get_trigger()) is_exit = self._is_exit_command(input_command.get_trigger())
if is_exit:
return
self.print_func(self.line_separate) self.print_func(self.line_separate)
is_unknown_command: bool = self._check_is_command_unknown(input_command) is_unknown_command: bool = self._check_is_command_unknown(input_command)
@@ -213,15 +214,16 @@ class App:
raise RepeatedCommandInDifferentRoutersException() raise RepeatedCommandInDifferentRoutersException()
def _check_command_for_exit_command(self, command: str): def _is_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)
exit(0) return True
else: else:
if command == self.exit_command: if command == self.exit_command:
self.print_func(self.farewell_message) self.print_func(self.farewell_message)
exit(0) return True
return False
def _check_is_command_unknown(self, command: Command): def _check_is_command_unknown(self, command: Command):
+3 -3
View File
@@ -6,6 +6,7 @@ from .exceptions import (UnprocessedInputFlagException,
from typing import Generic, TypeVar, cast, Literal from typing import Generic, TypeVar, cast, Literal
CommandType = TypeVar('CommandType') CommandType = TypeVar('CommandType')
@@ -15,8 +16,7 @@ class Command(Generic[CommandType]):
flags: Flag | FlagsGroup = None): flags: Flag | FlagsGroup = None):
self._trigger = trigger self._trigger = trigger
self._description = f'description for "{self._trigger}" command' if not description else description self._description = f'description for "{self._trigger}" 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._registered_flags: FlagsGroup | None = flags if isinstance(flags, FlagsGroup) else FlagsGroup(flags) if isinstance(flags, Flag) else flags
self._input_flags: FlagsGroup | None = None self._input_flags: FlagsGroup | None = None
@@ -56,7 +56,7 @@ class Command(Generic[CommandType]):
return self._input_flags return self._input_flags
@staticmethod @staticmethod
def parse_input_command(raw_command: str) -> 'Command[CommandType]': def parse_input_command(raw_command: str) -> CommandType:
if not raw_command: if not raw_command:
raise EmptyInputCommandException() raise EmptyInputCommandException()
list_of_tokens = raw_command.split() list_of_tokens = raw_command.split()
+3
View File
@@ -1,7 +1,10 @@
from dataclasses import dataclass
from argenta.command.flag import Flag from argenta.command.flag import Flag
import re import re
@dataclass
class DefaultFlags:
help_flag = Flag(flag_name='help', possible_flag_values=False) help_flag = Flag(flag_name='help', possible_flag_values=False)
short_help_flag = Flag(flag_name='h', flag_prefix='-', possible_flag_values=False) short_help_flag = Flag(flag_name='h', flag_prefix='-', possible_flag_values=False)
+1 -1
View File
@@ -1,4 +1,4 @@
from argenta.command.flag.entity import Flag from argenta.command.flag import Flag
class FlagsGroup: class FlagsGroup:
+1 -1
View File
@@ -23,7 +23,7 @@ class Router:
self._command_entities: list[dict[str, Callable[[], None] | Command]] = [] self._command_entities: list[dict[str, Callable[[], None] | Command]] = []
self._ignore_command_register: 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()}'") self._not_valid_flag_handler: Callable[[Flag], None] = lambda flag: print(f"Undefined or incorrect input flag: {flag.get_string_entity()}{(' '+flag.get_value()) if flag.get_value() else ''}")
def command(self, command: Command) -> Callable[[Any], Any]: def command(self, command: Command) -> Callable[[Any], Any]:
+6 -2
View File
@@ -1,3 +1,7 @@
from typing import cast, Literal import re
print(cast(Literal['-', '--', '---'], '----'))
def test(string):
return bool(re.match(r'\ntest command\n(.|\s)*\nsome command\n', string))
print(test('test command tpgm4tigm4tigmt\n i0hhmi6h some command'))
+3 -3
View File
@@ -4,13 +4,13 @@ from rich.console import Console
from argenta.command import Command from argenta.command import Command
from argenta.command.flag import Flag, FlagsGroup from argenta.command.flag import Flag, FlagsGroup
from argenta.command.flag.defaults import host_flag, port_flag from argenta.command.flag.defaults import DefaultFlags
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
work_router: Router = Router(title='Work nts:') work_router: Router = Router(title='Work nts:')
work_router.set_invalid_input_flag_handler(lambda flag: print(f'Invalid input flag: "{flag.get_string_entity()} {flag.get_value()}"')) work_router.set_invalid_input_flag_handler(lambda flag: print(f'Invalid input flag: {flag.get_string_entity()} {flag.get_value() if flag.get_value() else ''}'))
settings_router: Router = Router(title='Settings points:') settings_router: Router = Router(title='Settings points:')
@@ -23,7 +23,7 @@ def command_help():
help_command() help_command()
@work_router.command(Command(trigger='j', description='Start Solving', flags=FlagsGroup(host_flag, port_flag))) @work_router.command(Command(trigger='P', description='Start Solving', flags=FlagsGroup(DefaultFlags.host_flag, DefaultFlags.port_flag)))
def command_start_solving(args: dict): def command_start_solving(args: dict):
print('Solving...') print('Solving...')
pprint(args) pprint(args)
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "argenta" name = "argenta"
version = "0.3.9" version = "0.4.0"
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"}
View File
@@ -0,0 +1,198 @@
import _io
from unittest.mock import patch, MagicMock
import unittest
import io
import re
from argenta.app import App
from argenta.command import Command
from argenta.router import Router
from argenta.command.flag import Flag, FlagsGroup
from argenta.command.flag.defaults import DefaultFlags
class TestSystemHandlerNormalWork(unittest.TestCase):
@patch("builtins.input", side_effect=["help", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_incorrect_command(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print('test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn("\nUnknown command: help\n", output)
@patch("builtins.input", side_effect=["TeSt", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_incorrect_command2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print('test command')
app = App(ignore_command_register=False)
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nUnknown command: TeSt\n', output)
@patch("builtins.input", side_effect=["test --help", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_unregistered_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print(f'test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nUndefined or incorrect input flag: --help\n', output)
@patch("builtins.input", side_effect=["test --port 22", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_unregistered_flag2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print('test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nUndefined or incorrect input flag: --port 22\n', output)
@patch("builtins.input", side_effect=["test --host 192.168.32.1 --port 132", "q"])
@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):
router = Router()
flags = FlagsGroup(DefaultFlags.host_flag)
@router.command(Command('test', flags=flags))
def test(args: dict):
print(f'connecting to host {args["host"]["value"]}')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nUndefined or incorrect input flag: --port 132\n', output)
@patch("builtins.input", side_effect=["test", "some", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_one_correct_command_and_one_incorrect_command(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print(f'test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertRegex(output, re.compile(r'\ntest command\n(.|\n)*\nUnknown command: some\n'))
@patch("builtins.input", side_effect=["test", "some", "more", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_two_correct_commands_and_one_incorrect_command(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print(f'test command')
@router.command(Command('more'))
def test():
print(f'more command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertRegex(output, re.compile(r'\ntest command\n(.|\n)*\nUnknown command: some\n(.|\n)*\nmore command'))
@patch("builtins.input", side_effect=["test 535 --port", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_incorrect_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print(f'test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn("\nIncorrect flag syntax: \"test 535 --port\"\n", output)
@patch("builtins.input", side_effect=["", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_empty_command(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print(f'test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn("\nEmpty input command\n", output)
@patch("builtins.input", side_effect=["test --port 22 --port 33", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_repeated_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test', flags=DefaultFlags.port_flag))
def test(args):
print('test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn("\nRepeated input flags: \"test --port 22 --port 33\"", output)
@@ -0,0 +1,211 @@
import _io
from unittest.mock import patch, MagicMock
import unittest
import io
import re
from argenta.app import App
from argenta.command import Command
from argenta.router import Router
from argenta.command.flag import Flag, FlagsGroup
from argenta.command.flag.defaults import DefaultFlags
class TestSystemHandlerNormalWork(unittest.TestCase):
@patch("builtins.input", side_effect=["test", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print('test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\ntest command\n', output)
@patch("builtins.input", side_effect=["TeSt", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print('test command')
app = App(ignore_command_register=True)
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\ntest command\n', output)
@patch("builtins.input", side_effect=["test --help", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_custom_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
flag = Flag('help', '--', False)
@router.command(Command('test', flags=flag))
def test(args: dict):
print(f'\nhelp for {args['help']['name']} flag\n')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nhelp for help flag\n', output)
@patch("builtins.input", side_effect=["test --port 22", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_custom_flag2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
flag = Flag('port', '--', re.compile(r'^\d{1,5}$'))
@router.command(Command('test', flags=flag))
def test(args: dict):
print(f'flag value for {args['port']['name']} flag : {args["port"]["value"]}')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nflag value for port flag : 22\n', output)
@patch("builtins.input", side_effect=["test -h", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_default_flag(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
flag = DefaultFlags.short_help_flag
@router.command(Command('test', flags=flag))
def test(args: dict):
print(f'help for {args['h']['name']} flag')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nhelp for h flag\n', output)
@patch("builtins.input", side_effect=["test --info", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_default_flag2(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
flag = DefaultFlags.info_flag
@router.command(Command('test', flags=flag))
def test(args: dict):
if args.get('info'):
print('info about test command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\ninfo about test command\n', output)
@patch("builtins.input", side_effect=["test --host 192.168.0.1", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_default_flag3(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
flag = DefaultFlags.host_flag
@router.command(Command('test', flags=flag))
def test(args: dict):
print(f'connecting to host {args["host"]["value"]}')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nconnecting to host 192.168.0.1\n', output)
@patch("builtins.input", side_effect=["test --host 192.168.32.1 --port 132", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_correct_command_with_two_flags(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
flags = FlagsGroup(DefaultFlags.host_flag, DefaultFlags.port_flag)
@router.command(Command('test', flags=flags))
def test(args: dict):
print(f'connecting to host {args["host"]["value"]} and port {args["port"]["value"]}')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertIn('\nconnecting to host 192.168.32.1 and port 132\n', output)
@patch("builtins.input", side_effect=["test", "some", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_two_correct_command(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print(f'test command')
@router.command(Command('some'))
def test():
print(f'some command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertRegex(output, re.compile(r'\ntest command\n(.|\n)*\nsome command\n'))
@patch("builtins.input", side_effect=["test", "some", "more", "q"])
@patch("sys.stdout", new_callable=io.StringIO)
def test_input_three_correct_command(self, mock_stdout: _io.StringIO, magick_mock: MagicMock):
router = Router()
@router.command(Command('test'))
def test():
print(f'test command')
@router.command(Command('some'))
def test():
print(f'some command')
@router.command(Command('more'))
def test():
print(f'more command')
app = App()
app.include_router(router)
app.start_polling()
output = mock_stdout.getvalue()
self.assertRegex(output, re.compile(r'\ntest command\n(.|\n)*\nsome command\n(.|\n)*\nmore command'))
View File