new warning when triggers or aliases overlap

This commit is contained in:
2025-05-10 21:31:59 +03:00
parent ea2d068022
commit cbf7d3c578
5 changed files with 70 additions and 86 deletions
+4 -1
View File
@@ -17,10 +17,13 @@ copy_flags = Flags(
Flag('recursive', '--', False), # Булевый флаг без значения Flag('recursive', '--', False), # Булевый флаг без значения
Flag('force', '-', False) # Короткий булевый флаг Flag('force', '-', False) # Короткий булевый флаг
) )
@file_router.command(Command('case', aliases=['cp', 'ch']))
def handler(response: Response):
print('test')
# Регистрация команды копирования # Регистрация команды копирования
@file_router.command(Command( @file_router.command(Command(
trigger="copy", trigger="ch",
description="Копирование файлов", description="Копирование файлов",
flags=copy_flags, flags=copy_flags,
aliases=["cp"] aliases=["cp"]
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "argenta" name = "argenta"
version = "1.0.2" version = "1.0.3"
description = "Python library for building modular CLI applications" description = "Python library for building modular CLI applications"
authors = [{ name = "kolo", email = "kolo.is.main@gmail.com" }] authors = [{ name = "kolo", email = "kolo.is.main@gmail.com" }]
requires-python = ">=3.8" requires-python = ">=3.8"
+31 -48
View File
@@ -22,8 +22,7 @@ from argenta.response import Response
class BaseApp: class BaseApp:
def __init__( def __init__(self,
self,
prompt: str, prompt: str,
initial_message: str, initial_message: str,
farewell_message: str, farewell_message: str,
@@ -34,8 +33,7 @@ class BaseApp:
repeat_command_groups: bool, repeat_command_groups: bool,
override_system_messages: bool, override_system_messages: bool,
autocompleter: AutoCompleter, autocompleter: AutoCompleter,
print_func: Callable[[str], None], print_func: Callable[[str], None]) -> None:
) -> None:
self._prompt = prompt self._prompt = prompt
self._print_func = print_func self._print_func = print_func
self._exit_command = exit_command self._exit_command = exit_command
@@ -49,30 +47,18 @@ class BaseApp:
self._farewell_message = farewell_message self._farewell_message = farewell_message
self._initial_message = initial_message self._initial_message = initial_message
self._description_message_gen: Callable[[str, str], str] = ( self._description_message_gen: Callable[[str, str], str] = (lambda command, description: f"[{command}] *=*=* {description}")
lambda command, description: f"[{command}] *=*=* {description}"
)
self._registered_routers: RegisteredRouters = RegisteredRouters() self._registered_routers: RegisteredRouters = RegisteredRouters()
self._messages_on_startup: list[str] = [] self._messages_on_startup: list[str] = []
self._all_registered_triggers_in_lower: list[str] = [] self._all_registered_triggers_in_lower_case: list[str] = []
self._all_registered_triggers_in_default_case: list[str] = [] self._all_registered_triggers_in_default_case: list[str] = []
self._incorrect_input_syntax_handler: Callable[[str], None] = ( self._incorrect_input_syntax_handler: Callable[[str], None] = (lambda raw_command: print_func(f"Incorrect flag syntax: {raw_command}"))
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("Empty input command")
self._repeated_input_flags_handler: Callable[[str], None] = ( self._unknown_command_handler: Callable[[InputCommand], None] = (lambda command: print_func(f"Unknown command: {command.get_trigger()}"))
lambda raw_command: print_func(f"Repeated input flags: {raw_command}") self._exit_command_handler: Callable[[Response], None] = (lambda response: print_func(self._farewell_message))
)
self._empty_input_command_handler: Callable[[], None] = lambda: print_func(
"Empty input command"
)
self._unknown_command_handler: Callable[[InputCommand], None] = (
lambda command: print_func(f"Unknown command: {command.get_trigger()}")
)
self._exit_command_handler: Callable[[Response], None] = (
lambda response: print_func(self._farewell_message)
)
def set_description_message_pattern(self, _: Callable[[str, str], str]) -> None: def set_description_message_pattern(self, _: Callable[[str, str], str]) -> None:
""" """
@@ -208,7 +194,7 @@ class BaseApp:
""" """
input_command_trigger = command.get_trigger() input_command_trigger = command.get_trigger()
if self._ignore_command_register: if self._ignore_command_register:
if input_command_trigger.lower() in self._all_registered_triggers_in_lower: if input_command_trigger.lower() in self._all_registered_triggers_in_lower_case:
return False return False
else: else:
if input_command_trigger in self._all_registered_triggers_in_default_case: if input_command_trigger in self._all_registered_triggers_in_default_case:
@@ -249,7 +235,7 @@ class BaseApp:
def _most_similar_command(self, unknown_command: str) -> str | None: def _most_similar_command(self, unknown_command: str) -> str | None:
all_commands = ( all_commands = (
self._all_registered_triggers_in_lower self._all_registered_triggers_in_lower_case
if self._ignore_command_register if self._ignore_command_register
else self._all_registered_triggers_in_default_case else self._all_registered_triggers_in_default_case
) )
@@ -318,21 +304,22 @@ class BaseApp:
self._setup_system_router() self._setup_system_router()
for router_entity in self._registered_routers: for router_entity in self._registered_routers:
self._all_registered_triggers_in_default_case.extend( self._all_registered_triggers_in_default_case.extend(router_entity.get_triggers())
router_entity.get_triggers() self._all_registered_triggers_in_default_case.extend(router_entity.get_aliases())
)
self._all_registered_triggers_in_default_case.extend(
router_entity.get_aliases()
)
self._all_registered_triggers_in_lower.extend( self._all_registered_triggers_in_lower_case.extend([x.lower() for x in router_entity.get_triggers()])
[x.lower() for x in router_entity.get_triggers()] self._all_registered_triggers_in_lower_case.extend([x.lower() for x in router_entity.get_aliases()])
)
self._all_registered_triggers_in_lower.extend(
[x.lower() for x in router_entity.get_aliases()]
)
self._autocompleter.initial_setup(self._all_registered_triggers_in_lower) self._autocompleter.initial_setup(self._all_registered_triggers_in_lower_case)
if self._ignore_command_register:
for cmd in set(self._all_registered_triggers_in_lower_case):
if self._all_registered_triggers_in_lower_case.count(cmd) != 1:
Console().print(f"\n[b red]WARNING:[/b red] Overlapping trigger or alias: [b blue]{cmd}[/b blue]")
else:
for cmd in set(self._all_registered_triggers_in_default_case):
if self._all_registered_triggers_in_default_case.count(cmd) != 1:
Console().print(f"\n[b red]WARNING:[/b red] Overlapping trigger or alias: [b blue]{cmd}[/b blue]")
if not self._override_system_messages: if not self._override_system_messages:
self._setup_default_view() self._setup_default_view()
@@ -349,10 +336,9 @@ class BaseApp:
class App(BaseApp): class App(BaseApp):
def __init__( def __init__(self,
self,
prompt: str = "What do you want to do?\n", prompt: str = "What do you want to do?\n",
initial_message: str = "\nArgenta\n", initial_message: str = "Argenta\n",
farewell_message: str = "\nSee you\n", farewell_message: str = "\nSee you\n",
exit_command: Command = Command("Q", "Exit command"), exit_command: Command = Command("Q", "Exit command"),
system_router_title: str | None = "System points:", system_router_title: str | None = "System points:",
@@ -361,8 +347,7 @@ class App(BaseApp):
repeat_command_groups: bool = True, repeat_command_groups: bool = True,
override_system_messages: bool = False, override_system_messages: bool = False,
autocompleter: AutoCompleter = AutoCompleter(), autocompleter: AutoCompleter = AutoCompleter(),
print_func: Callable[[str], None] = Console().print, print_func: Callable[[str], None] = Console().print) -> None:
) -> None:
""" """
Public. The essence of the application itself. Public. The essence of the application itself.
Configures and manages all aspects of the behavior and presentation of the user interacting with the user Configures and manages all aspects of the behavior and presentation of the user interacting with the user
@@ -379,8 +364,7 @@ class App(BaseApp):
:param print_func: system messages text output function :param print_func: system messages text output function
:return: None :return: None
""" """
super().__init__( super().__init__(prompt=prompt,
prompt=prompt,
initial_message=initial_message, initial_message=initial_message,
farewell_message=farewell_message, farewell_message=farewell_message,
exit_command=exit_command, exit_command=exit_command,
@@ -390,8 +374,7 @@ class App(BaseApp):
repeat_command_groups=repeat_command_groups, repeat_command_groups=repeat_command_groups,
override_system_messages=override_system_messages, override_system_messages=override_system_messages,
autocompleter=autocompleter, autocompleter=autocompleter,
print_func=print_func, print_func=print_func)
)
def run_polling(self) -> None: def run_polling(self) -> None:
""" """
@@ -420,7 +403,7 @@ class App(BaseApp):
system_router.finds_appropriate_handler(input_command) system_router.finds_appropriate_handler(input_command)
if self._ignore_command_register: if self._ignore_command_register:
self._autocompleter.exit_setup( self._autocompleter.exit_setup(
self._all_registered_triggers_in_lower self._all_registered_triggers_in_lower_case
) )
else: else:
self._autocompleter.exit_setup( self._autocompleter.exit_setup(
+4 -6
View File
@@ -192,14 +192,12 @@ class Router:
pass pass
else: else:
file_path: str = getsourcefile(func) file_path: str = getsourcefile(func)
source_line: int = getsourcelines(func)[1] + 1 source_line: int = getsourcelines(func)[1]
fprint = Console().print fprint = Console().print
fprint( fprint(f'\nFile "{file_path}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint '
f'\nFile "{file_path}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint '
f"of argument([green]{transferred_arg}[/green]) passed to the handler is [/i][bold blue]{Response}[/bold blue]," f"of argument([green]{transferred_arg}[/green]) passed to the handler is [/i][bold blue]{Response}[/bold blue],"
f" [i]but[/i] [bold blue]{arg_annotation}[/bold blue] [i]is specified[/i]\n", f" [i]but[/i] [bold blue]{arg_annotation}[/bold blue] [i]is specified[/i]",
highlight=False, highlight=False)
)
def set_command_register_ignore(self, _: bool) -> None: def set_command_register_ignore(self, _: bool) -> None:
""" """
+2 -2
View File
@@ -33,13 +33,13 @@ class MyTestCase(unittest.TestCase):
def test_is_unknown_command1(self): def test_is_unknown_command1(self):
app = App() app = App()
app.set_unknown_command_handler(lambda command: None) app.set_unknown_command_handler(lambda command: None)
app._all_registered_triggers_in_lower = ['fr', 'tr', 'de'] app._all_registered_triggers_in_lower_case = ['fr', 'tr', 'de']
self.assertEqual(app._is_unknown_command(InputCommand('fr')), False) self.assertEqual(app._is_unknown_command(InputCommand('fr')), False)
def test_is_unknown_command2(self): def test_is_unknown_command2(self):
app = App() app = App()
app.set_unknown_command_handler(lambda command: None) app.set_unknown_command_handler(lambda command: None)
app._all_registered_triggers_in_lower = ['fr', 'tr', 'de'] app._all_registered_triggers_in_lower_case = ['fr', 'tr', 'de']
self.assertEqual(app._is_unknown_command(InputCommand('cr')), True) self.assertEqual(app._is_unknown_command(InputCommand('cr')), True)
def test_is_unknown_command3(self): def test_is_unknown_command3(self):