PrettyTable generates ASCII tables for terminal output from Python data. pip install prettytable. Basic: from prettytable import PrettyTable; t = PrettyTable(["Name", "Age"]). t.add_row(["Alice", 30]). print(t). Bulk: t.add_rows([["Bob",25],["Carol",35]]). Field names: PrettyTable(field_names=["A","B","C"]). Align: t.align["Name"] = "l" — l/r/c per column. t.align = "r" — all. Max width: t.max_width = 30. t._max_width["name"] = 20. Sort: t.sortby = "Age"; t.reversesort = True. Border: t.border = False. Header: t.header = False. Get string: t.get_string(fields=["Name"], start=0, end=5, sortby="Age"). Style: from prettytable import SINGLE_BORDER, DOUBLE_BORDER, MARKDOWN, ORGMODE; t.set_style(MARKDOWN). From CSV: from prettytable import from_csv; t = from_csv(open("f.csv")). From JSON: t = from_json('{"Name":["Alice"],"Age":[30]}'). Cursor: from prettytable import from_db_cursor; t = from_db_cursor(cursor). JSON out: t.get_json_string(). CSV out: t.get_csv_string(). t.get_html_string(). Float: t.float_format = ".2". t.int_format = "08". Claude Code generates prettytable terminal tables, report formatters, and CLI result displayers.
CLAUDE.md for PrettyTable
## PrettyTable Stack
- Version: prettytable >= 3.9 | pip install prettytable
- Create: PrettyTable(["Name", "Age"]) | t.add_row([...]) | t.add_rows([...])
- Align: t.align["col"] = "l"/"r"/"c" | t.align = "r" for all
- Sort: t.sortby = "col" | t.reversesort = True
- Style: t.set_style(MARKDOWN) | SINGLE_BORDER | DOUBLE_BORDER | ORGMODE
- Export: t.get_string() | t.get_csv_string() | t.get_json_string() | get_html_string()
- Import: from_csv(fh) | from_json(json_str) | from_db_cursor(cursor)
PrettyTable Terminal Table Pipeline
# app/tables.py — PrettyTable formatters, DB result display, and pandas integration
from __future__ import annotations
from typing import Any
from prettytable import (
DOUBLE_BORDER,
MARKDOWN,
ORGMODE,
SINGLE_BORDER,
PrettyTable,
from_db_cursor,
)
# ─────────────────────────────────────────────────────────────────────────────
# 1. Table factory helpers
# ─────────────────────────────────────────────────────────────────────────────
def make_table(
columns: list[str],
rows: list[list[Any]] | None = None,
align: str | dict[str, str] = "l",
max_width: int | None = None,
) -> PrettyTable:
"""
Create a PrettyTable with sensible defaults.
align: "l"/"r"/"c" for all columns, or {col: align} per column.
max_width: truncate all columns wider than this.
"""
t = PrettyTable(field_names=columns)
if isinstance(align, dict):
for col, a in align.items():
t.align[col] = a
else:
t.align = align
if max_width is not None:
t.max_width = max_width
if rows:
t.add_rows(rows)
return t
def dict_table(
data: list[dict[str, Any]],
columns: list[str] | None = None,
align: str = "l",
max_width: int | None = None,
) -> PrettyTable:
"""
Build a table from a list of dicts.
columns: explicit column order; defaults to keys of first row.
"""
if not data:
return PrettyTable(field_names=columns or [])
cols = columns or list(data[0].keys())
rows = [[str(row.get(c, "")) for c in cols] for row in data]
return make_table(cols, rows, align=align, max_width=max_width)
def markdown_table(
columns: list[str],
rows: list[list[Any]],
) -> str:
"""Return a Markdown-formatted table string."""
t = make_table(columns, rows)
t.set_style(MARKDOWN)
return t.get_string()
def bordered_table(
columns: list[str],
rows: list[list[Any]],
double: bool = False,
) -> str:
"""Return a table with single or double line borders."""
t = make_table(columns, rows)
t.set_style(DOUBLE_BORDER if double else SINGLE_BORDER)
return t.get_string()
def org_table(columns: list[str], rows: list[list[Any]]) -> str:
"""Return an Emacs org-mode table string."""
t = make_table(columns, rows)
t.set_style(ORGMODE)
return t.get_string()
# ─────────────────────────────────────────────────────────────────────────────
# 2. Sorted and filtered output
# ─────────────────────────────────────────────────────────────────────────────
def print_sorted(
t: PrettyTable,
sort_by: str,
reverse: bool = False,
fields: list[str] | None = None,
limit: int | None = None,
) -> None:
"""Print a subset of a table, sorted by a column."""
kwargs: dict[str, Any] = {
"sortby": sort_by,
"reversesort": reverse,
}
if fields:
kwargs["fields"] = fields
if limit is not None:
kwargs["end"] = limit
print(t.get_string(**kwargs))
def top_n_table(
columns: list[str],
rows: list[list[Any]],
sort_by: str,
n: int = 10,
reverse: bool = True,
) -> str:
"""Return the top-N rows sorted by a numeric column."""
t = make_table(columns, rows)
return t.get_string(sortby=sort_by, reversesort=reverse, end=n)
# ─────────────────────────────────────────────────────────────────────────────
# 3. Database cursor helpers
# ─────────────────────────────────────────────────────────────────────────────
def table_from_cursor(cursor) -> PrettyTable:
"""
Build a PrettyTable directly from a DB-API 2.0 cursor.
Works with sqlite3, psycopg2, psycopg3, pymysql, etc.
"""
return from_db_cursor(cursor)
def print_query_result(cursor, max_width: int = 40) -> None:
"""Execute a query and print results as a table."""
t = from_db_cursor(cursor)
t.max_width = max_width
t.align = "l"
print(t)
def sqlite_query_table(db_path: str, sql: str, params: tuple = ()) -> str:
"""
Run a SQLite query and return its output as a formatted table string.
Useful for quick ad-hoc reporting.
"""
import sqlite3
with sqlite3.connect(db_path) as conn:
cur = conn.execute(sql, params)
t = from_db_cursor(cur)
t.align = "l"
t.max_width = 50
return t.get_string()
# ─────────────────────────────────────────────────────────────────────────────
# 4. Pandas integration
# ─────────────────────────────────────────────────────────────────────────────
def from_dataframe(
df,
columns: list[str] | None = None,
max_rows: int | None = None,
max_width: int | None = None,
) -> PrettyTable:
"""
Create a PrettyTable from a pandas DataFrame.
columns: which columns to include; defaults to all.
max_rows: truncate to first max_rows rows.
"""
cols = columns or list(df.columns)
data = df[cols]
if max_rows is not None:
data = data.head(max_rows)
rows = data.values.tolist()
rows = [[str(v) for v in row] for row in rows]
return make_table(cols, rows, max_width=max_width)
def dataframe_summary_table(df) -> str:
"""
Generate a summary table for a DataFrame: column name, dtype, non-nulls, unique count.
"""
rows = []
for col in df.columns:
rows.append([
col,
str(df[col].dtype),
str(df[col].notna().sum()),
str(df[col].nunique()),
str(df[col].iloc[0]) if len(df) > 0 else "",
])
t = make_table(
["Column", "Dtype", "Non-Null", "Unique", "Sample"],
rows,
align={"Column": "l", "Dtype": "l", "Non-Null": "r", "Unique": "r", "Sample": "l"},
)
return t.get_string()
# ─────────────────────────────────────────────────────────────────────────────
# 5. Export helpers
# ─────────────────────────────────────────────────────────────────────────────
def to_markdown(t: PrettyTable) -> str:
t.set_style(MARKDOWN)
return t.get_string()
def to_csv_string(t: PrettyTable) -> str:
return t.get_csv_string()
def to_html(t: PrettyTable, attributes: dict[str, str] | None = None) -> str:
attr_str = " ".join(f'{k}="{v}"' for k, v in (attributes or {}).items())
return t.get_html_string(attributes={"class": "table", **(attributes or {})})
def save_table(t: PrettyTable, path: str, fmt: str = "txt") -> None:
"""Save a table to a file. fmt: 'txt', 'csv', 'html', 'md'."""
if fmt == "csv":
content = to_csv_string(t)
elif fmt == "html":
content = to_html(t)
elif fmt == "md":
content = to_markdown(t)
else:
content = t.get_string()
with open(path, "w", encoding="utf-8") as fh:
fh.write(content)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== Basic table ===")
t = make_table(
["Name", "City", "Score", "Active"],
[
["Alice", "München", "98.5", "Yes"],
["Bob", "New York", "87.0", "Yes"],
["Charlie", "Tokyo", "92.3", "No"],
["Diana", "London", "95.1", "Yes"],
],
align={"Name": "l", "City": "l", "Score": "r", "Active": "c"},
)
print(t)
print("\n=== Sorted top-3 by Score ===")
print(t.get_string(sortby="Score", reversesort=True, end=3))
print("\n=== Markdown style ===")
print(markdown_table(
["Package", "Stars", "Description"],
[
["prettytable", "1.2k", "ASCII table formatting"],
["tabulate", "3.4k", "Table formatting library"],
["rich", "45k", "Rich text and beautiful formatting"],
],
))
print("\n=== Single border ===")
print(bordered_table(
["Host", "Port", "Status"],
[["db.prod", "5432", "UP"],
["db.replica","5432","UP"],
["redis", "6379", "DOWN"]],
))
print("\n=== Double border ===")
print(bordered_table(
["Month", "Revenue", "Users"],
[["Jan", "$12,500", "1,234"],
["Feb", "$14,200", "1,567"],
["Mar", "$16,800", "1,890"]],
double=True,
))
print("\n=== dict_table ===")
data = [
{"name": "Alice", "score": 98, "rank": 1},
{"name": "Bob", "score": 87, "rank": 2},
{"name": "Carol", "score": 92, "rank": 3},
]
print(dict_table(data).get_string(sortby="rank"))
print("\n=== Export formats ===")
simple = make_table(["a", "b"], [[1, 2], [3, 4]])
print(f"CSV: {to_csv_string(simple).strip()!r}")
print(f"MD: {to_markdown(simple).strip()!r}")
For the tabulate alternative — tabulate is similar in purpose (ASCII table formatting) but returns a string rather than an object, so you can’t modify alignment or sort after construction; PrettyTable provides a mutable object (t.align["col"], t.sortby, t.max_width) and built-in DB cursor and DataFrame ingestion, while tabulate is better when you just need tabulate(data, headers=...) as a one-liner without needing to reconfigure the table. For the rich.table.Table alternative — rich.Table supports ANSI color, live updates, emoji, box styles from a large library, and renders beautifully in supported terminals; PrettyTable targets plain ASCII output that works equally well in logs, files, email bodies, and non-color terminals — choose rich when color is available, PrettyTable when you need portable plain-text output. The Claude Skills 360 bundle includes PrettyTable skill sets covering PrettyTable() constructor, add_row()/add_rows(), align per-column, border/header control, set_style(MARKDOWN/SINGLE_BORDER/DOUBLE_BORDER/ORGMODE), get_string() with sortby/fields/start/end, from_db_cursor() for DB results, dict_table() helper, from_dataframe() pandas integration, dataframe_summary_table() column profiler, markdown_table()/bordered_table() convenience wrappers, and export to CSV/HTML/Markdown. Start with the free tier to try ASCII table formatting code generation.