rustypyxl
A Rust-powered Excel (XLSX) library for Python with an openpyxl-compatible API.
It is also a standalone Rust crate: the same library is published on crates.io
as rustypyxl for use directly from Rust,
with no Python involved. See Using from Rust.
Installation
pip install rustypyxl
Using from Rust
The core is a normal Rust library -- the Python package is a thin binding over it.
[dependencies]
rustypyxl = "0.6"
use rustypyxl::{Workbook, CellValue};
let mut wb = Workbook::new();
wb.create_sheet(Some("Data".to_string())).unwrap();
wb.set_cell_value_in_sheet("Data", 1, 1, CellValue::from("Hello")).unwrap();
wb.save("output.xlsx").unwrap();
Usage
import rustypyxl
# Create or load a workbook
wb = rustypyxl.Workbook()
ws = wb.create_sheet('Sheet1') # or: wb = rustypyxl.load_workbook('input.xlsx'); ws = wb.active
# openpyxl-style cell access
ws['A1'] = 'Hello'
ws['A2'] = 42.5
ws['A3'] = '=SUM(A1:A2)'
ws.cell(row=4, column=1).value = 'world'
print(ws['A1'].value) # -> "Hello"
# Append rows, merge, freeze, rename
ws.append(['Name', 'Age', 'Score'])
ws.merge_cells('A1:C1')
ws.freeze_panes = 'A2'
ws.title = 'Data'
# Iterate
for row in ws.iter_rows(values_only=True):
print(row)
wb.save('output.xlsx')
Bulk API (fastest for large grids)
When writing or reading many rows at once, the workbook-level bulk methods avoid per-cell Python overhead:
wb.write_rows('Data', [
['Name', 'Age', 'Score'],
['Alice', 30, 95.5],
['Bob', 25, 87.3],
])
data = wb.read_rows('Data', min_row=1, max_row=100)
Features
- openpyxl-compatible API: Familiar patterns (
ws['A1'],ws.cell(),ws.append(),iter_rows()) for easy migration - Read and write support: Full round-trip capability
- Cell values: Strings, numbers, booleans, dates, formulas
- Formatting: Fonts (incl. underline styles), alignment, fills, borders, number formats
- Workbook features: Hyperlinks, comments, named ranges, merged cells, freeze panes
- Sheet protection: Cell locking and worksheet protection
Not yet supported through the Python API: inserting/deleting rows and columns, charts, and images.
- Parquet import/export: Direct Parquet ↔ Excel conversion (bypasses Python FFI)
- S3 support: Works with boto3 via bytes I/O
- Bytes I/O: Load from bytes or file-like objects, save to bytes
- Configurable compression: Trade off speed vs file size
Parquet Import
Import large Parquet files directly into Excel worksheets. Data flows from Parquet → Rust → Excel without crossing the Python FFI boundary, making it very fast for large datasets.
import rustypyxl
wb = rustypyxl.Workbook()
wb.create_sheet("Data")
# Import parquet file into sheet
result = wb.insert_from_parquet(
sheet_name="Data",
path="large_dataset.parquet",
start_row=1,
start_col=1,
include_headers=True,
column_renames={"old_name": "new_name"}, # optional
columns=["col1", "col2", "col3"], # optional: select specific columns
)
print(f"Imported {result['rows_imported']} rows")
print(f"Data range: {result['range']}")
wb.save("output.xlsx")
Performance: ~4 seconds for 1M rows × 20 columns on M1 MacBook Pro.
Parquet Export
Export worksheet data to Parquet format with automatic type inference:
import rustypyxl
wb = rustypyxl.load_workbook("data.xlsx")
# Export entire sheet
result = wb.export_to_parquet(
sheet_name="Sheet1",
path="output.parquet",
has_headers=True, # first row contains headers
compression="snappy", # snappy, zstd, gzip, lz4, none
column_renames={"old": "new"}, # optional: rename columns
column_types={"date_col": "datetime"}, # optional: force column types
)
print(f"Exported {result['rows_exported']} rows")
print(f"File size: {result['file_size']} bytes")
# Export specific range
result = wb.export_range_to_parquet(
sheet_name="Sheet1",
path="subset.parquet",
min_row=1, min_col=1,
max_row=1000, max_col=5,
)
Supported column type hints: string, float64, int64, boolean, date, datetime, auto.
Loading from Bytes or File-like Objects
Load workbooks from in-memory bytes or file-like objects:
import rustypyxl
import io
# From bytes
with open("file.xlsx", "rb") as f:
data = f.read()
wb = rustypyxl.load_workbook(data)
# From file-like object (e.g., BytesIO, HTTP response)
wb = rustypyxl.load_workbook(io.BytesIO(data))
# Save to bytes (for HTTP responses, S3, etc.)
output_bytes = wb.save_to_bytes()
S3 Support
Use save_to_bytes() and load_workbook(bytes) with boto3 for S3 integration:
import boto3
import rustypyxl
s3 = boto3.client("s3")
# Load from S3
response = s3.get_object(Bucket="my-bucket", Key="path/to/file.xlsx")
wb = rustypyxl.load_workbook(response["Body"].read())
# Save to S3
data = wb.save_to_bytes()
s3.put_object(Bucket="my-bucket", Key="path/to/output.xlsx", Body=data)
This works with any S3-compatible service and uses boto3's credential handling (IAM roles, environment variables, etc.).
Streaming Writes (Low Memory)
For very large files, use WriteOnlyWorkbook which streams rows directly to disk:
import rustypyxl
wb = rustypyxl.WriteOnlyWorkbook("large_output.xlsx")
wb.create_sheet("Data")
for i in range(1_000_000):
wb.append_row([f"Row {i}", i, i * 1.5, i % 2 == 0])
wb.close() # Must call close() to finalize the file
This uses minimal memory regardless of file size, similar to openpyxl's write_only=True mode.
Benchmarks
Apple Silicon, openpyxl 3.1.5. Times are the minimum wall-clock over several runs (the fastest run is the one least disturbed by other processes), measured on an otherwise-idle machine. Your results will vary with data and hardware.
Write Performance (1M rows × 20 columns, mixed data)
| Method | Time | vs openpyxl |
|---|---|---|
rustypyxl WriteOnlyWorkbook (streaming) |
~4s | ~27x |
rustypyxl write_rows (build in memory, then save) |
~29s | ~4x |
openpyxl (write_only) |
~112s | — |
The streaming path is the one to use for large writes: it serializes rows
straight to disk and never holds the sheet in memory. write_rows is slower at
this scale because all 20M cell values cross the Python/Rust boundary and the
whole workbook is built in memory first; it is convenient for moderate sheets,
not the throughput path. (Loading straight from Parquet with
insert_from_parquet avoids the boundary entirely -- see the Parquet section.)
Read Performance (min wall time)
| Dataset | rustypyxl | calamine | openpyxl |
|---|---|---|---|
| 10k × 20 (numeric) | 0.09s | 0.10s | 0.73s |
| 10k × 20 (strings) | 0.11s | 0.11s | 1.55s |
| 100k × 20 (numeric) | 1.02s | 1.01s | 7.16s |
| 100k × 20 (mixed) | 1.25s | 1.16s | 11.9s |
rustypyxl and calamine (a read-only Rust reader, via python-calamine) are within noise of each other, both roughly 5-10x faster than openpyxl's read-only mode.
Memory Usage (Read)
| Dataset | rustypyxl | calamine | openpyxl |
|---|---|---|---|
| 10k × 20 | 29 MB | 9 MB | 11 MB |
| 50k × 20 | 62 MB | 48 MB | 53 MB |
| 100k × 20 | 103 MB | 95 MB | 106 MB |
rustypyxl keeps the whole workbook resident (like openpyxl's default mode), so its read footprint is comparable to openpyxl and above calamine's streaming reader. For low-memory reads of very large files, the trade-off is CPU vs RAM.
Memory Usage (Write)
| Dataset | rustypyxl (write_rows) |
WriteOnlyWorkbook |
openpyxl (write_only) |
|---|---|---|---|
| 10k × 20 | 10 MB | ~0 MB | 0.4 MB |
| 50k × 20 | 50 MB | ~0 MB | 0.4 MB |
| 100k × 20 | 99 MB | ~0 MB | 0.4 MB |
WriteOnlyWorkbook streams rows directly to disk, so its memory stays flat
regardless of file size -- the same idea as openpyxl's write_only mode.
Building from Source
# Install Rust and maturin
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
pip install maturin
# Build
maturin develop --release
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rustypyxl-0.7.0.tar.gz.
File metadata
- Download URL: rustypyxl-0.7.0.tar.gz
- Upload date:
- Size: 260.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6da68726a7427517cc0d7288be9329ca72e62db6682bc7772c55c74daaccf64f
|
|
| MD5 |
23b00fb2066e6816b112cf5dd44f6fe1
|
|
| BLAKE2b-256 |
d569b46c427c9074157311c9bf41fed785d87eb2f87e01be833eaab557b81577
|
Provenance
The following attestation bundles were made for rustypyxl-0.7.0.tar.gz:
Publisher:
publish.yml on freeeve/rustypyxl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypyxl-0.7.0.tar.gz -
Subject digest:
6da68726a7427517cc0d7288be9329ca72e62db6682bc7772c55c74daaccf64f - Sigstore transparency entry: 2177972604
- Sigstore integration time:
-
Permalink:
freeeve/rustypyxl@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/freeeve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file rustypyxl-0.7.0-cp310-abi3-win_amd64.whl.
File metadata
- Download URL: rustypyxl-0.7.0-cp310-abi3-win_amd64.whl
- Upload date:
- Size: 4.0 MB
- Tags: CPython 3.10+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ab0218a3e429f9460c79afcdb3c862010bb7287eea2bac8ccff27b15d0060b63
|
|
| MD5 |
555ec31e354c6200add471ac94808ecc
|
|
| BLAKE2b-256 |
2664e730cab07ff752d146c8ef011c5789c3de112382e643aef3a730e9874f07
|
Provenance
The following attestation bundles were made for rustypyxl-0.7.0-cp310-abi3-win_amd64.whl:
Publisher:
publish.yml on freeeve/rustypyxl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypyxl-0.7.0-cp310-abi3-win_amd64.whl -
Subject digest:
ab0218a3e429f9460c79afcdb3c862010bb7287eea2bac8ccff27b15d0060b63 - Sigstore transparency entry: 2177974462
- Sigstore integration time:
-
Permalink:
freeeve/rustypyxl@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/freeeve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 4.3 MB
- Tags: CPython 3.10+, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc9b99651d959eb8ee2134b42ad8721f5a0a2d29c6f91deceb996e25216301da
|
|
| MD5 |
d3265804d64a23e2fd1257c2790a7ad4
|
|
| BLAKE2b-256 |
bc62a22440952ee99e1deb09c057c2f15bd93162697eafeccf15c2bd5218d11a
|
Provenance
The following attestation bundles were made for rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_x86_64.whl:
Publisher:
publish.yml on freeeve/rustypyxl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_x86_64.whl -
Subject digest:
fc9b99651d959eb8ee2134b42ad8721f5a0a2d29c6f91deceb996e25216301da - Sigstore transparency entry: 2177974845
- Sigstore integration time:
-
Permalink:
freeeve/rustypyxl@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/freeeve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 4.1 MB
- Tags: CPython 3.10+, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4752943cf75f686dee70f8466ecf360f3364e4cd776ab3c7448720a2fd1653ab
|
|
| MD5 |
dc777d3cd3784c8ecdf6bf56cdab6ce6
|
|
| BLAKE2b-256 |
bbbe93264d84dd77c4968fdf024ed220109a5b4642761c37ebb77c4bea62ef16
|
Provenance
The following attestation bundles were made for rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_aarch64.whl:
Publisher:
publish.yml on freeeve/rustypyxl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypyxl-0.7.0-cp310-abi3-musllinux_1_2_aarch64.whl -
Subject digest:
4752943cf75f686dee70f8466ecf360f3364e4cd776ab3c7448720a2fd1653ab - Sigstore transparency entry: 2177974027
- Sigstore integration time:
-
Permalink:
freeeve/rustypyxl@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/freeeve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 4.1 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f6d1145162e2733e44c9faa6904f894458f80f1be9116177232c18cf1dd9b31
|
|
| MD5 |
201626764f5040bf75ec86d33dee6b85
|
|
| BLAKE2b-256 |
902fcba86ceca1cf09091ac88370efa2d09984d1a26ad92f1a83ba1e2bdcb842
|
Provenance
The following attestation bundles were made for rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
publish.yml on freeeve/rustypyxl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
5f6d1145162e2733e44c9faa6904f894458f80f1be9116177232c18cf1dd9b31 - Sigstore transparency entry: 2177973696
- Sigstore integration time:
-
Permalink:
freeeve/rustypyxl@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/freeeve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.9 MB
- Tags: CPython 3.10+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e87a2b893e7b780dfab52d980d5bcfc1c38e0cd6fe5170d347f0c5062f18e74c
|
|
| MD5 |
384d150585cbed93b9d4a81d98cc0fcc
|
|
| BLAKE2b-256 |
f3bab256c61b47c22be6266d06b79f74c19c5e5561ca05b2766b98fe033f94e7
|
Provenance
The following attestation bundles were made for rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
publish.yml on freeeve/rustypyxl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypyxl-0.7.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
e87a2b893e7b780dfab52d980d5bcfc1c38e0cd6fe5170d347f0c5062f18e74c - Sigstore transparency entry: 2177973431
- Sigstore integration time:
-
Permalink:
freeeve/rustypyxl@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/freeeve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Trigger Event:
release
-
Statement type:
File details
Details for the file rustypyxl-0.7.0-cp310-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: rustypyxl-0.7.0-cp310-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.7 MB
- Tags: CPython 3.10+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
207b18d3f23fe98d147a10b7f1c6daddc42e1e2921056677135d733270453fee
|
|
| MD5 |
5fdf7739c597cf7fe734f428d20a5d60
|
|
| BLAKE2b-256 |
34b24bb8c1eb441a4973ffad8188932a9f5fe1f360567747ef78daa42c6b70cd
|
Provenance
The following attestation bundles were made for rustypyxl-0.7.0-cp310-abi3-macosx_11_0_arm64.whl:
Publisher:
publish.yml on freeeve/rustypyxl
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
rustypyxl-0.7.0-cp310-abi3-macosx_11_0_arm64.whl -
Subject digest:
207b18d3f23fe98d147a10b7f1c6daddc42e1e2921056677135d733270453fee - Sigstore transparency entry: 2177972906
- Sigstore integration time:
-
Permalink:
freeeve/rustypyxl@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Branch / Tag:
refs/tags/v0.7.0 - Owner: https://github.com/freeeve
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@249e8cde1c7beedf1b8c9845a905b9cfbe52b0a7 -
Trigger Event:
release
-
Statement type: