Add filtering and options for History Command
This commit is contained in:
parent
09eeb90dc6
commit
ac82076511
|
@ -74,7 +74,7 @@ class BaseAction(ABC):
|
|||
self.inject_into: str = inject_into
|
||||
self._never_prompt: bool = never_prompt
|
||||
self._skip_in_chain: bool = False
|
||||
self.console = Console(color_system="auto")
|
||||
self.console = Console(color_system="truecolor")
|
||||
self.options_manager: OptionsManager | None = None
|
||||
|
||||
if logging_hooks:
|
||||
|
|
|
@ -51,7 +51,10 @@ class MenuAction(BaseAction):
|
|||
self.columns = columns
|
||||
self.prompt_message = prompt_message
|
||||
self.default_selection = default_selection
|
||||
self.console = console or Console(color_system="auto")
|
||||
if isinstance(console, Console):
|
||||
self.console = console
|
||||
elif console:
|
||||
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
||||
self.prompt_session = prompt_session or PromptSession()
|
||||
self.include_reserved = include_reserved
|
||||
self.show_table = show_table
|
||||
|
|
|
@ -43,7 +43,10 @@ class PromptMenuAction(BaseAction):
|
|||
self.menu_options = menu_options
|
||||
self.prompt_message = prompt_message
|
||||
self.default_selection = default_selection
|
||||
self.console = console or Console(color_system="auto")
|
||||
if isinstance(console, Console):
|
||||
self.console = console
|
||||
elif console:
|
||||
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
||||
self.prompt_session = prompt_session or PromptSession()
|
||||
self.include_reserved = include_reserved
|
||||
|
||||
|
|
|
@ -76,7 +76,10 @@ class SelectFileAction(BaseAction):
|
|||
self.prompt_message = prompt_message
|
||||
self.suffix_filter = suffix_filter
|
||||
self.style = style
|
||||
self.console = console or Console(color_system="auto")
|
||||
if isinstance(console, Console):
|
||||
self.console = console
|
||||
elif console:
|
||||
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
||||
self.prompt_session = prompt_session or PromptSession()
|
||||
self.return_type = self._coerce_return_type(return_type)
|
||||
|
||||
|
|
|
@ -67,7 +67,10 @@ class SelectionAction(BaseAction):
|
|||
self.return_type: SelectionReturnType = self._coerce_return_type(return_type)
|
||||
self.title = title
|
||||
self.columns = columns
|
||||
self.console = console or Console(color_system="auto")
|
||||
if isinstance(console, Console):
|
||||
self.console = console
|
||||
elif console:
|
||||
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
||||
self.prompt_session = prompt_session or PromptSession()
|
||||
self.default_selection = default_selection
|
||||
self.prompt_message = prompt_message
|
||||
|
|
|
@ -40,7 +40,10 @@ class UserInputAction(BaseAction):
|
|||
)
|
||||
self.prompt_text = prompt_text
|
||||
self.validator = validator
|
||||
self.console = console or Console(color_system="auto")
|
||||
if isinstance(console, Console):
|
||||
self.console = console
|
||||
elif console:
|
||||
raise ValueError("`console` must be an instance of `rich.console.Console`")
|
||||
self.prompt_session = prompt_session or PromptSession()
|
||||
|
||||
def get_infer_target(self) -> tuple[None, None]:
|
||||
|
|
|
@ -30,7 +30,7 @@ class BottomBar:
|
|||
key_validator: Callable[[str], bool] | None = None,
|
||||
) -> None:
|
||||
self.columns = columns
|
||||
self.console = Console(color_system="auto")
|
||||
self.console = Console(color_system="truecolor")
|
||||
self._named_items: dict[str, Callable[[], HTML]] = {}
|
||||
self._value_getters: dict[str, Callable[[], Any]] = CaseInsensitiveDict()
|
||||
self.toggle_keys: list[str] = []
|
||||
|
|
|
@ -44,7 +44,7 @@ from falyx.signals import CancelSignal
|
|||
from falyx.themes import OneColors
|
||||
from falyx.utils import ensure_async
|
||||
|
||||
console = Console(color_system="auto")
|
||||
console = Console(color_system="truecolor")
|
||||
|
||||
|
||||
class Command(BaseModel):
|
||||
|
|
|
@ -18,11 +18,10 @@ from falyx.action.base import BaseAction
|
|||
from falyx.command import Command
|
||||
from falyx.falyx import Falyx
|
||||
from falyx.logger import logger
|
||||
from falyx.parsers import CommandArgumentParser
|
||||
from falyx.retry import RetryPolicy
|
||||
from falyx.themes import OneColors
|
||||
|
||||
console = Console(color_system="auto")
|
||||
console = Console(color_system="truecolor")
|
||||
|
||||
|
||||
def wrap_if_needed(obj: Any, name=None) -> BaseAction | Command:
|
||||
|
|
|
@ -80,8 +80,10 @@ class ExecutionContext(BaseModel):
|
|||
start_wall: datetime | None = None
|
||||
end_wall: datetime | None = None
|
||||
|
||||
index: int | None = None
|
||||
|
||||
extra: dict[str, Any] = Field(default_factory=dict)
|
||||
console: Console = Field(default_factory=lambda: Console(color_system="auto"))
|
||||
console: Console = Field(default_factory=lambda: Console(color_system="truecolor"))
|
||||
|
||||
shared_context: SharedContext | None = None
|
||||
|
||||
|
|
|
@ -29,7 +29,8 @@ from __future__ import annotations
|
|||
|
||||
from collections import defaultdict
|
||||
from datetime import datetime
|
||||
from typing import Dict, List
|
||||
from threading import Lock
|
||||
from typing import Any, Literal
|
||||
|
||||
from rich import box
|
||||
from rich.console import Console
|
||||
|
@ -70,23 +71,30 @@ class ExecutionRegistry:
|
|||
ExecutionRegistry.summary()
|
||||
"""
|
||||
|
||||
_store_by_name: Dict[str, List[ExecutionContext]] = defaultdict(list)
|
||||
_store_all: List[ExecutionContext] = []
|
||||
_console = Console(color_system="auto")
|
||||
_store_by_name: dict[str, list[ExecutionContext]] = defaultdict(list)
|
||||
_store_by_index: dict[int, ExecutionContext] = {}
|
||||
_store_all: list[ExecutionContext] = []
|
||||
_console = Console(color_system="truecolor")
|
||||
_index = 0
|
||||
_lock = Lock()
|
||||
|
||||
@classmethod
|
||||
def record(cls, context: ExecutionContext):
|
||||
"""Record an execution context."""
|
||||
logger.debug(context.to_log_line())
|
||||
with cls._lock:
|
||||
context.index = cls._index
|
||||
cls._store_by_index[cls._index] = context
|
||||
cls._index += 1
|
||||
cls._store_by_name[context.name].append(context)
|
||||
cls._store_all.append(context)
|
||||
|
||||
@classmethod
|
||||
def get_all(cls) -> List[ExecutionContext]:
|
||||
def get_all(cls) -> list[ExecutionContext]:
|
||||
return cls._store_all
|
||||
|
||||
@classmethod
|
||||
def get_by_name(cls, name: str) -> List[ExecutionContext]:
|
||||
def get_by_name(cls, name: str) -> list[ExecutionContext]:
|
||||
return cls._store_by_name.get(name, [])
|
||||
|
||||
@classmethod
|
||||
|
@ -97,11 +105,74 @@ class ExecutionRegistry:
|
|||
def clear(cls):
|
||||
cls._store_by_name.clear()
|
||||
cls._store_all.clear()
|
||||
cls._store_by_index.clear()
|
||||
|
||||
@classmethod
|
||||
def summary(cls):
|
||||
table = Table(title="📊 Execution History", expand=True, box=box.SIMPLE)
|
||||
def summary(
|
||||
cls,
|
||||
name: str = "",
|
||||
index: int = -1,
|
||||
result: int = -1,
|
||||
clear: bool = False,
|
||||
last_result: bool = False,
|
||||
status: Literal["all", "success", "error"] = "all",
|
||||
):
|
||||
if clear:
|
||||
cls.clear()
|
||||
cls._console.print(f"[{OneColors.GREEN}]✅ Execution history cleared.")
|
||||
return
|
||||
|
||||
if last_result:
|
||||
for ctx in reversed(cls._store_all):
|
||||
if ctx.name.upper() not in [
|
||||
"HISTORY",
|
||||
"HELP",
|
||||
"EXIT",
|
||||
"VIEW EXECUTION HISTORY",
|
||||
"BACK",
|
||||
]:
|
||||
cls._console.print(ctx.result)
|
||||
return
|
||||
cls._console.print(
|
||||
f"[{OneColors.DARK_RED}]❌ No valid executions found to display last result."
|
||||
)
|
||||
return
|
||||
|
||||
if result and result >= 0:
|
||||
try:
|
||||
result_context = cls._store_by_index[result]
|
||||
except KeyError:
|
||||
cls._console.print(
|
||||
f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
|
||||
)
|
||||
return
|
||||
cls._console.print(result_context.result)
|
||||
return
|
||||
|
||||
if name:
|
||||
contexts = cls.get_by_name(name)
|
||||
if not contexts:
|
||||
cls._console.print(
|
||||
f"[{OneColors.DARK_RED}]❌ No executions found for action '{name}'."
|
||||
)
|
||||
return
|
||||
title = f"📊 Execution History for '{contexts[0].name}'"
|
||||
elif index and index >= 0:
|
||||
try:
|
||||
contexts = [cls._store_by_index[index]]
|
||||
except KeyError:
|
||||
cls._console.print(
|
||||
f"[{OneColors.DARK_RED}]❌ No execution found for index {index}."
|
||||
)
|
||||
return
|
||||
title = f"📊 Execution History for Index {index}"
|
||||
else:
|
||||
contexts = cls.get_all()
|
||||
title = "📊 Execution History"
|
||||
|
||||
table = Table(title=title, expand=True, box=box.SIMPLE)
|
||||
|
||||
table.add_column("Index", justify="right", style="dim")
|
||||
table.add_column("Name", style="bold cyan")
|
||||
table.add_column("Start", justify="right", style="dim")
|
||||
table.add_column("End", justify="right", style="dim")
|
||||
|
@ -109,7 +180,7 @@ class ExecutionRegistry:
|
|||
table.add_column("Status", style="bold")
|
||||
table.add_column("Result / Exception", overflow="fold")
|
||||
|
||||
for ctx in cls.get_all():
|
||||
for ctx in contexts:
|
||||
start = (
|
||||
datetime.fromtimestamp(ctx.start_time).strftime("%H:%M:%S")
|
||||
if ctx.start_time
|
||||
|
@ -122,15 +193,19 @@ class ExecutionRegistry:
|
|||
)
|
||||
duration = f"{ctx.duration:.3f}s" if ctx.duration else "n/a"
|
||||
|
||||
if ctx.exception:
|
||||
status = f"[{OneColors.DARK_RED}]❌ Error"
|
||||
result = repr(ctx.exception)
|
||||
if ctx.exception and status.lower() in ["all", "error"]:
|
||||
final_status = f"[{OneColors.DARK_RED}]❌ Error"
|
||||
final_result = repr(ctx.exception)
|
||||
elif status.lower() in ["all", "success"]:
|
||||
final_status = f"[{OneColors.GREEN}]✅ Success"
|
||||
final_result = repr(ctx.result)
|
||||
if len(final_result) > 1000:
|
||||
final_result = f"{final_result[:1000]}..."
|
||||
else:
|
||||
status = f"[{OneColors.GREEN}]✅ Success"
|
||||
result = repr(ctx.result)
|
||||
if len(result) > 1000:
|
||||
result = f"{result[:1000]}..."
|
||||
continue
|
||||
|
||||
table.add_row(ctx.name, start, end, duration, status, result)
|
||||
table.add_row(
|
||||
str(ctx.index), ctx.name, start, end, duration, final_status, final_result
|
||||
)
|
||||
|
||||
cls._console.print(table)
|
||||
|
|
|
@ -201,7 +201,7 @@ class Falyx:
|
|||
self.help_command: Command | None = (
|
||||
self._get_help_command() if include_help_command else None
|
||||
)
|
||||
self.console: Console = Console(color_system="auto", theme=get_nord_theme())
|
||||
self.console: Console = Console(color_system="truecolor", theme=get_nord_theme())
|
||||
self.welcome_message: str | Markdown | dict[str, Any] = welcome_message
|
||||
self.exit_message: str | Markdown | dict[str, Any] = exit_message
|
||||
self.hooks: HookManager = HookManager()
|
||||
|
@ -300,6 +300,40 @@ class Falyx:
|
|||
|
||||
def _get_history_command(self) -> Command:
|
||||
"""Returns the history command for the menu."""
|
||||
parser = CommandArgumentParser(
|
||||
command_key="Y",
|
||||
command_description="History",
|
||||
command_style=OneColors.DARK_YELLOW,
|
||||
aliases=["HISTORY"],
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--name",
|
||||
help="Filter by execution name.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--index",
|
||||
type=int,
|
||||
help="Filter by execution index (0-based).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--status",
|
||||
choices=["all", "success", "error"],
|
||||
default="all",
|
||||
help="Filter by execution status (default: all).",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--clear",
|
||||
action="store_true",
|
||||
help="Clear the Execution History.",
|
||||
)
|
||||
parser.add_argument("-r", "--result", type=int, help="Get the result by index")
|
||||
parser.add_argument(
|
||||
"-l", "--last-result", action="store_true", help="Get the last result"
|
||||
)
|
||||
return Command(
|
||||
key="Y",
|
||||
description="History",
|
||||
|
@ -307,6 +341,8 @@ class Falyx:
|
|||
action=Action(name="View Execution History", action=er.summary),
|
||||
style=OneColors.DARK_YELLOW,
|
||||
simple_help_signature=True,
|
||||
arg_parser=parser,
|
||||
help_text="View the execution history of commands.",
|
||||
)
|
||||
|
||||
async def _show_help(self, tag: str = "") -> None:
|
||||
|
|
|
@ -98,7 +98,7 @@ commands:
|
|||
aliases: [clean, cleanup]
|
||||
"""
|
||||
|
||||
console = Console(color_system="auto")
|
||||
console = Console(color_system="truecolor")
|
||||
|
||||
|
||||
def init_project(name: str) -> None:
|
||||
|
|
|
@ -159,7 +159,7 @@ class CommandArgumentParser:
|
|||
aliases: list[str] | None = None,
|
||||
) -> None:
|
||||
"""Initialize the CommandArgumentParser."""
|
||||
self.console = Console(color_system="auto")
|
||||
self.console = Console(color_system="truecolor")
|
||||
self.command_key: str = command_key
|
||||
self.command_description: str = command_description
|
||||
self.command_style: str = command_style
|
||||
|
|
|
@ -273,7 +273,7 @@ async def prompt_for_index(
|
|||
show_table: bool = True,
|
||||
) -> int:
|
||||
prompt_session = prompt_session or PromptSession()
|
||||
console = console or Console(color_system="auto")
|
||||
console = console or Console(color_system="truecolor")
|
||||
|
||||
if show_table:
|
||||
console.print(table, justify="center")
|
||||
|
@ -298,7 +298,7 @@ async def prompt_for_selection(
|
|||
) -> str:
|
||||
"""Prompt the user to select a key from a set of options. Return the selected key."""
|
||||
prompt_session = prompt_session or PromptSession()
|
||||
console = console or Console(color_system="auto")
|
||||
console = console or Console(color_system="truecolor")
|
||||
|
||||
if show_table:
|
||||
console.print(table, justify="center")
|
||||
|
@ -351,7 +351,7 @@ async def select_value_from_list(
|
|||
highlight=highlight,
|
||||
)
|
||||
prompt_session = prompt_session or PromptSession()
|
||||
console = console or Console(color_system="auto")
|
||||
console = console or Console(color_system="truecolor")
|
||||
|
||||
selection_index = await prompt_for_index(
|
||||
len(selections) - 1,
|
||||
|
@ -376,7 +376,7 @@ async def select_key_from_dict(
|
|||
) -> Any:
|
||||
"""Prompt for a key from a dict, returns the key."""
|
||||
prompt_session = prompt_session or PromptSession()
|
||||
console = console or Console(color_system="auto")
|
||||
console = console or Console(color_system="truecolor")
|
||||
|
||||
console.print(table, justify="center")
|
||||
|
||||
|
@ -401,7 +401,7 @@ async def select_value_from_dict(
|
|||
) -> Any:
|
||||
"""Prompt for a key from a dict, but return the value."""
|
||||
prompt_session = prompt_session or PromptSession()
|
||||
console = console or Console(color_system="auto")
|
||||
console = console or Console(color_system="truecolor")
|
||||
|
||||
console.print(table, justify="center")
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.48"
|
||||
__version__ = "0.1.49"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "falyx"
|
||||
version = "0.1.48"
|
||||
version = "0.1.49"
|
||||
description = "Reliable and introspectable async CLI action framework."
|
||||
authors = ["Roland Thomas Jr <roland@rtj.dev>"]
|
||||
license = "MIT"
|
||||
|
|
Loading…
Reference in New Issue