Claude Code for tabulate: Python Table Formatting — Claude Skills 360 Blog
Blog / AI / Claude Code for tabulate: Python Table Formatting
AI

Claude Code for tabulate: Python Table Formatting

Published: February 2, 2028
Read time: 5 min read
By: Claude Skills 360

tabulate formats list-of-lists or list-of-dicts as text tables. pip install tabulate. Basic: from tabulate import tabulate. print(tabulate([[1,"Alice",99.5],[2,"Bob",87.2]], headers=["id","name","score"])). Dict list: tabulate([{"a":1,"b":2},{"a":3,"b":4}], headers="keys"). Format: tablefmt="plain" (default) | "simple" (lines) | "grid" | "pipe" (Markdown) | "github" | "rst" | "latex" | "html" | "tsv" | "rounded_grid" | "heavy_grid" | "double_grid". Numbers: floatfmt=".2f" — 2 decimal places. intfmt="," — thousands separator. Alignment: colalign=("right","left","decimal"). showindex: showindex=True — 0-based row numbers. showindex="always". showindex=range(1,N). Headers style: headers=["Name","Score"]. headers="keys" for dicts. headers="firstrow" — first list row is header. Missing: missingval="N/A". numalign: numalign="right" (default) | "left" | "center" | "decimal". stralign: stralign="left". Width: maxcolwidths=30 — truncate. disable_numparse=True — treat numbers as strings. pandas: tabulate(df, headers="keys", tablefmt="pipe", showindex=False). Tuple input. Generator input. Single column: tabulate([[v] for v in values], headers=["Value"]). Claude Code generates tabulate table formatters, Markdown output functions, and CLI report formatters.

CLAUDE.md for tabulate

## tabulate Stack
- Version: tabulate >= 0.9 | pip install tabulate
- Basic: tabulate(data, headers=["A","B"]) where data is list-of-lists or list-of-dicts
- Dict: tabulate(list_of_dicts, headers="keys") — auto-extracts column names
- Formats: "simple" | "grid" | "pipe" (Markdown) | "github" | "html" | "rst" | "latex"
- Numbers: floatfmt=".2f" | intfmt="," | numalign="decimal"
- Align: colalign=("right","left","center") — per-column
- Truncate: maxcolwidths=30 — wrap/truncate long cell content

tabulate Table Formatting Pipeline

# app/tables.py — tabulate formatting for CLI, Markdown, HTML, and reports
from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Sequence

from tabulate import tabulate


# ─────────────────────────────────────────────────────────────────────────────
# 1. All table formats demonstrated
# ─────────────────────────────────────────────────────────────────────────────

SAMPLE_DATA = [
    {"name": "Alice", "score": 97.5, "rank": 1, "passed": True},
    {"name": "Bob",   "score": 84.2, "rank": 2, "passed": True},
    {"name": "Carol", "score": 61.0, "rank": 3, "passed": False},
    {"name": "Dave",  "score": 58.9, "rank": 4, "passed": False},
]


def show_all_formats(data: list[dict] = SAMPLE_DATA) -> None:
    """Print the same table in several common formats."""
    formats = [
        ("plain",        "Plain (no borders)"),
        ("simple",       "Simple (lines under header)"),
        ("grid",         "Grid (full ASCII grid)"),
        ("rounded_grid", "Rounded Grid"),
        ("pipe",         "Pipe (Markdown)"),
        ("github",       "GitHub Flavored Markdown"),
        ("rst",          "reStructuredText"),
        ("html",         "HTML <table>"),
    ]
    for fmt, label in formats:
        print(f"\n── {label} (tablefmt={fmt!r}) ──")
        print(tabulate(data, headers="keys", tablefmt=fmt, floatfmt=".1f"))


# ─────────────────────────────────────────────────────────────────────────────
# 2. Number formatting
# ─────────────────────────────────────────────────────────────────────────────

def format_financial_table(records: list[dict]) -> str:
    """Format a financial table with currency, percent, and integer formatting."""
    rows = [
        [
            r["product"],
            r["revenue"],
            r["cost"],
            r["margin"],
            r["units"],
        ]
        for r in records
    ]
    return tabulate(
        rows,
        headers=["Product", "Revenue ($)", "Cost ($)", "Margin %", "Units"],
        tablefmt="simple",
        floatfmt=("", ",.2f", ",.2f", ".1%", ""),
        intfmt=",",
        colalign=("left", "right", "right", "right", "right"),
    )


# ─────────────────────────────────────────────────────────────────────────────
# 3. Markdown tables for docs and GitHub PRs
# ─────────────────────────────────────────────────────────────────────────────

def to_markdown(
    data: list[dict],
    floatfmt: str = ".2f",
    showindex: bool = False,
) -> str:
    """
    tablefmt="pipe" produces GitHub/CommonMark Markdown tables.
    Paste directly into README.md, PR descriptions, or Confluence pages.
    """
    return tabulate(
        data,
        headers="keys",
        tablefmt="pipe",
        floatfmt=floatfmt,
        showindex=showindex,
    )


def benchmark_results_markdown(results: list[dict]) -> str:
    """Format benchmark results as a Markdown table for CI comments."""
    rows = sorted(results, key=lambda r: r.get("mean_ms", 0))
    return tabulate(
        rows,
        headers={
            "name":    "Test",
            "mean_ms": "Mean (ms)",
            "min_ms":  "Min (ms)",
            "stddev":  "Std Dev",
            "ops":     "Ops/sec",
        },
        tablefmt="github",
        floatfmt=".3f",
        intfmt=",",
        numalign="decimal",
        colalign=("left", "right", "right", "right", "right"),
    )


# ─────────────────────────────────────────────────────────────────────────────
# 4. HTML output
# ─────────────────────────────────────────────────────────────────────────────

def to_html_table(
    data: list[dict],
    floatfmt: str = ".2f",
    table_id: str = "data-table",
) -> str:
    """
    tablefmt="html" produces a <table> element.
    Wrap in a style block for reports or emails.
    """
    raw = tabulate(data, headers="keys", tablefmt="html", floatfmt=floatfmt)
    # Inject id for CSS targeting
    return raw.replace("<table>", f'<table id="{table_id}">', 1)


# ─────────────────────────────────────────────────────────────────────────────
# 5. CLI report — with row index and truncated columns
# ─────────────────────────────────────────────────────────────────────────────

def cli_report(
    data: list[dict],
    title: str = "",
    maxcolwidths: int = 40,
    showindex: bool = True,
) -> str:
    """
    maxcolwidths=N wraps long strings in cells.
    showindex=True adds a 0-based row number column.
    """
    lines: list[str] = []
    if title:
        lines.append(title)
        lines.append("=" * len(title))
    table = tabulate(
        data,
        headers="keys",
        tablefmt="rounded_grid",
        showindex=showindex,
        maxcolwidths=maxcolwidths,
        missingval="—",
    )
    lines.append(table)
    return "\n".join(lines)


# ─────────────────────────────────────────────────────────────────────────────
# 6. pandas DataFrame integration
# ─────────────────────────────────────────────────────────────────────────────

def dataframe_table(df, tablefmt: str = "pipe", floatfmt: str = ".3f") -> str:
    """
    tabulate works directly with pandas DataFrames.
    headers="keys" uses column names; showindex=False omits the DataFrame index.
    """
    return tabulate(
        df,
        headers="keys",
        tablefmt=tablefmt,
        floatfmt=floatfmt,
        showindex=False,
    )


# ─────────────────────────────────────────────────────────────────────────────
# 7. Mixed-type data with missing values
# ─────────────────────────────────────────────────────────────────────────────

def format_mixed(data: list[dict]) -> str:
    """Handle None/missing values and mixed types gracefully."""
    return tabulate(
        data,
        headers="keys",
        tablefmt="simple",
        missingval="N/A",
        floatfmt=".2f",
        numalign="decimal",
        stralign="left",
    )


# ─────────────────────────────────────────────────────────────────────────────
# 8. Comparison table (diff results)
# ─────────────────────────────────────────────────────────────────────────────

def diff_table(
    before: list[dict],
    after:  list[dict],
    key:    str,
    metric: str,
) -> str:
    """
    Side-by-side comparison: before vs after for a given metric.
    Adds a delta column.
    """
    before_map = {r[key]: r[metric] for r in before}
    after_map  = {r[key]: r[metric] for r in after}

    all_keys = sorted(set(before_map) | set(after_map))
    rows = []
    for k in all_keys:
        b = before_map.get(k)
        a = after_map.get(k)
        delta = (a - b) if (a is not None and b is not None) else None
        rows.append({
            key:         k,
            "before":    b,
            "after":     a,
            "delta":     delta,
            "change %":  (delta / b * 100) if b and delta is not None else None,
        })

    return tabulate(
        rows,
        headers="keys",
        tablefmt="simple",
        floatfmt=".2f",
        missingval="N/A",
        numalign="decimal",
        colalign=("left", "right", "right", "right", "right"),
    )


# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("=== Sample formats ===")
    for fmt in ["simple", "grid", "pipe", "github"]:
        print(f"\n[{fmt}]")
        print(tabulate(SAMPLE_DATA, headers="keys", tablefmt=fmt, floatfmt=".1f"))

    print("\n=== Financial table ===")
    financial = [
        {"product": "Widget A", "revenue": 15_234.50, "cost": 8_900.00, "margin": 0.416, "units": 508},
        {"product": "Widget B", "revenue": 28_750.00, "cost": 19_200.00, "margin": 0.332, "units": 575},
        {"product": "Gadget X", "revenue":  9_100.25, "cost":  4_050.00, "margin": 0.555, "units": 607},
    ]
    print(format_financial_table(financial))

    print("\n=== Benchmark markdown ===")
    bench = [
        {"name": "sorted()", "mean_ms": 0.042, "min_ms": 0.038, "stddev": 0.004, "ops": 23_000},
        {"name": "insertion_sort", "mean_ms": 0.315, "min_ms": 0.290, "stddev": 0.025, "ops": 3_173},
        {"name": "bubble_sort",    "mean_ms": 1.840, "min_ms": 1.750, "stddev": 0.090, "ops": 543},
    ]
    print(benchmark_results_markdown(bench))

    print("\n=== Missing values ===")
    mixed = [
        {"name": "Alice", "score": 97.5, "note": None},
        {"name": "Bob",   "score": None, "note": "absent"},
    ]
    print(format_mixed(mixed))

    print("\n=== Diff table ===")
    before = [{"module": "auth", "p50": 12.4}, {"module": "api", "p50": 45.1}]
    after  = [{"module": "auth", "p50": 10.2}, {"module": "api", "p50": 48.7}]
    print(diff_table(before, after, key="module", metric="p50"))

For the Rich Table alternative — Rich’s Table renders ANSI-colored, box-drawing-character tables directly to a terminal with full color support and dynamic layout, while tabulate outputs plain strings that can be printed, written to files, embedded in Markdown documentation, pasted into GitHub PR comments (tablefmt="pipe"), compiled into LaTeX reports (tablefmt="latex"), or emailed as HTML (tablefmt="html") — the table string is just a string, so it composes with logging, file output, and string formatting without a console object. For the print + format() alternative — manually right-padding strings with f"{val:>10}" requires calculating column widths, handling Unicode width, aligning numeric columns, and producing a consistent format across different data shapes, while tabulate(data, headers="keys") handles all alignment automatically, detects numeric columns for right-alignment, and switches from ASCII dashes to proper box-drawing characters with tablefmt="rounded_grid". The Claude Skills 360 bundle includes tabulate skill sets covering all major tablefmt values (simple/grid/pipe/github/html/rst/latex), floatfmt and intfmt number formatting, colalign per-column alignment, showindex row numbering, missingval for None handling, maxcolwidths for truncation, pandas DataFrame tabulation, Markdown table generation for PR comments, HTML table output, benchmark comparison tables, and side-by-side diff tables. Start with the free tier to try table formatting code generation.

Keep Reading

AI

Claude Code for email.contentmanager: Python Email Content Accessors

Read and write EmailMessage body content with Python's email.contentmanager module and Claude Code — email contentmanager ContentManager for the class that maps content types to get and set handler functions allowing EmailMessage to support get_content and set_content with type-specific behaviour, email contentmanager raw_data_manager for the ContentManager instance that handles raw bytes and str payloads without any conversion, email contentmanager content_manager for the standard ContentManager instance used by email.policy.default that intelligently handles text plain text html multipart and binary content types, email contentmanager get_content_text for the handler that returns the decoded text payload of a text-star message part as a str, email contentmanager get_content_binary for the handler that returns the raw decoded bytes payload of a non-text message part, email contentmanager get_data_manager for the get-handler lookup used by EmailMessage get_content to find the right reader function for the content type, email contentmanager set_content text for the handler that creates and sets a text part correctly choosing charset and transfer encoding, email contentmanager set_content bytes for the handler that creates and sets a binary part with base64 encoding and optional filename Content-Disposition, email contentmanager EmailMessage get_content for the method that reads the message body using the registered content manager handlers, email contentmanager EmailMessage set_content for the method that sets the message body and MIME headers in one call, email contentmanager EmailMessage make_alternative make_mixed make_related for the methods that convert a simple message into a multipart container, email contentmanager EmailMessage add_attachment for the method that attaches a file or bytes to a multipart message, and email contentmanager integration with email.message and email.policy and email.mime and io for building high-level email readers attachment extractors text body accessors HTML readers and policy-aware MIME construction pipelines.

5 min read Feb 12, 2029
AI

Claude Code for email.charset: Python Email Charset Encoding

Control header and body encoding for international email with Python's email.charset module and Claude Code — email charset Charset for the class that wraps a character set name with the encoding rules for header encoding and body encoding describing how to encode text for that charset in email messages, email charset Charset header_encoding for the attribute specifying whether headers using this charset should use QP quoted-printable encoding BASE64 encoding or no encoding, email charset Charset body_encoding for the attribute specifying the Content-Transfer-Encoding to use for message bodies in this charset such as QP or BASE64, email charset Charset output_codec for the attribute giving the Python codec name used to encode the string to bytes for the wire format, email charset Charset input_codec for the attribute giving the Python codec name used to decode incoming bytes to str, email charset Charset get_output_charset for returning the output charset name, email charset Charset header_encode for encoding a header string using the charset's header_encoding method, email charset Charset body_encode for encoding body content using the charset's body_encoding, email charset Charset convert for converting a string from the input_codec to the output_codec, email charset add_charset for registering a new charset with custom encoding rules in the global charset registry, email charset add_alias for adding an alias name that maps to an existing registered charset, email charset add_codec for registering a codec name mapping for use by the charset machinery, and email charset integration with email.message and email.mime and email.policy and email.encoders for building international email senders non-ASCII header encoders Content-Transfer-Encoding selectors charset-aware message constructors and MIME encoding pipelines.

5 min read Feb 11, 2029
AI

Claude Code for email.utils: Python Email Address and Header Utilities

Parse and format RFC 2822 email addresses and dates with Python's email.utils module and Claude Code — email utils parseaddr for splitting a display-name plus angle-bracket address string into a realname and email address tuple, email utils formataddr for combining a realname and address string into a properly quoted RFC 2822 address with angle brackets, email utils getaddresses for parsing a list of raw address header strings each potentially containing multiple comma-separated addresses into a list of realname address tuples, email utils parsedate for parsing an RFC 2822 date string into a nine-tuple compatible with time.mktime, email utils parsedate_tz for parsing an RFC 2822 date string into a ten-tuple that includes the UTC offset timezone in seconds, email utils parsedate_to_datetime for parsing an RFC 2822 date string into an aware datetime object with timezone, email utils formatdate for formatting a POSIX timestamp or the current time as an RFC 2822 date string with optional usegmt and localtime flags, email utils format_datetime for formatting a datetime object as an RFC 2822 date string, email utils make_msgid for generating a globally unique Message-ID string with optional idstring and domain components, email utils decode_rfc2231 for decoding an RFC 2231 encoded parameter value into a tuple of charset language and value, email utils encode_rfc2231 for encoding a string as an RFC 2231 encoded parameter value, email utils collapse_rfc2231_value for collapsing a decoded RFC 2231 tuple to a Unicode string, and email utils integration with email.message and email.headerregistry and datetime and time for building address parsers date formatters message-id generators header extractors and RFC-compliant email construction utilities.

5 min read Feb 10, 2029

Put these ideas into practice

Claude Skills 360 gives you production-ready skills for everything in this article — and 2,350+ more. Start free or go all-in.

Back to Blog

Get 360 skills free