diff --git a/metrics/benchmarks/core/__init__.py b/metrics/benchmarks/core/__init__.py index 64424de..bddae4e 100644 --- a/metrics/benchmarks/core/__init__.py +++ b/metrics/benchmarks/core/__init__.py @@ -1 +1,4 @@ -from .pre_cycle_setup import * \ No newline at end of file +from .pre_cycle_setup import * +from .validate_routers_for_collisions import * +from .most_similar_command import * +from .finds_appropriate_handler import * \ No newline at end of file diff --git a/metrics/benchmarks/core/finds_appropriate_handler.py b/metrics/benchmarks/core/finds_appropriate_handler.py new file mode 100644 index 0000000..9c22ae4 --- /dev/null +++ b/metrics/benchmarks/core/finds_appropriate_handler.py @@ -0,0 +1,81 @@ +__all__ = [ + "benchmark_simple_command", + "benchmark_command_with_flags", + "benchmark_many_commands", + "benchmark_command_with_many_flags", + "benchmark_extreme_router" +] + +from argenta.command.models import Command, InputCommand +from argenta.command import Flag, Flags +from argenta.response import Response +from argenta.router import Router + +from ..models import benchmark +from ..utils import get_time_of_finds_appropriate_handler + + +@benchmark(type_="finds_appropriate_handler", description="Simple command (no flags)") +def benchmark_simple_command() -> float: + router = Router() + + @router.command(Command('test')) + def handler(_res: Response) -> None: + pass + + input_cmd = InputCommand.parse('test') + return get_time_of_finds_appropriate_handler(router, input_cmd) + + +@benchmark(type_="finds_appropriate_handler", description="Command with flags (3 flags)") +def benchmark_command_with_flags() -> float: + router = Router() + + @router.command(Command('test', flags=Flags([Flag('a'), Flag('b'), Flag('c')]))) + def handler(_res: Response) -> None: + pass + + input_cmd = InputCommand.parse('test -a -b -c') + return get_time_of_finds_appropriate_handler(router, input_cmd) + + +@benchmark(type_="finds_appropriate_handler", description="Many commands (50 commands)") +def benchmark_many_commands() -> float: + router = Router() + + for i in range(50): + @router.command(Command(f'cmd{i}')) + def handler(_res: Response) -> None: + pass + + input_cmd = InputCommand.parse('cmd25') + return get_time_of_finds_appropriate_handler(router, input_cmd) + + +@benchmark(type_="finds_appropriate_handler", description="Command with many flags (20 flags)") +def benchmark_command_with_many_flags() -> float: + router = Router() + + flags = Flags([Flag(f'flag{i}') for i in range(20)]) + + @router.command(Command('test', flags=flags)) + def handler(_res: Response) -> None: + pass + + input_cmd = InputCommand.parse('test ' + ' '.join(f'-flag{i}' for i in range(10))) + return get_time_of_finds_appropriate_handler(router, input_cmd) + + +@benchmark(type_="finds_appropriate_handler", description="Extreme (100 commands, 10 flags each)") +def benchmark_extreme_router() -> float: + router = Router() + + for i in range(100): + flags = Flags([Flag(f'f{i}_{j}') for j in range(10)]) + + @router.command(Command(f'cmd{i}', flags=flags)) + def handler(_res: Response) -> None: + pass + + input_cmd = InputCommand.parse('cmd50 -f50_0 -f50_1 -f50_2') + return get_time_of_finds_appropriate_handler(router, input_cmd) diff --git a/metrics/benchmarks/core/most_similar_command.py b/metrics/benchmarks/core/most_similar_command.py new file mode 100644 index 0000000..ffe513c --- /dev/null +++ b/metrics/benchmarks/core/most_similar_command.py @@ -0,0 +1,65 @@ +__all__ = [ + "benchmark_few_commands", + "benchmark_many_commands", + "benchmark_many_aliases", + "benchmark_partial_match", + "benchmark_extreme_commands" +] + +import io +from contextlib import redirect_stdout + +from argenta import App +from argenta.command.models import Command +from argenta.response import Response +from argenta.router import Router + +from ..models import benchmark +from ..utils import get_time_of_most_similar_command + + +def setup_app_with_commands(command_count: int, aliases_per_command: int = 0) -> App: + app = App(override_system_messages=True) + router = Router() + + for i in range(command_count): + aliases = {f'alias{i}_{j}' for j in range(aliases_per_command)} if aliases_per_command else set() + + @router.command(Command(f'command{i}', aliases=aliases)) + def handler(_res: Response) -> None: + pass + + app.include_router(router) + with redirect_stdout(io.StringIO()): + app._pre_cycle_setup() # pyright: ignore[reportPrivateUsage] + return app + + +@benchmark(type_="most_similar_command", description="Few commands (10 commands, no match)") +def benchmark_few_commands() -> float: + app = setup_app_with_commands(10) + return get_time_of_most_similar_command(app, "unknown") + + +@benchmark(type_="most_similar_command", description="Many commands (50 commands, no match)") +def benchmark_many_commands() -> float: + app = setup_app_with_commands(50) + return get_time_of_most_similar_command(app, "unknown") + + +@benchmark(type_="most_similar_command", description="Many aliases (20 commands, 10 aliases each)") +def benchmark_many_aliases() -> float: + app = setup_app_with_commands(20, aliases_per_command=10) + return get_time_of_most_similar_command(app, "unknown") + + +@benchmark(type_="most_similar_command", description="Partial match (50 commands, prefix match)") +def benchmark_partial_match() -> float: + app = setup_app_with_commands(50) + return get_time_of_most_similar_command(app, "comm") + + +@benchmark(type_="most_similar_command", description="Extreme (100 commands, 20 aliases each)") +def benchmark_extreme_commands() -> float: + app = setup_app_with_commands(100, aliases_per_command=20) + return get_time_of_most_similar_command(app, "comm") diff --git a/metrics/benchmarks/core/validate_routers_for_collisions.py b/metrics/benchmarks/core/validate_routers_for_collisions.py new file mode 100644 index 0000000..65c8c1d --- /dev/null +++ b/metrics/benchmarks/core/validate_routers_for_collisions.py @@ -0,0 +1,99 @@ +__all__ = [ + "benchmark_few_routers", + "benchmark_many_routers", + "benchmark_many_commands_per_router", + "benchmark_many_aliases_per_command", + "benchmark_extreme_routers" +] + +from argenta import App +from argenta.command.models import Command +from argenta.response import Response +from argenta.router import Router + +from ..utils import get_time_of_validate_routers_for_collisions + +from ..models import benchmark + + +@benchmark(type_="validate_routers_for_collisions", description="With few routers (3 routers, 1 command each)") +def benchmark_few_routers() -> float: + app = App(override_system_messages=True) + + for i in range(3): + router = Router() + + @router.command(Command(f'cmd{i}')) + def handler(_res: Response) -> None: + pass + + app.include_router(router) + + return get_time_of_validate_routers_for_collisions(app) + + +@benchmark(type_="validate_routers_for_collisions", description="With many routers (10 routers, 1 command each)") +def benchmark_many_routers() -> float: + app = App(override_system_messages=True) + + for i in range(10): + router = Router() + + @router.command(Command(f'cmd{i}')) + def handler(_res: Response) -> None: + pass + + app.include_router(router) + + return get_time_of_validate_routers_for_collisions(app) + + +@benchmark(type_="validate_routers_for_collisions", description="With many commands per router (3 routers, 10 commands each)") +def benchmark_many_commands_per_router() -> float: + app = App(override_system_messages=True) + + for i in range(3): + router = Router() + + for j in range(10): + @router.command(Command(f'cmd{i}_{j}')) + def handler(_res: Response) -> None: + pass + + app.include_router(router) + + return get_time_of_validate_routers_for_collisions(app) + + +@benchmark(type_="validate_routers_for_collisions", description="With many aliases (3 routers, 5 commands, 10 aliases each)") +def benchmark_many_aliases_per_command() -> float: + app = App(override_system_messages=True) + + for i in range(3): + router = Router() + + for j in range(5): + @router.command(Command(f'cmd{i}_{j}', aliases={f'alias{i}_{j}_{k}' for k in range(10)})) + def handler(_res: Response) -> None: + pass + + app.include_router(router) + + return get_time_of_validate_routers_for_collisions(app) + + +@benchmark(type_="validate_routers_for_collisions", description="Extreme (20 routers, 10 commands, 20 aliases each)") +def benchmark_extreme_routers() -> float: + app = App(override_system_messages=True) + + for i in range(20): + router = Router() + + for j in range(10): + @router.command(Command(f'cmd{i}_{j}', aliases={f'alias{i}_{j}_{k}' for k in range(20)})) + def handler(_res: Response) -> None: + pass + + app.include_router(router) + + return get_time_of_validate_routers_for_collisions(app) diff --git a/metrics/benchmarks/utils.py b/metrics/benchmarks/utils.py index 5ad0f78..bb10000 100644 --- a/metrics/benchmarks/utils.py +++ b/metrics/benchmarks/utils.py @@ -1,5 +1,8 @@ __all__ = [ "get_time_of_pre_cycle_setup", + "get_time_of_validate_routers_for_collisions", + "get_time_of_most_similar_command", + "get_time_of_finds_appropriate_handler", "attempts_to_average", "run_benchmark", "run_all_benchmarks", @@ -16,6 +19,8 @@ from contextlib import redirect_stdout from decimal import ROUND_HALF_UP, Decimal from argenta import App +from argenta.router import Router +from argenta.command.models import InputCommand from .models import Benchmark, BenchmarkResult, Benchmarks @@ -26,6 +31,30 @@ def get_time_of_pre_cycle_setup(app: App) -> float: end = time.perf_counter() return (end - start) * 1000 # as milliseconds +def get_time_of_validate_routers_for_collisions(app: App) -> float: + app._setup_system_router() # pyright: ignore[reportPrivateUsage] + start = time.perf_counter() + with redirect_stdout(io.StringIO()): + app._validate_routers_for_collisions() # pyright: ignore[reportPrivateUsage] + end = time.perf_counter() + return (end - start) * 1000 + + +def get_time_of_most_similar_command(app: App, unknown_command: str) -> float: + start = time.perf_counter() + with redirect_stdout(io.StringIO()): + app._most_similar_command(unknown_command) # pyright: ignore[reportPrivateUsage] + end = time.perf_counter() + return (end - start) * 1000 + + +def get_time_of_finds_appropriate_handler(router: "Router", input_command: "InputCommand") -> float: + start = time.perf_counter() + with redirect_stdout(io.StringIO()): + router.finds_appropriate_handler(input_command) + end = time.perf_counter() + return (end - start) * 1000 + def get_kernel_version() -> dict[str, str]: system = platform.system() diff --git a/src/argenta/app/models.py b/src/argenta/app/models.py index 2bf37f8..5f03f88 100644 --- a/src/argenta/app/models.py +++ b/src/argenta/app/models.py @@ -168,9 +168,9 @@ class BaseApp: max_length_line = max([len(line) for line in clear_text.split("\n")]) max_length_line = ( max_length_line - if 10 <= max_length_line <= 80 - else 80 - if max_length_line > 80 + if 10 <= max_length_line <= 100 + else 100 + if max_length_line > 100 else 10 ) @@ -306,7 +306,7 @@ class BaseApp: Private. Sets up default app view :return: None """ - self._prompt = f"[italic dim bold]{self._prompt}" + self._prompt = f"[italic dim bold]{self._prompt}[/italic dim bold]" self._initial_message = ( "\n" + f"[bold red]{text2art(self._initial_message, font='tarty1')}" + "\n" )