Add ExecutionContext.signature, fix partial command matching with arguments, fix passing args to Falyx._create_context helper
This commit is contained in:
parent
ac82076511
commit
b24079ea7e
|
@ -70,7 +70,7 @@ class ExecutionContext(BaseModel):
|
|||
|
||||
name: str
|
||||
args: tuple = ()
|
||||
kwargs: dict = {}
|
||||
kwargs: dict = Field(default_factory=dict)
|
||||
action: Any
|
||||
result: Any | None = None
|
||||
exception: Exception | None = None
|
||||
|
@ -120,6 +120,17 @@ class ExecutionContext(BaseModel):
|
|||
def status(self) -> str:
|
||||
return "OK" if self.success else "ERROR"
|
||||
|
||||
@property
|
||||
def signature(self) -> str:
|
||||
"""
|
||||
Returns a string representation of the action signature, including
|
||||
its name and arguments.
|
||||
"""
|
||||
args = ", ".join(map(repr, self.args))
|
||||
kwargs = ", ".join(f"{key}={value!r}" for key, value in self.kwargs.items())
|
||||
signature = ", ".join(filter(None, [args, kwargs]))
|
||||
return f"{self.name} ({signature})"
|
||||
|
||||
def as_dict(self) -> dict:
|
||||
return {
|
||||
"name": self.name,
|
||||
|
|
|
@ -8,7 +8,7 @@ from falyx.logger import logger
|
|||
def log_before(context: ExecutionContext):
|
||||
"""Log the start of an action."""
|
||||
args = ", ".join(map(repr, context.args))
|
||||
kwargs = ", ".join(f"{k}={v!r}" for k, v in context.kwargs.items())
|
||||
kwargs = ", ".join(f"{key}={value!r}" for key, value in context.kwargs.items())
|
||||
signature = ", ".join(filter(None, [args, kwargs]))
|
||||
logger.info("[%s] Starting -> %s(%s)", context.name, context.action, signature)
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ from __future__ import annotations
|
|||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from threading import Lock
|
||||
from typing import Any, Literal
|
||||
from typing import Literal
|
||||
|
||||
from rich import box
|
||||
from rich.console import Console
|
||||
|
@ -111,8 +111,8 @@ class ExecutionRegistry:
|
|||
def summary(
|
||||
cls,
|
||||
name: str = "",
|
||||
index: int = -1,
|
||||
result: int = -1,
|
||||
index: int | None = None,
|
||||
result: int | None = None,
|
||||
clear: bool = False,
|
||||
last_result: bool = False,
|
||||
status: Literal["all", "success", "error"] = "all",
|
||||
|
@ -138,7 +138,7 @@ class ExecutionRegistry:
|
|||
)
|
||||
return
|
||||
|
||||
if result and result >= 0:
|
||||
if result is not None and result >= 0:
|
||||
try:
|
||||
result_context = cls._store_by_index[result]
|
||||
except KeyError:
|
||||
|
@ -146,7 +146,11 @@ class ExecutionRegistry:
|
|||
f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
|
||||
)
|
||||
return
|
||||
cls._console.print(result_context.result)
|
||||
cls._console.print(f"{result_context.signature}:")
|
||||
if result_context.exception:
|
||||
cls._console.print(result_context.exception)
|
||||
else:
|
||||
cls._console.print(result_context.result)
|
||||
return
|
||||
|
||||
if name:
|
||||
|
@ -157,9 +161,10 @@ class ExecutionRegistry:
|
|||
)
|
||||
return
|
||||
title = f"📊 Execution History for '{contexts[0].name}'"
|
||||
elif index and index >= 0:
|
||||
elif index is not None and index >= 0:
|
||||
try:
|
||||
contexts = [cls._store_by_index[index]]
|
||||
print(contexts)
|
||||
except KeyError:
|
||||
cls._console.print(
|
||||
f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
|
||||
|
|
|
@ -796,7 +796,12 @@ class Falyx:
|
|||
def table(self) -> Table:
|
||||
"""Creates or returns a custom table to display the menu commands."""
|
||||
if callable(self.custom_table):
|
||||
return self.custom_table(self)
|
||||
custom_table = self.custom_table(self)
|
||||
if not isinstance(custom_table, Table):
|
||||
raise FalyxError(
|
||||
"custom_table must return an instance of rich.table.Table."
|
||||
)
|
||||
return custom_table
|
||||
elif isinstance(self.custom_table, Table):
|
||||
return self.custom_table
|
||||
else:
|
||||
|
@ -834,21 +839,31 @@ class Falyx:
|
|||
|
||||
choice = choice.upper()
|
||||
name_map = self._name_map
|
||||
run_command = None
|
||||
if name_map.get(choice):
|
||||
run_command = name_map[choice]
|
||||
else:
|
||||
prefix_matches = [
|
||||
cmd for key, cmd in name_map.items() if key.startswith(choice)
|
||||
]
|
||||
if len(prefix_matches) == 1:
|
||||
run_command = prefix_matches[0]
|
||||
|
||||
if run_command:
|
||||
if not from_validate:
|
||||
logger.info("Command '%s' selected.", choice)
|
||||
logger.info("Command '%s' selected.", run_command.key)
|
||||
if is_preview:
|
||||
return True, name_map[choice], args, kwargs
|
||||
return True, run_command, args, kwargs
|
||||
elif self.mode in {FalyxMode.RUN, FalyxMode.RUN_ALL, FalyxMode.PREVIEW}:
|
||||
return False, name_map[choice], args, kwargs
|
||||
return False, run_command, args, kwargs
|
||||
try:
|
||||
args, kwargs = await name_map[choice].parse_args(
|
||||
input_args, from_validate
|
||||
)
|
||||
args, kwargs = await run_command.parse_args(input_args, from_validate)
|
||||
except (CommandArgumentError, Exception) as error:
|
||||
if not from_validate:
|
||||
name_map[choice].show_help()
|
||||
self.console.print(f"[{OneColors.DARK_RED}]❌ [{choice}]: {error}")
|
||||
run_command.show_help()
|
||||
self.console.print(
|
||||
f"[{OneColors.DARK_RED}]❌ [{run_command.key}]: {error}"
|
||||
)
|
||||
else:
|
||||
raise ValidationError(
|
||||
message=str(error), cursor_position=len(raw_choices)
|
||||
|
@ -856,11 +871,7 @@ class Falyx:
|
|||
return is_preview, None, args, kwargs
|
||||
except HelpSignal:
|
||||
return True, None, args, kwargs
|
||||
return is_preview, name_map[choice], args, kwargs
|
||||
|
||||
prefix_matches = [cmd for key, cmd in name_map.items() if key.startswith(choice)]
|
||||
if len(prefix_matches) == 1:
|
||||
return is_preview, prefix_matches[0], args, kwargs
|
||||
return is_preview, run_command, args, kwargs
|
||||
|
||||
fuzzy_matches = get_close_matches(choice, list(name_map.keys()), n=3, cutoff=0.7)
|
||||
if fuzzy_matches:
|
||||
|
@ -890,12 +901,14 @@ class Falyx:
|
|||
)
|
||||
return is_preview, None, args, kwargs
|
||||
|
||||
def _create_context(self, selected_command: Command) -> ExecutionContext:
|
||||
"""Creates a context dictionary for the selected command."""
|
||||
def _create_context(
|
||||
self, selected_command: Command, args: tuple, kwargs: dict[str, Any]
|
||||
) -> ExecutionContext:
|
||||
"""Creates an ExecutionContext object for the selected command."""
|
||||
return ExecutionContext(
|
||||
name=selected_command.description,
|
||||
args=tuple(),
|
||||
kwargs={},
|
||||
args=args,
|
||||
kwargs=kwargs,
|
||||
action=selected_command,
|
||||
)
|
||||
|
||||
|
@ -929,7 +942,7 @@ class Falyx:
|
|||
logger.info("Back selected: exiting %s", self.get_title())
|
||||
return False
|
||||
|
||||
context = self._create_context(selected_command)
|
||||
context = self._create_context(selected_command, args, kwargs)
|
||||
context.start_timer()
|
||||
try:
|
||||
await self.hooks.trigger(HookType.BEFORE, context)
|
||||
|
@ -974,7 +987,7 @@ class Falyx:
|
|||
selected_command.description,
|
||||
)
|
||||
|
||||
context = self._create_context(selected_command)
|
||||
context = self._create_context(selected_command, args, kwargs)
|
||||
context.start_timer()
|
||||
try:
|
||||
await self.hooks.trigger(HookType.BEFORE, context)
|
||||
|
|
|
@ -629,7 +629,10 @@ class CommandArgumentParser:
|
|||
consumed_positional_indicies.add(j)
|
||||
|
||||
if i < len(args):
|
||||
raise CommandArgumentError(f"Unexpected positional argument: {args[i:]}")
|
||||
plural = "s" if len(args[i:]) > 1 else ""
|
||||
raise CommandArgumentError(
|
||||
f"Unexpected positional argument{plural}: {', '.join(args[i:])}"
|
||||
)
|
||||
|
||||
return i
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.49"
|
||||
__version__ = "0.1.50"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "falyx"
|
||||
version = "0.1.49"
|
||||
version = "0.1.50"
|
||||
description = "Reliable and introspectable async CLI action framework."
|
||||
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
||||
license = "MIT"
|
||||
|
|
Loading…
Reference in New Issue