Skip to main content

Create styled excel reports with declarative python.

Project description

xpyxl — Excel in Python

Compose polished spreadsheets with pure Python—no manual coordinates. You assemble rows/columns/cells; xpyxl handles layout, rendering, and styling with utility-style classes.

Core ideas

  • Positionless composition: Build sheets declaratively from row, col, cell, table, vstack, and hstack.
  • Composable styling: Tailwind-inspired utilities (typography, colors, alignment, number formats) applied via style=[...].
  • Deterministic rendering: Pure-data trees compiled into .xlsx files with predictable output—ideal for tests and CI diffing.

Installation

uv add xpyxl
pip install xpyxl

Getting started

import xpyxl as x

report = (
    x.workbook()[
        x.sheet("Summary")[
            x.row(style=[x.text_2xl, x.bold, x.text_blue])["Q3 Sales Overview"],
            x.row(style=[x.text_sm, x.text_gray])["Region", "Units", "Price"],
            x.row(style=[x.bg_primary, x.text_white, x.bold])["EMEA", 1200, 19.0],
            x.row()["APAC", 900, 21.0],
            x.row()["AMER", 1500, 18.5],
        ]
    ]
)

report.save("report.xlsx")

Rendering Engines

xpyxl supports multiple rendering engines, allowing you to choose the best one for your needs:

  • openpyxl (default): Full-featured engine with comprehensive Excel support. Best for complex workbooks with advanced formatting.
  • xlsxwriter: Fast, memory-efficient engine. Ideal for large datasets and performance-critical applications.

Using Different Engines

Specify the engine when saving:

import xpyxl as x

workbook = x.workbook()[
    x.sheet("Data")[
        x.row(style=[x.bold])["Name", "Value"],
        x.row()["Item A", 100],
        x.row()["Item B", 200],
    ]
]

# Use openpyxl (default)
workbook.save("output-openpyxl.xlsx", engine="openpyxl")

# Use xlsxwriter
workbook.save("output-xlsxwriter.xlsx", engine="xlsxwriter")

Both engines produce equivalent Excel files, but may have subtle differences in:

  • File size and memory usage
  • Rendering performance
  • Support for advanced Excel features

Choose openpyxl for maximum compatibility and feature support, or xlsxwriter when performance and memory efficiency are priorities.

Performance Benchmarks

Benchmark results comparing the two rendering engines across different scenarios (averaged over 3 runs):

Big Tables

Size Engine Time (s) Memory (MB)
100 openpyxl 0.0977 0.68
100 xlsxwriter 0.0325 0.50
1,000 openpyxl 0.9354 3.10
1,000 xlsxwriter 0.2565 2.04
10,000 openpyxl 11.2455 31.26
10,000 xlsxwriter 4.0539 17.81
50,000 openpyxl 55.5080 156.33
50,000 xlsxwriter 20.5686 94.43

Summary: xlsxwriter is 2.7-3.7x faster and uses 1.4-1.8x less memory for large tables. The openpyxl engine has been optimized with style object caching, providing ~15% performance improvement compared to previous versions.

Simple Layouts

Engine Time (s) Memory (MB)
openpyxl 0.0084 0.38
xlsxwriter 0.0058 0.34

Summary: xlsxwriter is 1.5x faster with similar memory usage.

Complex Layouts

Engine Time (s) Memory (MB)
openpyxl 0.1462 0.69
xlsxwriter 0.0454 0.52

Summary: xlsxwriter is 3.2x faster and uses 1.3x less memory for multi-sheet workbooks with styling.

Run benchmarks yourself:

uv run scripts/benchmark.py

Primitives

x.row(style=[x.bold, x.bg_warning])[1, 2, 3, 4, 5]
x.col(style=[x.italic])["a", "b", "c"]
x.cell(style=[x.text_green, x.number_precision])[42100]
  • row[...] accepts any sequence (numbers, strings, dataclasses…)
  • col[...] stacks values vertically
  • cell[...] wraps a single scalar
  • All primitives accept style=[...]

Component: table

x.table(...) renders a header + body with optional style overrides. Combine with vstack/hstack for dashboards and reports.

sales_table = x.table(
    header_style=[x.text_sm, x.text_gray, x.align_middle],
    style=[x.table_bordered, x.table_compact],
)[
    {"Region": "EMEA", "Units": 1200, "Price": 19.0},
    {"Region": "APAC", "Units": 900, "Price": 21.0},
    {"Region": "AMER", "Units": 1500, "Price": 18.5},
]

layout = x.vstack(
    x.row(style=[x.text_xl, x.bold])["Q3 Sales Overview"],
    x.space(),
    x.hstack(
        sales_table,
        x.cell(style=[x.text_sm, x.text_gray])["Generated with xpyxl"],
        gap=2,
    ),
)

Tables also accept pandas-friendly shapes:

  • records: table()[[{"region": "EMEA", "units": 1200}, ...]] derives the header from dict keys (missing keys are filled with None).
  • dict of lists: table()[{"region": ["EMEA", "APAC"], "units": [1200, 900]}] zips columns together (lengths must match). Headers are inferred from your keys and default to bold text on a muted background; override with header_style=[...] when needed.

Utility styles (non-exhaustive)

  • Typography: text_xs/_sm/_base/_lg/_xl/_2xl/_3xl, bold, italic, mono
  • Text colors: text_red, text_green, text_blue, text_orange, text_purple, text_black, text_gray
  • Backgrounds: bg_red, bg_primary, bg_muted, bg_success, bg_warning, bg_info
  • Layout & alignment: text_left, text_center, text_right, align_top/middle/bottom, wrap, nowrap, wrap_shrink, allow_overflow, row_height(...), row_width(...)
  • Use allow_overflow when you want to keep a column narrow and let the text spill into adjacent empty cells, row_height(32) to force a specific row height, and row_width(12) to pin a column width.
  • Borders: border_all, border_top, border_bottom, border_left, border_right, border_x, border_y, border_red, border_green, border_blue, border_orange, border_purple, border_black, border_gray, border_white, border_muted, border_primary, border_thin, border_medium, border_thick, border_dashed, border_dotted, border_double, border_none
  • Tables: table_bordered, table_banded, table_compact
  • Number/date formats: number_comma, number_precision, percent, currency_usd, currency_eur, date_short, datetime_short, time_short

Border utilities can sit on individual cells or be applied at the row/column level for fast outlines.

Mix and match utilities freely—what you see is what you get.

Layout helpers

  • vstack(a, b, c, gap=1, style=[x.border_all]) vertically stacks components with optional blank rows and shared styles (great for card-like borders).
  • hstack(a, b, gap=1, style=[x.border_all]) arranges components side by side with configurable column gaps and shared wrapper styles.
  • space(rows=1, height=None) inserts empty rows (optionally with a fixed height) in a vstack or empty columns when dropped into an hstack.
  • sheet(name, background_color="#F8FAFC") sets a sheet-wide background fill; the first ~200 rows and 80 columns are painted so the grid feels cohesive.

Examples & Tests

The tests/ directory contains example modules that demonstrate various features of xpyxl. Each module exports a build_workbook() function (or build_sample_workbook() for multi-sheet examples) that returns a SheetNode or list of SheetNode objects.

  • Multi-sheet sales demo: tests/multi_sheet_sales_demo.py - showcases tables, stacks, spacing, and utility styles across multiple sheets.
  • Border styles demo: tests/border_styles_demo.py - demonstrates border utilities at cell, row, and column levels.
  • Wrap styles demo: tests/wrap_styles_demo.py - shows text wrapping and overflow utilities.
  • Row height demo: tests/row_height_demo.py - examples of manual row height and width controls.
  • Big table demo: tests/big_table_demo.py - performance test with a 1k-row table.

Running Tests

Run all test modules to generate combined Excel files with both rendering engines:

uv run scripts/run_tests.py

This will:

  • Collect sheets from all test modules
  • Combine them into a single workbook
  • Generate two output files in .testing/:
    • combined-output-openpyxl.xlsx (rendered with openpyxl engine)
    • combined-output-xlsxwriter.xlsx (rendered with xlsxwriter engine)

Each test module contributes one or more sheets to the combined workbook, allowing you to compare rendering output between engines.

Types & ergonomics

  • Modern Python with full type hints.
  • Pure Python stack traces; easy to debug, script, and test.
  • Deterministic rendering for stable diffs in CI.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xpyxl-0.5.1.tar.gz (22.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xpyxl-0.5.1-py3-none-any.whl (22.8 kB view details)

Uploaded Python 3

File details

Details for the file xpyxl-0.5.1.tar.gz.

File metadata

  • Download URL: xpyxl-0.5.1.tar.gz
  • Upload date:
  • Size: 22.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for xpyxl-0.5.1.tar.gz
Algorithm Hash digest
SHA256 cf3392c881facbeb322dba05affbbdb497cc363956ebfff1d527300cb21741e7
MD5 46a44c5b3fc8f23aca5ac2badd958cc7
BLAKE2b-256 c659701d6d71a4af75f5fe288d8a10c4930f4725a17a449c40a0c55c37b86683

See more details on using hashes here.

Provenance

The following attestation bundles were made for xpyxl-0.5.1.tar.gz:

Publisher: publish.yml on dakixr/xpyxl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file xpyxl-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: xpyxl-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 22.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for xpyxl-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 970a1b3a40d7ebdf24ee45d551a85493bba56d475ec0f560c833fbdc3147d5e8
MD5 08f4d0e0cbf54fe4d8112a2c11d9c60f
BLAKE2b-256 9c2ef1758530e391f63506bfaa35acfb6eb449122b56845c60cfdca014650099

See more details on using hashes here.

Provenance

The following attestation bundles were made for xpyxl-0.5.1-py3-none-any.whl:

Publisher: publish.yml on dakixr/xpyxl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page