4 Commits

Author SHA1 Message Date
kolo b4b7d5442c v0.2.1 2025-02-09 22:47:53 +03:00
kolo 684760121c some fix 2025-02-09 13:42:06 +03:00
kolo dfc3c472ce v0.2.0 2025-02-09 13:41:11 +03:00
kolo 8a3f742636 v0.2.0 2025-02-09 02:37:44 +03:00
10 changed files with 222 additions and 73 deletions
+105 -30
View File
@@ -5,40 +5,61 @@ from .exceptions import (InvalidRouterInstanceException,
OnlyOneMainRouterIsAllowedException, OnlyOneMainRouterIsAllowedException,
MissingMainRouterException, MissingMainRouterException,
MissingHandlersForUnknownCommandsOnMainRouterException, MissingHandlersForUnknownCommandsOnMainRouterException,
HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException) HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException,
NoRegisteredRoutersException,
NoRegisteredHandlersException,
RepeatedCommandInDifferentRoutersException)
class App: class App:
def __init__(self, def __init__(self,
prompt: str = 'Enter a command', prompt: str = 'Enter a command',
exit_command: str = 'q',
ignore_exit_command_register: bool = True,
initial_greeting: str = '\nHello, I am Argenta\n', initial_greeting: str = '\nHello, I am Argenta\n',
farewell_message: str = 'GoodBye', farewell_message: str = '\nGoodBye\n',
exit_command: str = 'Q',
exit_command_description: str = 'Exit command',
exit_command_title: str = 'System points:',
ignore_exit_command_register: bool = True,
ignore_command_register: bool = False,
line_separate: str = '', line_separate: str = '',
command_group_description_separate: str = '', command_group_description_separate: str = '',
repeat_command_groups: bool = True,
print_func: Callable[[str], None] = print) -> None: print_func: Callable[[str], None] = print) -> 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
self.exit_command_description = exit_command_description
self.exit_command_title = exit_command_title
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_greeting = initial_greeting self.initial_greeting = initial_greeting
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.repeat_command_groups = repeat_command_groups
self._routers: list[Router] = [] self._routers: list[Router] = []
self._registered_commands: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = [] self._registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = []
self._main_app_router: Router | None = None self._app_main_router: Router | None = None
self._description_message_pattern: str = '[{command}] *=*=* {description}' self._description_message_pattern: str = '[{command}] *=*=* {description}'
def start_polling(self) -> None: def start_polling(self) -> None:
self.print_func(self.initial_greeting) self._validate_number_of_routers()
self._validate_included_routers()
self._validate_main_router() self._validate_main_router()
self._validate_all_router_commands()
self.print_func(self.initial_greeting)
if not self.repeat_command_groups:
self._print_command_group_description()
if self.repeat_command_groups:
self.print_func(self.prompt)
while True: while True:
self._print_command_group_description() if self.repeat_command_groups:
self._print_command_group_description()
self.print_func(self.prompt) self.print_func(self.prompt)
command: str = input() command: str = input()
@@ -47,12 +68,12 @@ class App:
self.print_func(self.line_separate) self.print_func(self.line_separate)
is_unknown_command: bool = self._check_is_command_unknown(command) is_unknown_command: bool = self._check_is_command_unknown(command)
if is_unknown_command: if is_unknown_command:
continue continue
for router in self._routers: for router in self._routers:
router.input_command_handler(command) router.input_command_handler(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)
@@ -74,18 +95,62 @@ class App:
self._description_message_pattern: str = pattern self._description_message_pattern: str = pattern
def _validate_main_router(self): def get_main_router(self) -> Router:
if not self._main_app_router: return self._app_main_router
raise MissingMainRouterException()
if not self._main_app_router.unknown_command_func:
def get_all_app_commands(self) -> list[str]:
all_commands: list[str] = []
for router in self._routers:
all_commands.extend(router.get_all_commands())
return all_commands
def _validate_number_of_routers(self) -> None:
if not self._routers:
raise NoRegisteredRoutersException()
def _validate_included_routers(self) -> None:
for router in self._routers:
if not router.get_command_entities():
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 MissingHandlersForUnknownCommandsOnMainRouterException() raise MissingHandlersForUnknownCommandsOnMainRouterException()
for router in self._routers: for router in self._routers:
if router.unknown_command_func and self._main_app_router is not router: if router.unknown_command_func and self._app_main_router is not router:
raise HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException() raise HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException()
def _validate_all_router_commands(self) -> None:
for idx in range(len(self._registered_router_entities)):
current_router: Router = self._registered_router_entities[idx]['entity']
routers_without_current_router = self._registered_router_entities.copy()
routers_without_current_router.pop(idx)
current_router_all_commands: list[str] = current_router.get_all_commands()
for router_entity in routers_without_current_router:
if len(set(current_router_all_commands).intersection(set(router_entity['entity'].get_all_commands()))) > 0:
raise RepeatedCommandInDifferentRoutersException()
if self.ignore_command_register:
if len(set([x.lower() for x in current_router_all_commands]).intersection(set([x.lower() for x in router_entity['entity'].get_all_commands()]))) > 0:
raise RepeatedCommandInDifferentRoutersException()
def _checking_command_for_exit_command(self, command: str): def _checking_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:
@@ -98,25 +163,25 @@ class App:
def _check_is_command_unknown(self, command: str): def _check_is_command_unknown(self, command: str):
registered_commands: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = self._registered_commands registered_router_entities: list[dict[str, str | list[dict[str, Callable[[], None] | str]] | Router]] = self._registered_router_entities
for router in registered_commands: for router_entity in registered_router_entities:
for command_entity in router['commands']: for command_entity in router_entity['commands']:
if command_entity['command'].lower() == command.lower(): if command_entity['command'].lower() == command.lower():
if router['router'].ignore_command_register: if self.ignore_command_register:
return False return False
else: else:
if command_entity['command'] == command: if command_entity['command'] == command:
return False return False
self._main_app_router.unknown_command_handler(command) self._app_main_router.unknown_command_handler(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)
return True return True
def _print_command_group_description(self): def _print_command_group_description(self):
for router in self._registered_commands: for router_entity in self._registered_router_entities:
self.print_func(router['name']) self.print_func(router_entity['title'])
for command_entity in router['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'],
description=command_entity['description'] description=command_entity['description']
@@ -124,22 +189,32 @@ class App:
) )
self.print_func(self.command_group_description_separate) self.print_func(self.command_group_description_separate)
self.print_func(self.exit_command_title)
self.print_func(self._description_message_pattern.format(
command=self.exit_command,
description=self.exit_command_description
)
)
self.print_func(self.command_group_description_separate)
def include_router(self, router: Router, is_main: bool = False) -> None:
def include_router(self, router: Router, is_main: True | False = False) -> None:
if not isinstance(router, Router): if not isinstance(router, Router):
raise InvalidRouterInstanceException() raise InvalidRouterInstanceException()
if is_main: if is_main:
if not self._main_app_router: if not self._app_main_router:
self._main_app_router = router self._app_main_router = router
router.set_router_as_main() router.set_router_as_main()
else: else:
raise OnlyOneMainRouterIsAllowedException(router) raise OnlyOneMainRouterIsAllowedException(self._app_main_router.get_name())
router.set_ignore_command_register(self.ignore_command_register)
self._routers.append(router) self._routers.append(router)
registered_commands: list[dict[str, Callable[[], None] | str]] = router.get_registered_commands() command_entities: list[dict[str, Callable[[], None] | str]] = router.get_command_entities()
self._registered_commands.append({'name': router.get_name(), self._registered_router_entities.append({'name': router.get_name(),
'router': router, 'title': router.get_title(),
'commands': registered_commands}) 'entity': router,
'commands': command_entities})
+17
View File
@@ -37,3 +37,20 @@ class MissingHandlersForUnknownCommandsOnMainRouterException(Exception):
class HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException(Exception): class HandlerForUnknownCommandsCanOnlyBeDeclaredForMainRouterException(Exception):
def __str__(self): def __str__(self):
return '\nThe handler for unknown commands can only be declared for the main router' return '\nThe handler for unknown commands can only be declared for the main router'
class NoRegisteredRoutersException(Exception):
def __str__(self):
return "No Registered Router Found"
class NoRegisteredHandlersException(Exception):
def __init__(self, router_name):
self.router_name = router_name
def __str__(self):
return f"No Registered Handlers Found For '{self.router_name}'"
class RepeatedCommandInDifferentRoutersException(Exception):
def __str__(self):
return "Commands in different handlers cannot be repeated"
+76 -24
View File
@@ -1,36 +1,37 @@
from typing import Callable, Any from typing import Callable, Any
from ..router.exceptions import (InvalidCommandInstanceException, from ..router.exceptions import (InvalidCommandInstanceException,
UnknownCommandHandlerHasAlreadyBeenCreatedException, UnknownCommandHandlerHasAlreadyBeenCreatedException,
InvalidDescriptionInstanceException) InvalidDescriptionInstanceException,
RepeatedCommandException)
class Router: class Router:
def __init__(self, def __init__(self,
name: str, title: str = 'Commands group title:',
ignore_command_register: bool = False): name: str = 'subordinate'):
self.ignore_command_register = ignore_command_register self.title = title
self.name = name self.name = name
self._processed_commands: list[dict[str, Callable[[], None] | str]] = [] self._command_entities: list[dict[str, Callable[[], None] | str]] = []
self.unknown_command_func: Callable[[str], None] | None = None self.unknown_command_func: Callable[[str], None] | None = None
self._is_main_router: bool = False self._is_main_router: bool = False
self.ignore_command_register: bool = False
def command(self, command: str, description: str) -> Callable[[Any], Any]: def command(self, command: str, description: str = None) -> Callable[[Any], Any]:
if not isinstance(command, str): processed_description = Router._validate_description(command, description)
raise InvalidCommandInstanceException() self._validate_command(command)
if not isinstance(description, str):
raise InvalidDescriptionInstanceException() def command_decorator(func):
else: self._command_entities.append({'handler_func': func,
def command_decorator(func): 'command': command,
self._processed_commands.append({'func': func, 'description': processed_description})
'command': command, def wrapper(*args, **kwargs):
'description': description}) return func(*args, **kwargs)
def wrapper(*args, **kwargs): return wrapper
return func(*args, **kwargs)
return wrapper return command_decorator
return command_decorator
def unknown_command(self, func): def unknown_command(self, func):
@@ -45,27 +46,78 @@ class Router:
def input_command_handler(self, input_command): def input_command_handler(self, input_command):
for command_entity in self._processed_commands: for command_entity in self._command_entities:
if input_command.lower() == command_entity['command'].lower(): if input_command.lower() == command_entity['command'].lower():
if self.ignore_command_register: if self.ignore_command_register:
return command_entity['func']() return command_entity['handler_func']()
else: else:
if input_command == command_entity['command']: if input_command == command_entity['command']:
return command_entity['func']() return command_entity['handler_func']()
def unknown_command_handler(self, unknown_command): def unknown_command_handler(self, unknown_command):
self.unknown_command_func(unknown_command) self.unknown_command_func(unknown_command)
def _validate_command(self, command: str):
if not isinstance(command, str):
raise InvalidCommandInstanceException()
if command in self.get_all_commands():
raise RepeatedCommandException()
if self.ignore_command_register:
if command.lower() in [x.lower() for x in self.get_all_commands()]:
raise RepeatedCommandException()
@staticmethod
def _validate_description(command: str, description: str):
if not isinstance(description, str):
if description is None:
description = f'description for "{command}" command'
else:
raise InvalidDescriptionInstanceException()
return description
def set_router_as_main(self): def set_router_as_main(self):
if self.name == 'subordinate':
self.name = 'main'
self._is_main_router = True self._is_main_router = True
def get_registered_commands(self) -> list[dict[str, Callable[[], None] | str]]: def set_ignore_command_register(self, ignore_command_register: bool):
return self._processed_commands self.ignore_command_register = ignore_command_register
def get_command_entities(self) -> list[dict[str, Callable[[], None] | str]]:
return self._command_entities
def get_name(self) -> str: def get_name(self) -> str:
return self.name return self.name
def get_title(self) -> str:
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]:
all_commands: list[str] = []
for command_entity in self._command_entities:
all_commands.append(command_entity['command'])
return all_commands
+5
View File
@@ -11,3 +11,8 @@ class InvalidDescriptionInstanceException(Exception):
class UnknownCommandHandlerHasAlreadyBeenCreatedException(Exception): class UnknownCommandHandlerHasAlreadyBeenCreatedException(Exception):
def __str__(self): def __str__(self):
return "Only one unknown command handler can be declared" return "Only one unknown command handler can be declared"
class RepeatedCommandException(Exception):
def __str__(self):
return "Commands in handler cannot be repeated"
+1 -1
View File
@@ -1,6 +1,6 @@
[project] [project]
name = "argenta" name = "argenta"
version = "0.1.3" version = "0.2.1"
description = "python library for creating cli apps" description = "python library for creating cli apps"
authors = [ authors = [
{name = "kolo",email = "kolo.is.main@gmail.com"} {name = "kolo",email = "kolo.is.main@gmail.com"}
+1 -1
View File
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
setup( setup(
name="argenta", name="argenta",
version="0.1.3", version="0.2.1",
author="kolo", author="kolo",
author_email="kolo.is.main@gmail.com", author_email="kolo.is.main@gmail.com",
description="Python library for creating CLI apps", description="Python library for creating CLI apps",
+2 -4
View File
@@ -6,10 +6,8 @@ from ..handlers.handlers_implementation.solving_command import start_solving_com
from ..handlers.handlers_implementation.upgrade_command import upgrade_command from ..handlers.handlers_implementation.upgrade_command import upgrade_command
work_router: Router = Router(name='Work points:', work_router: Router = Router(title='Work points:')
ignore_command_register=False) settings_router: Router = Router(title='Settings points:')
settings_router: Router = Router(name='Settings points:',
ignore_command_register=True)
console = Console() console = Console()
+2 -1
View File
@@ -7,7 +7,8 @@ from rich.console import Console
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='[bold green]\n---------------------------------------------\n',
print_func=Console().print, print_func=Console().print,
command_group_description_separate='') command_group_description_separate='',
repeat_command_groups=False)
def main(): def main():
+10 -11
View File
@@ -1,28 +1,27 @@
from pprint import pprint
from rich.console import Console from rich.console import Console
from argenta.router import Router from argenta.router import Router
work_router: Router = Router(name='Work points:', work_router: Router = Router(name='work')
ignore_command_register=False) settings_router: Router = Router(name='settings')
settings_router: Router = Router(name='Settings points:',
ignore_command_register=True)
console = Console() console = Console()
@work_router.command(command='0', description='Get Help') @work_router.command(command='a')
def command_help(): def command_help():
print('help command') console.print('[bold red]command help [/bold red]')
@work_router.command(command='1', description='Start Solving') @work_router.command(command='B', description='tester')
def command_start_solving(): def command_start_solving():
print('start solving') console.print('[bold red]command start [/bold red]')
@settings_router.command(command='U', description='Update WordMath') @settings_router.command(command='b')
def command_update(): def command_settings():
print('update wordmath') console.print('[bold red]command settings [/bold red]')
@work_router.unknown_command @work_router.unknown_command
+3 -1
View File
@@ -1,9 +1,11 @@
from pprint import pprint
from tests.mock_default_app.handlers.routers import work_router, settings_router from tests.mock_default_app.handlers.routers import work_router, settings_router
from argenta.app.entity import App from argenta.app.entity import App
from art import text2art from art import text2art
app: App = App() app: App = App(ignore_command_register=False,
line_separate='\n-------------------------------\n')
def main(): def main():