fix type hints with ty help, add ability to configure stdout capture when handling input by the router

This commit is contained in:
2025-05-20 22:44:47 +03:00
parent 45f410e3e8
commit fd287c5da0
15 changed files with 59 additions and 70 deletions
-1
View File
@@ -58,7 +58,6 @@ if __name__ == '__main__':
# Funktionen in der Entwicklung # Funktionen in der Entwicklung
- Vollständige Unterstützung für Autocompleter unter Linux - Vollständige Unterstützung für Autocompleter unter Linux
- Möglichkeit zur Konfiguration der Standardausgabeerfassung bei der Verarbeitung von Eingaben durch den Handler
## Vollständige [Dokumentation](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id) ## Vollständige [Dokumentation](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
-1
View File
@@ -60,7 +60,6 @@ if __name__ == '__main__':
# Features in development # Features in development
- Full support for autocompleter on Linux - Full support for autocompleter on Linux
- Ability to configure stdout capture when handling input by the handler
## Full [docs](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id) ## Full [docs](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
-1
View File
@@ -58,7 +58,6 @@ if __name__ == '__main__':
# Фичи в разработке # Фичи в разработке
- Полноценная поддержка автокомплитера на Linux - Полноценная поддержка автокомплитера на Linux
- Возможность настройки захвата stdout при обработке хэндлером ввода
## Полная [документация](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id) ## Полная [документация](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
+2 -5
View File
@@ -10,10 +10,7 @@ from argenta.orchestrator import Orchestrator
router = Router() router = Router()
for i in range(10000): @router.command(Command('case are'))
trigger = f"cmd{i}"
@router.command(Command(trigger, aliases=[f'dfs{i}']))
def handler(response: Response): def handler(response: Response):
print(response.status) print(response.status)
@@ -22,7 +19,7 @@ for i in range(10000):
app = App(repeat_command_groups=False) app = App(repeat_command_groups=False)
app.include_router(router) app.include_router(router)
print(get_time_of_pre_cycle_setup(app)) app.run_polling()
+1 -1
View File
@@ -21,7 +21,7 @@ console = Console()
) )
) )
def command_help(response: Response): def command_help(response: Response):
case = input('test') case = input('test > ')
print(case) print(case)
print(response.status) print(response.status)
print(response.undefined_flags.get_flags()) print(response.undefined_flags.get_flags())
+1 -6
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "argenta" name = "argenta"
version = "1.0.5" version = "1.0.6"
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"
@@ -26,8 +26,3 @@ exclude = [
requires = ["hatchling"] requires = ["hatchling"]
build-backend = "hatchling.build" build-backend = "hatchling.build"
[dependency-groups]
dev = [
"pandas>=2.0.3",
]
+1 -1
View File
@@ -5,7 +5,7 @@ from typing import Never
class AutoCompleter: class AutoCompleter:
def __init__( def __init__(
self, history_filename: str = False, autocomplete_button: str = "tab" self, history_filename: str | None = None, autocomplete_button: str = "tab"
) -> None: ) -> None:
""" """
Public. Configures and implements auto-completion of input command Public. Configures and implements auto-completion of input command
+14 -7
View File
@@ -208,12 +208,11 @@ class BaseApp:
:param raw_command: the raw input command :param raw_command: the raw input command
:return: None :return: None
""" """
match error: if isinstance(error, UnprocessedInputFlagException):
case UnprocessedInputFlagException():
self._incorrect_input_syntax_handler(raw_command) self._incorrect_input_syntax_handler(raw_command)
case RepeatedInputFlagsException(): elif isinstance(error, RepeatedInputFlagsException):
self._repeated_input_flags_handler(raw_command) self._repeated_input_flags_handler(raw_command)
case EmptyInputCommandException(): elif isinstance(error, EmptyInputCommandException):
self._empty_input_command_handler() self._empty_input_command_handler()
def _setup_system_router(self) -> None: def _setup_system_router(self) -> None:
@@ -258,11 +257,11 @@ class BaseApp:
""" """
self._prompt = "[italic dim bold]What do you want to do?\n" self._prompt = "[italic dim bold]What do you want to do?\n"
self._initial_message = ( self._initial_message = (
f"\n[bold red]{text2art(self._initial_message, font='tarty1')}\n" "\n"+f"[bold red]{text2art(self._initial_message, font='tarty1')}"+"\n"
) )
self._farewell_message = ( self._farewell_message = (
f"[bold red]\n{text2art(f'\n{self._farewell_message}\n', font='chanky')}[/bold red]\n" "[bold red]\n\n"+text2art(self._farewell_message, font='chanky')+"\n[/bold red]\n"
f"[red i]github.com/koloideal/Argenta[/red i] | [red bold i]made by kolo[/red bold i]\n" "[red i]github.com/koloideal/Argenta[/red i] | [red bold i]made by kolo[/red bold i]\n"
) )
self._description_message_gen = lambda command, description: ( self._description_message_gen = lambda command, description: (
f"[bold red]{escape('[' + command + ']')}[/bold red] " f"[bold red]{escape('[' + command + ']')}[/bold red] "
@@ -425,11 +424,19 @@ class App(BaseApp):
for registered_router in self._registered_routers: for registered_router in self._registered_routers:
if registered_router.disable_redirect_stdout: if registered_router.disable_redirect_stdout:
if isinstance(self._dividing_line, StaticDividingLine):
self._print_func(self._dividing_line.get_full_static_line(self._override_system_messages))
registered_router.finds_appropriate_handler(input_command) registered_router.finds_appropriate_handler(input_command)
self._print_func(self._dividing_line.get_full_static_line(self._override_system_messages))
else:
self._print_func(StaticDividingLine(self._dividing_line.get_unit_part()).get_full_static_line(self._override_system_messages))
registered_router.finds_appropriate_handler(input_command)
self._print_func(StaticDividingLine(self._dividing_line.get_unit_part()).get_full_static_line(self._override_system_messages))
else: else:
with redirect_stdout(io.StringIO()) as f: with redirect_stdout(io.StringIO()) as f:
registered_router.finds_appropriate_handler(input_command) registered_router.finds_appropriate_handler(input_command)
res: str = f.getvalue() res: str = f.getvalue()
if res:
self._print_framed_text(res) self._print_framed_text(res)
def include_router(self, router: Router) -> None: def include_router(self, router: Router) -> None:
+1 -1
View File
@@ -4,7 +4,7 @@ from argenta.router import Router
class RegisteredRouters: class RegisteredRouters:
def __init__(self, registered_routers: list[Router] = None) -> None: def __init__(self, registered_routers: list[Router] | None = None) -> None:
""" """
Private. Combines registered routers Private. Combines registered routers
:param registered_routers: list of the registered routers :param registered_routers: list of the registered routers
+1 -1
View File
@@ -87,7 +87,7 @@ class Flag(BaseFlag):
class InputFlag(BaseFlag): class InputFlag(BaseFlag):
def __init__( def __init__(
self, name: str, prefix: Literal["-", "--", "---"] = "--", value: str = None self, name: str, prefix: Literal["-", "--", "---"] = "--", value: str | None = None
): ):
""" """
Public. The entity of the flag of the entered command Public. The entity of the flag of the entered command
+7 -10
View File
@@ -5,10 +5,7 @@ from argenta.command.exceptions import (
RepeatedInputFlagsException, RepeatedInputFlagsException,
EmptyInputCommandException, EmptyInputCommandException,
) )
from typing import Generic, TypeVar, cast, Literal from typing import cast, Literal
InputCommandType = TypeVar("InputCommandType")
class BaseCommand: class BaseCommand:
@@ -31,9 +28,9 @@ class Command(BaseCommand):
def __init__( def __init__(
self, self,
trigger: str, trigger: str,
description: str = None, description: str | None = None,
flags: Flag | Flags = None, flags: Flag | Flags | None = None,
aliases: list[str] = None, aliases: list[str] | None = None,
): ):
""" """
Public. The command that can and should be registered in the Router Public. The command that can and should be registered in the Router
@@ -109,8 +106,8 @@ class Command(BaseCommand):
return self._description return self._description
class InputCommand(BaseCommand, Generic[InputCommandType]): class InputCommand(BaseCommand):
def __init__(self, trigger: str, input_flags: InputFlag | InputFlags = None): def __init__(self, trigger: str, input_flags: InputFlag | InputFlags | None = None):
""" """
Private. The model of the input command, after parsing Private. The model of the input command, after parsing
:param trigger:the trigger of the command :param trigger:the trigger of the command
@@ -142,7 +139,7 @@ class InputCommand(BaseCommand, Generic[InputCommandType]):
return self._input_flags return self._input_flags
@staticmethod @staticmethod
def parse(raw_command: str) -> InputCommandType: def parse(raw_command: str) -> 'InputCommand':
""" """
Private. Parse the raw input command Private. Parse the raw input command
:param raw_command: raw input command :param raw_command: raw input command
+2 -2
View File
@@ -5,13 +5,13 @@ from argenta.orchestrator.argparser import ArgParser
class Orchestrator: class Orchestrator:
def __init__(self, arg_parser: ArgParser = False): def __init__(self, arg_parser: ArgParser | None = None):
""" """
Public. An orchestrator and configurator that defines the behavior of an integrated system, one level higher than the App Public. An orchestrator and configurator that defines the behavior of an integrated system, one level higher than the App
:param arg_parser: Cmd argument parser and configurator at startup :param arg_parser: Cmd argument parser and configurator at startup
:return: None :return: None
""" """
self.arg_parser: ArgParser | False = arg_parser self.arg_parser: ArgParser | None = arg_parser
if arg_parser: if arg_parser:
self.arg_parser.register_args() self.arg_parser.register_args()
+1 -1
View File
@@ -11,7 +11,7 @@ class Response:
def __init__( def __init__(
self, self,
status: Status = None, status: Status | None = None,
valid_flags: ValidInputFlags = ValidInputFlags(), valid_flags: ValidInputFlags = ValidInputFlags(),
undefined_flags: UndefinedInputFlags = UndefinedInputFlags(), undefined_flags: UndefinedInputFlags = UndefinedInputFlags(),
invalid_value_flags: InvalidValueInputFlags = InvalidValueInputFlags(), invalid_value_flags: InvalidValueInputFlags = InvalidValueInputFlags(),
+1 -1
View File
@@ -38,7 +38,7 @@ class CommandHandler:
class CommandHandlers: class CommandHandlers:
def __init__(self, command_handlers: list[CommandHandler] = None): def __init__(self, command_handlers: list[CommandHandler] | None = None):
""" """
Private. The model that unites all CommandHandler of the routers Private. The model that unites all CommandHandler of the routers
:param command_handlers: list of CommandHandlers for register :param command_handlers: list of CommandHandlers for register
+9 -13
View File
@@ -45,13 +45,15 @@ class Router:
:param command: Registered command :param command: Registered command
:return: decorated handler as Callable :return: decorated handler as Callable
""" """
self._validate_command(command)
if isinstance(command, str): if isinstance(command, str):
command = Command(command) redefined_command = Command(command)
else:
redefined_command = command
self._validate_command(redefined_command)
def command_decorator(func): def command_decorator(func):
Router._validate_func_args(func) Router._validate_func_args(func)
self._command_handlers.add_handler(CommandHandler(func, command)) self._command_handlers.add_handler(CommandHandler(func, redefined_command))
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return func(*args, **kwargs) return func(*args, **kwargs)
@@ -123,12 +125,11 @@ class Router:
flag_status: Literal["Undefined", "Valid", "Invalid"] = ( flag_status: Literal["Undefined", "Valid", "Invalid"] = (
handled_command.validate_input_flag(flag) handled_command.validate_input_flag(flag)
) )
match flag_status: if flag_status == "Valid":
case "Valid":
valid_input_flags.add_flag(flag) valid_input_flags.add_flag(flag)
case "Undefined": elif flag_status == "Undefined":
undefined_input_flags.add_flag(flag) undefined_input_flags.add_flag(flag)
case "Invalid": elif flag_status == "Invalid":
invalid_value_input_flags.add_flag(flag) invalid_value_input_flags.add_flag(flag)
if ( if (
@@ -163,8 +164,6 @@ class Router:
:param command: validated command :param command: validated command
:return: None if command is valid else raise exception :return: None if command is valid else raise exception
""" """
match type(command).__name__:
case "Command":
command_name: str = command.get_trigger() command_name: str = command.get_trigger()
if command_name.find(" ") != -1: if command_name.find(" ") != -1:
raise TriggerContainSpacesException() raise TriggerContainSpacesException()
@@ -173,9 +172,6 @@ class Router:
flags_name: list = [x.get_string_entity().lower() for x in flags] flags_name: list = [x.get_string_entity().lower() for x in flags]
if len(set(flags_name)) < len(flags_name): if len(set(flags_name)) < len(flags_name):
raise RepeatedFlagNameException() raise RepeatedFlagNameException()
case "str":
if command.find(" ") != -1:
raise TriggerContainSpacesException()
@staticmethod @staticmethod
def _validate_func_args(func: Callable) -> None: def _validate_func_args(func: Callable) -> None:
@@ -197,7 +193,7 @@ class Router:
if arg_annotation is Response: if arg_annotation is Response:
pass pass
else: else:
file_path: str = getsourcefile(func) file_path: str | None = getsourcefile(func)
source_line: int = getsourcelines(func)[1] source_line: int = getsourcelines(func)[1]
fprint = Console().print fprint = Console().print
fprint(f'\nFile "{file_path}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint ' fprint(f'\nFile "{file_path}", line {source_line}\n[b red]WARNING:[/b red] [i]The typehint '