"""Renderers for the ResultTable API. This module provides a Rich-based Renderer implementation that returns a `rich.table.Table` renderable. The implementation is intentionally small and focused on the command-line display use-case; keep logic side-effect-free where possible and let callers decide whether to `Console.print()` or capture output. """ from __future__ import annotations from typing import Any, Dict, Iterable, Optional import logging logger = logging.getLogger(__name__) from SYS.result_table import ( RESULT_TABLE_BORDER_STYLE, RESULT_TABLE_HEADER_STYLE, apply_result_table_layout, get_result_table_row_style, ) from SYS.result_table_api import ColumnSpec, ResultModel, ResultTable, Renderer class RichRenderer(Renderer): """Rich renderer implementing the `Renderer` protocol. Usage: from rich.console import Console table = RichRenderer().render(rows, columns, meta) Console().print(table) """ def render(self, rows: Iterable[ResultModel], columns: Iterable[ColumnSpec], meta: Optional[Dict[str, Any]] = None) -> Any: # pragma: no cover - simple wrapper try: from rich.table import Table as RichTable except Exception as exc: raise RuntimeError("rich is required for RichRenderer") from exc table = RichTable( show_header=True, header_style=RESULT_TABLE_HEADER_STYLE, border_style=RESULT_TABLE_BORDER_STYLE, box=None, padding=(0, 1), pad_edge=False, collapse_padding=True, expand=False, ) apply_result_table_layout(table) cols = list(columns) for col in cols: if str(col.header or "").strip().lower() == "tag": table.add_column(col.header, overflow="fold") else: table.add_column(col.header) for row_idx, r in enumerate(rows): cells = [] for col in cols: try: raw = col.extractor(r) if col.format_fn: try: cell = col.format_fn(raw) except Exception as exc: logger.exception("Column format function failed for '%s': %s", col.header, exc) cell = str(raw or "") else: cell = str(raw or "") except Exception as exc: logger.exception("Column extractor failed for '%s': %s", col.header, exc) cell = "" cells.append(cell) table.add_row(*cells, style=get_result_table_row_style(row_idx)) return table # Small convenience function def render_to_console(rows: Iterable[ResultModel], columns: Iterable[ColumnSpec], meta: Optional[Dict[str, Any]] = None) -> None: try: from rich.console import Console except Exception: # If rich isn't present, fall back to simple text output for r in rows: print(" ".join(str((col.extractor(r) or "")) for col in columns)) return table = RichRenderer().render(rows, columns, meta) Console().print(table) def render_result_table(table: ResultTable, renderer: Optional[Renderer] = None) -> Any: """Render a ResultTable with the provided renderer (RichRenderer by default).""" rend = renderer or RichRenderer() return rend.render(table.rows, table.columns, table.meta) def render_result_table_to_console(table: ResultTable, renderer: Optional[Renderer] = None) -> None: try: from rich.console import Console except Exception: for r in table.rows: print(" ".join(str((col.extractor(r) or "")) for col in table.columns)) return console = Console() console.print(render_result_table(table, renderer))