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