High-performance Excel and CSV file generation for Python, powered by Rust. ~7x-9x faster than XlsxWriter, ~5x faster CSV (records) than Python's csv module, and ~12x faster Pandas DataFrame → CSV than pandas.to_csv via zero-copy Arrow.
from rustpy_xlsxwriter import FastExcel
FastExcel("report.xlsx").sheet("Sheet1", records).save()pip install rustpy-xlsxwriterBenchmarked via benchmark.py — run python benchmark.py to reproduce:
| Output | Input type | Records | RustPy | Baseline | Speedup |
|---|---|---|---|---|---|
| Excel | Records (list of dicts) | 500K | ~2.99s | ~26.72s | 8.9x |
| 1M | ~5.94s | ~51.92s | 8.7x | ||
| Pandas DataFrame | 500K | ~1.21s | ~9.11s | 7.6x | |
| 1M | ~2.41s | ~18.17s | 7.5x | ||
| Polars DataFrame | 500K | ~1.20s | ~8.59s | 7.1x | |
| 1M | ~2.42s | ~17.07s | 7.1x | ||
| CSV | Records (generator) | 500K | ~0.16s | ~0.77s | 4.8x |
| 1M | ~0.32s | ~1.53s | 4.8x | ||
| Pandas DataFrame | 1M | — | — | ~12x† | |
| Polars DataFrame | 1M | — | — | (use Polars' native write_csv — already faster)† |
Baselines: Excel → Python xlsxwriter; Records CSV → Python csv module; Pandas DataFrame CSV → DataFrame.to_csv().
† DataFrame → CSV rows measured on a separate machine — only speedup ratio shown. The Pandas path goes through zero-copy Arrow C Data Interface.
Key optimizations
- Arrow zero-copy for DataFrames — reads memory buffers directly via Arrow C Data Interface (Excel and CSV paths)
- First-row type caching for Records — detect column types once, skip type cascade
- LTO (Link-Time Optimization) and single codegen unit
- Constant memory mode for large files
- Pre-allocated Format objects (created once, reused across all cells)
- Dict
values()iteration instead of per-key hash lookups - Lazy processing of Python iterables (including generators)
- High-precision floating point with ryu
- Efficient zlib compression
Data Sources
- List of dicts, generators/iterators, Pandas DataFrame, Polars DataFrame
- All Python types:
str,int,float,bool,None,datetime,date - Numpy scalar types (
numpy.int64,numpy.float64,numpy.bool_)
Formatting & Styling
- Float number format (e.g.
"0.00") - Custom datetime format (e.g.
"dd/mm/yyyy") - Bold headers and bold index columns
- Freeze panes (rows, columns, per-sheet overrides)
Output Options
.xlsx(Excel) — auto-detected from file extension.csv/.tsv— auto-detected; ~5x faster than Pythoncsv(records), ~12x faster thanpandas.to_csv(Pandas DataFrame, via Arrow zero-copy)io.BytesIOin-memory buffer- Password protection (Excel only)
- Optional column auto-fit (
autofit=True/False) - Multiple sheets in a single file (Excel only)
API
- Fluent builder via
FastExcelclass - Context manager (
withstatement) for auto-save - Lower-level functional API (
write_worksheet,write_worksheets)
from rustpy_xlsxwriter import FastExcel
# Simple
FastExcel("output.xlsx").sheet("Users", [{"Name": "Alice", "Age": 30}]).save()
# Full-featured with context manager
with FastExcel("report.xlsx", password="secret") as f:
f.format(
float_format="0.00",
datetime_format="dd/mm/yyyy",
bold_headers=True,
index_columns=["ID"],
)
f.freeze(row=1)
f.sheet("Employees", employee_records)
f.sheet("Departments", dept_records)import pandas as pd
import polars as pl
from rustpy_xlsxwriter import FastExcel
# Pandas — Arrow zero-copy, dtype-aware
df_pd = pd.DataFrame({"Name": ["Alice", "Bob"], "Score": [88.5, 92.3]})
FastExcel("pandas.xlsx").sheet("Data", df_pd).save()
# Polars — native support, no .to_pandas() needed
df_pl = pl.DataFrame({"Name": ["Alice", "Bob"], "Score": [88.5, 92.3]})
FastExcel("polars.xlsx").sheet("Data", df_pl).save()# Freeze header row on all sheets
FastExcel("frozen.xlsx").freeze(row=1).sheet("Sheet1", data).save()
# Per-sheet freeze configuration
(
FastExcel("custom.xlsx")
.freeze(row=1) # all sheets
.freeze(row=1, col=2, sheet="Details") # override for Details
.sheet("Summary", summary_data)
.sheet("Details", detail_data)
.save()
)def rows():
for i in range(1_000_000):
yield {"id": i, "value": f"row_{i}"}
FastExcel("streamed.xlsx").sheet("Data", rows()).save()import io
from rustpy_xlsxwriter import FastExcel
buf = io.BytesIO()
FastExcel(buf).sheet("Sheet1", records).save()
xlsx_bytes = buf.getvalue() # send as HTTP response# Auto-detected from file extension — same API, no code change
FastExcel("output.csv").sheet("Sheet1", records).save()
FastExcel("output.tsv").sheet("Sheet1", records).save()
# Or use write_csv directly
from rustpy_xlsxwriter import write_csv
write_csv(records, "output.csv")
write_csv(records, "output.csv", delimiter=";") # custom delimiterfrom rustpy_xlsxwriter import write_worksheet, write_worksheets
write_worksheet(records, "output.xlsx", sheet_name="Sheet1", password="secret")
write_worksheets(
[{"Sheet1": records1}, {"Sheet2": records2}],
"output.xlsx",
freeze_panes={"general": {"row": 1, "col": 0}},
)| Method | Description |
|---|---|
FastExcel(target, *, password=None, autofit=True) |
Create writer for file path or BytesIO buffer |
.format(*, float_format, datetime_format, index_columns, bold_headers) |
Set number/datetime format and styling |
.freeze(*, row=None, col=None, sheet=None) |
Configure freeze panes (general or per-sheet) |
.sheet(name, data) |
Add a worksheet (list of dicts, generator, or DataFrame) |
.save() |
Write all sheets and save |
Supports context manager (with statement) — auto-saves on exit, skips save on exception.
| Function | Description |
|---|---|
write_worksheet(records, file_name, ...) |
Write single Excel sheet |
write_worksheets(records_with_sheet_name, file_name, ...) |
Write multiple Excel sheets |
write_csv(records, file_name, delimiter=",") |
Write CSV/TSV file |
validate_sheet_name(name) |
Check if sheet name is valid for Excel |
| Python Type | Excel Output |
|---|---|
str |
Text |
int |
Number |
float |
Number (with optional format) |
bool |
Boolean |
None |
Empty cell |
datetime.datetime |
DateTime (with optional format) |
datetime.date |
Date (with optional format) |
numpy.int64 / numpy.float64 |
Number |
numpy.bool_ |
Boolean |
dict, other |
String representation |
See examples/ for 13 runnable scripts + a Jupyter notebook:
| File | Description |
|---|---|
01_basic.py |
Single sheet from list of dicts |
02_multiple_sheets.py |
Multiple sheets in one file |
03_dataframe.py |
Pandas DataFrame with styling |
04_freeze_panes.py |
Freeze rows, columns, per-sheet config |
05_bytesio.py |
In-memory buffer for web frameworks |
06_generator.py |
Memory-efficient streaming (100K rows) |
07_password.py |
Password-protected workbook |
08_full_featured.py |
All features combined |
09_polars.py |
Polars DataFrame (native support) |
10_context_manager.py |
Auto-save with with statement |
11_datetime_format.py |
Custom datetime/date formatting |
12_bold_headers.py |
Bold header row |
13_autofit.py |
Column auto-fit toggle |
14_csv_tsv.py |
CSV/TSV output (~5x faster) |
quickstart.ipynb |
Jupyter notebook walkthrough |
# Unit tests (~1 second)
pytest tests/ -m "not benchmark"
# All tests including benchmarks
pytest tests/
# Benchmark only
python benchmark.pyTest structure (86 tests)
| File | Coverage |
|---|---|
test_metadata.py |
Package metadata functions |
test_validation.py |
Sheet name validation (unicode, length, special chars) |
test_write_single.py |
Single sheet: all types, generator, context manager, autofit |
test_write_multi.py |
Multiple sheets |
test_write_functional.py |
Functional API |
test_freeze_panes.py |
Freeze panes (single & multi-sheet) |
test_password.py |
Password protection |
test_bytesio.py |
In-memory buffer I/O |
test_dataframe.py |
Pandas DataFrame, numpy scalar types |
test_polars.py |
Polars DataFrame: types, datetime, date, null, styling |
test_styling.py |
Float format, datetime format, bold headers, index columns |
test_benchmark.py |
Performance benchmarks (Records + Pandas + Polars vs xlsxwriter) |
Contributions are welcome! Please submit issues or pull requests on the GitHub repository.
This project is licensed under the MIT License.
This project is powered by rust_xlsxwriter, PyO3, and maturin.