|
4 | 4 | # Contact: qubitium@modelcloud.ai, x.com/qubitium |
5 | 5 |
|
6 | 6 | import contextlib |
| 7 | +import numbers |
7 | 8 | import os |
8 | 9 | import sys |
9 | 10 | import threading |
10 | 11 | import time |
11 | 12 | from collections import OrderedDict |
12 | | -from typing import Dict, Iterator, Optional |
| 13 | +from typing import Any, Dict, Iterator, Optional, Sequence |
13 | 14 |
|
| 15 | +import pcre |
14 | 16 | from logbar import LogBar |
15 | 17 |
|
16 | 18 |
|
| 19 | +_ANSI_ESCAPE_RE = pcre.compile(r"\x1b\[[0-9;]*m") |
| 20 | + |
| 21 | + |
17 | 22 | class _SilentProgress: |
18 | 23 | """Minimal no-op progress handle for non-interactive test sessions.""" |
19 | 24 |
|
@@ -102,6 +107,99 @@ def setup_logger(): |
102 | 107 | return _AdaptiveLoggerProxy(LogBar.shared()) |
103 | 108 |
|
104 | 109 |
|
| 110 | +def _table_cell_text(value: Any, floatfmt: Optional[str]) -> str: |
| 111 | + if value is None: |
| 112 | + return "" |
| 113 | + if isinstance(value, bool): |
| 114 | + return str(value) |
| 115 | + if floatfmt is not None and isinstance(value, numbers.Real) and not isinstance(value, numbers.Integral): |
| 116 | + return format(float(value), floatfmt) |
| 117 | + return str(value) |
| 118 | + |
| 119 | + |
| 120 | +def _visible_width(value: str) -> int: |
| 121 | + return len(_ANSI_ESCAPE_RE.sub("", value)) |
| 122 | + |
| 123 | + |
| 124 | +def _pad_table_cell(value: str, width: int) -> str: |
| 125 | + return value + (" " * max(0, width - _visible_width(value))) |
| 126 | + |
| 127 | + |
| 128 | +def _render_grid_table(headers: Sequence[str], rows: Sequence[Sequence[str]], widths: Sequence[int]) -> str: |
| 129 | + def border() -> str: |
| 130 | + return "+" + "+".join("-" * (width + 2) for width in widths) + "+" |
| 131 | + |
| 132 | + def row_line(values: Sequence[str]) -> str: |
| 133 | + return "| " + " | ".join(_pad_table_cell(value, widths[idx]) for idx, value in enumerate(values)) + " |" |
| 134 | + |
| 135 | + lines = [border(), row_line(headers), border()] |
| 136 | + lines.extend(row_line(row) for row in rows) |
| 137 | + lines.append(border()) |
| 138 | + return "\n".join(lines) |
| 139 | + |
| 140 | + |
| 141 | +def _render_github_table(headers: Sequence[str], rows: Sequence[Sequence[str]], widths: Sequence[int]) -> str: |
| 142 | + def row_line(values: Sequence[str]) -> str: |
| 143 | + return "| " + " | ".join(_pad_table_cell(value, widths[idx]) for idx, value in enumerate(values)) + " |" |
| 144 | + |
| 145 | + separator = "| " + " | ".join("-" * width for width in widths) + " |" |
| 146 | + lines = [row_line(headers), separator] |
| 147 | + lines.extend(row_line(row) for row in rows) |
| 148 | + return "\n".join(lines) |
| 149 | + |
| 150 | + |
| 151 | +def _render_simple_table(headers: Sequence[str], rows: Sequence[Sequence[str]], widths: Sequence[int]) -> str: |
| 152 | + def row_line(values: Sequence[str]) -> str: |
| 153 | + return " ".join(_pad_table_cell(value, widths[idx]) for idx, value in enumerate(values)) |
| 154 | + |
| 155 | + separator = " ".join("-" * width for width in widths) |
| 156 | + lines = [row_line(headers), separator] |
| 157 | + lines.extend(row_line(row) for row in rows) |
| 158 | + return "\n".join(lines) |
| 159 | + |
| 160 | + |
| 161 | +def render_table( |
| 162 | + rows: Sequence[Sequence[Any]], |
| 163 | + *, |
| 164 | + headers: Sequence[Any], |
| 165 | + tablefmt: str = "grid", |
| 166 | + floatfmt: Optional[str] = None, |
| 167 | + logger: Optional[LogBar] = None, |
| 168 | +) -> str: |
| 169 | + """Render a small diagnostic table using LogBar-compatible column sizing.""" |
| 170 | + |
| 171 | + header_text = [str(header) for header in headers] |
| 172 | + row_text: list[list[str]] = [] |
| 173 | + for row in rows: |
| 174 | + values = list(row) |
| 175 | + if len(values) != len(header_text): |
| 176 | + raise ValueError( |
| 177 | + f"Row length {len(values)} does not match header length {len(header_text)}" |
| 178 | + ) |
| 179 | + row_text.append([_table_cell_text(value, floatfmt) for value in values]) |
| 180 | + |
| 181 | + widths = [_visible_width(header) for header in header_text] |
| 182 | + if header_text: |
| 183 | + columns = (logger or LogBar.shared()).columns( |
| 184 | + cols=[{"label": header, "width": "fit"} for header in header_text], |
| 185 | + padding=1, |
| 186 | + ) |
| 187 | + for row in row_text: |
| 188 | + columns.info.simulate(*row) |
| 189 | + widths = [max(widths[idx], width) for idx, width in enumerate(columns.widths)] |
| 190 | + |
| 191 | + for row in row_text: |
| 192 | + for idx, cell in enumerate(row): |
| 193 | + widths[idx] = max(widths[idx], _visible_width(cell)) |
| 194 | + |
| 195 | + tablefmt_normalized = (tablefmt or "grid").lower() |
| 196 | + if tablefmt_normalized == "github": |
| 197 | + return _render_github_table(header_text, row_text, widths) |
| 198 | + if tablefmt_normalized == "simple": |
| 199 | + return _render_simple_table(header_text, row_text, widths) |
| 200 | + return _render_grid_table(header_text, row_text, widths) |
| 201 | + |
| 202 | + |
105 | 203 | class QuantizationRegionTimer: |
106 | 204 | """Aggregate and display timing statistics for key quantization stages.""" |
107 | 205 |
|
|
0 commit comments