AQIT 0.1.0
Loading...
Searching...
No Matches
table_plain.py
Go to the documentation of this file.
1# Copyright (c) 2025-present Aquin Labs Private Limited. All Rights Reserved.
2"""Plain Unicode tables for quiet CLI surfaces (no Rich)."""
3
4from __future__ import annotations
5
6import sys
7from collections.abc import Sequence
8
9
10# Box-drawing (falls back to ASCII if stdout can't encode them).
11_UNICODE = {
12 "tl": "┌", "tr": "┐", "bl": "└", "br": "┘",
13 "h": "─", "v": "│",
14 "l": "├", "r": "┤", "t": "┬", "b": "┴", "x": "┼",
16_ASCII = {
17 "tl": "+", "tr": "+", "bl": "+", "br": "+",
18 "h": "-", "v": "|",
19 "l": "+", "r": "+", "t": "+", "b": "+", "x": "+",
21
22
23def _glyphs() -> dict[str, str]:
24 try:
25 encoding = getattr(sys.stdout, "encoding", None) or "utf-8"
26 "┌─│".encode(encoding)
27 return _UNICODE
28 except Exception:
29 return _ASCII
30
31
32def _cell(text: str, width: int, *, align: str = "left") -> str:
33 s = str(text)
34 if len(s) > width:
35 if width <= 1:
36 s = s[:width]
37 else:
38 s = s[: width - 1] + "…"
39 if align == "right":
40 return s.rjust(width)
41 return s.ljust(width)
42
43
44def format_table(
45 headers: Sequence[str],
46 rows: Sequence[Sequence[str]],
47 *,
48 aligns: Sequence[str] | None = None,
49 max_col: int = 48,
50) -> str:
51 """Return a bordered table as a multi-line string."""
52 g = _glyphs()
53 n = len(headers)
54 if aligns is None:
55 aligns = ["left"] * n
56 elif len(aligns) < n:
57 aligns = list(aligns) + ["left"] * (n - len(aligns))
58
59 widths = [min(max_col, len(str(h))) for h in headers]
60 for row in rows:
61 for i, cell in enumerate(row[:n]):
62 widths[i] = min(max_col, max(widths[i], len(str(cell))))
63
64 def line(left: str, join: str, right: str) -> str:
65 return left + join.join(g["h"] * (w + 2) for w in widths) + right
66
67 def row_line(cells: Sequence[str]) -> str:
68 parts = [
69 f" {_cell(cells[i] if i < len(cells) else '', widths[i], align=aligns[i])} "
70 for i in range(n)
71 ]
72 return g["v"] + g["v"].join(parts) + g["v"]
73
74 out: list[str] = [
75 line(g["tl"], g["t"], g["tr"]),
76 row_line(headers),
77 line(g["l"], g["x"], g["r"]),
78 ]
79 for row in rows:
80 out.append(row_line(row))
81 out.append(line(g["bl"], g["b"], g["br"]))
82 return "\n".join(out)
83
84
85def print_table(
86 headers: Sequence[str],
87 rows: Sequence[Sequence[str]],
88 *,
89 aligns: Sequence[str] | None = None,
90 max_col: int = 48,
91 title: str | None = None,
92) -> None:
93 try:
94 sys.stdout.reconfigure(encoding="utf-8") # type: ignore[attr-defined]
95 except Exception:
96 pass
97 if title:
98 print(title)
99 print(format_table(headers, rows, aligns=aligns, max_col=max_col))
None print_table(Sequence[str] headers, Sequence[Sequence[str]] rows, *, Sequence[str]|None aligns=None, int max_col=48, str|None title=None)
dict[str, str] _glyphs()
str _cell(str text, int width, *, str align="left")
str format_table(Sequence[str] headers, Sequence[Sequence[str]] rows, *, Sequence[str]|None aligns=None, int max_col=48)