Files
cleveragents-core/benchmarks/cli_render_bench.py
brent.edwards f2be43dcc2 chore(cli): polish help and output
Centralize CLI output through shared renderers module with get_console()
and get_err_console() accessors. Introduce ColumnSpec-based table rendering
for plan list, resource list, and actor list commands. Add _FORMAT_HELP
constant and fmt= parameter threading for consistent format handling.

Includes Behave BDD tests for format output validation, Robot Framework
integration tests for CLI formatting consistency, and CHANGELOG entry.

ISSUES CLOSED: #210
2026-03-20 00:25:44 +00:00

190 lines
5.3 KiB
Python

"""ASV benchmarks for CLI output rendering overhead (issue #210).
Measures render_detail, render_list, render_error, and render_success
across all six output formats.
"""
from __future__ import annotations
import importlib
import io
import sys
from pathlib import Path
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
import cleveragents # noqa: E402
importlib.reload(cleveragents)
from rich.console import Console # noqa: E402
import cleveragents.cli.renderers as _renderers_mod # noqa: E402
from cleveragents.cli.renderers import ( # noqa: E402
ColumnSpec,
render_detail,
render_error,
render_list,
render_success,
)
# ---------------------------------------------------------------------------
# Shared sample data
# ---------------------------------------------------------------------------
_DETAIL_DATA = {
"name": "bench-item",
"count": 42,
"active": True,
"description": "A benchmark test item for measuring renderer overhead",
}
_LIST_COLUMNS = [
ColumnSpec(key="name", label="Name"),
ColumnSpec(key="value", label="Value"),
ColumnSpec(key="status", label="Status"),
]
_LIST_ROWS_10 = [
{
"name": f"item-{i}",
"value": i * 10,
"status": "active" if i % 2 == 0 else "inactive",
}
for i in range(10)
]
# ---------------------------------------------------------------------------
# Console buffer helpers
# ---------------------------------------------------------------------------
def _install_buffer_consoles() -> None:
"""Replace module-level consoles with buffer-backed instances."""
_renderers_mod._console = Console(
file=io.StringIO(), no_color=True, highlight=False
)
_renderers_mod._err_console = Console(
file=io.StringIO(), no_color=True, highlight=False
)
def _reset_buffers() -> None:
"""Seek to start and truncate the console buffers between iterations."""
for attr in ("_console", "_err_console"):
console = getattr(_renderers_mod, attr)
if console is not None and hasattr(console, "file"):
console.file.seek(0)
console.file.truncate()
# ---------------------------------------------------------------------------
# RenderDetailSuite
# ---------------------------------------------------------------------------
class RenderDetailSuite:
"""Benchmark render_detail across formats."""
def setup(self) -> None:
_install_buffer_consoles()
def teardown(self) -> None:
_renderers_mod._console = None
_renderers_mod._err_console = None
def time_render_detail_json(self) -> None:
_reset_buffers()
render_detail(_DETAIL_DATA, fmt="json")
def time_render_detail_yaml(self) -> None:
_reset_buffers()
render_detail(_DETAIL_DATA, fmt="yaml")
def time_render_detail_plain(self) -> None:
_reset_buffers()
render_detail(_DETAIL_DATA, fmt="plain")
def time_render_detail_rich(self) -> None:
_reset_buffers()
render_detail(_DETAIL_DATA, fmt="rich")
# ---------------------------------------------------------------------------
# RenderListSuite
# ---------------------------------------------------------------------------
class RenderListSuite:
"""Benchmark render_list across formats."""
def setup(self) -> None:
_install_buffer_consoles()
def teardown(self) -> None:
_renderers_mod._console = None
_renderers_mod._err_console = None
def time_render_list_json_10_rows(self) -> None:
_reset_buffers()
render_list(_LIST_ROWS_10, columns=_LIST_COLUMNS, fmt="json")
def time_render_list_plain_10_rows(self) -> None:
_reset_buffers()
render_list(_LIST_ROWS_10, columns=_LIST_COLUMNS, fmt="plain")
def time_render_list_rich_10_rows(self) -> None:
_reset_buffers()
render_list(_LIST_ROWS_10, columns=_LIST_COLUMNS, fmt="rich")
# ---------------------------------------------------------------------------
# RenderErrorSuite
# ---------------------------------------------------------------------------
class RenderErrorSuite:
"""Benchmark render_error across formats."""
def setup(self) -> None:
_install_buffer_consoles()
def teardown(self) -> None:
_renderers_mod._console = None
_renderers_mod._err_console = None
def time_render_error_rich(self) -> None:
_reset_buffers()
render_error("BenchError", "something went wrong", fmt="rich")
def time_render_error_json(self) -> None:
_reset_buffers()
render_error("BenchError", "something went wrong", fmt="json")
# ---------------------------------------------------------------------------
# RenderSuccessSuite
# ---------------------------------------------------------------------------
class RenderSuccessSuite:
"""Benchmark render_success across formats."""
def setup(self) -> None:
_install_buffer_consoles()
def teardown(self) -> None:
_renderers_mod._console = None
_renderers_mod._err_console = None
def time_render_success_rich(self) -> None:
_reset_buffers()
render_success("Benchmark complete", fmt="rich")
def time_render_success_plain(self) -> None:
_reset_buffers()
render_success("Benchmark complete", fmt="plain")