tabulate Markdown and grid reports from dict rows: working versions (tested 2026-10-16)
Turn lists of dicts into GitHub Markdown or grid tables with tabulate, controlling column order, labels, missing values and numeric alignment.
passed6 passed · 0 failed2026-10-16 09:58 UTC- Status
- passed
- Last passed
- 2026-10-16 09:58 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions2
tabulate@0.10.0pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 38883899161
The code
Exactly the files that passed. Your agent can pull them with get_recipe("tabulate-markdown-report") (install), or see them on GitHub.
requirements.txt
tabulate==0.10.0 pytest==9.1.1
report.py
from tabulate import tabulate
def render(rows, columns=None, tablefmt="github", **kwargs):
if columns is None:
return tabulate(rows, headers="keys", tablefmt=tablefmt, **kwargs)
headers = [label for _, label in columns]
data = [[row.get(key) for key, _ in columns] for row in rows]
return tabulate(data, headers=headers, tablefmt=tablefmt, **kwargs)
def markdown_report(rows, columns=None, **kwargs):
return render(rows, columns, tablefmt="github", **kwargs)
def grid_report(rows, columns=None, **kwargs):
return render(rows, columns, tablefmt="grid", **kwargs)
tests/test_report.py
from report import grid_report, markdown_report, render
COLUMNS = [("name", "Repo"), ("stars", "Stars"), ("lang", "Lang")]
ROWS = [
{"name": "alpha", "stars": 12, "lang": "py"},
{"name": "beta", "stars": 3},
]
def test_markdown_projects_dict_rows_in_declared_order():
out = markdown_report(ROWS, COLUMNS, missingval="-")
assert out.splitlines() == [
"| Repo | Stars | Lang |",
"|--------|---------|--------|",
"| alpha | 12 | py |",
"| beta | 3 | - |",
]
def test_grid_uses_box_drawing():
out = grid_report(ROWS, [("name", "Repo")], missingval="-")
assert out.splitlines()[0] == "+--------+"
assert "| alpha |" in out
assert "| beta |" in out
def test_columns_not_supplied_falls_back_to_keys_union():
out = render([{"b": 1}, {"a": 2}, {"c": 3}])
assert out.splitlines()[0] == "| b | a | c |"
assert out.splitlines()[2] == "| 1 | | |"
def test_ragged_rows_get_missingval():
out = markdown_report(ROWS, COLUMNS, missingval="n/a")
assert "| beta | 3 | n/a |" in out
def test_disable_numparse_changes_alignment_of_numeric_strings():
parsed = markdown_report([{"id": "007"}], [("id", "ID")]).splitlines()
raw = markdown_report([{"id": "007"}], [("id", "ID")], disable_numparse=True).splitlines()
assert parsed[0] == "| ID |" and parsed[2] == "| 007 |"
assert raw[0] == "| ID |" and raw[2] == "| 007 |"
def test_floatfmt_rounds_numeric_cells():
out = markdown_report([{"ratio": 3.14159}], [("ratio", "Ratio")], floatfmt=".2f")
assert out.splitlines()[2] == "| 3.14 |"
pytest.ini
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.