Skip to main content

A fast .xlsx reader/writer with an openpyxl-style API, powered by Rust

Project description

oxlsx

A high-performance Rust library for reading and writing .xlsx files with full formatting support — designed as an openpyxl equivalent in Rust, with Python bindings (PyO3).

5–10x faster than openpyxl on write operations. Streaming reads via open_readonly().

Status

v0.1.1 released — feature-complete (phases 1–13) + bulk write, conditional formatting, sheet auto-filter, PatternFill, column utils, and dataframe_to_rows.

Install

Rust

# Cargo.toml
[dependencies]
oxlsx = { git = "https://github.com/alvaroroco/oxlsx", tag = "v0.1.0" }

Published to crates.io on each GitHub release.

Python (build from source via maturin)

Prebuilt abi3 wheels (a single cp38-abi3 wheel per platform, runs on CPython 3.8+) are published to PyPI on each release:

pip install oxlsx

To build from source, use maturin. The wheel build (abi3 extension-module) is configured in pyproject.toml, so no extra flags are needed:

pip install maturin
maturin develop --release   # install into the active venv
# or: maturin build --release  -> target/wheels/oxlsx-0.1.0-cp38-abi3-*.whl

Contributors running the in-crate PyO3 tests use the interpreter-linked path instead: cargo test --features python.

Phase Scope Status
1 ZIP parsing, styles, cell.font() / cell.fill() ✅ Done
2 Shared strings, Value::String/Bool, cell.text() ✅ Done
3 Multi-sheet, Value::Date, openpyxl API parity ✅ Done
4 Write support — Workbook::new() + wb.save() ✅ Done
5 Lazy / streaming reader (open_readonly()) ✅ Done
6 PyO3 Python bindings ✅ Done
7 Read-modify-write (open + edit + save) ✅ Done
8 Extended write features ✅ Done
9 Worksheet iteration API ✅ Done
10 Bulk write API (append, insert/delete rows/cols, move_range) ✅ Done
11 Rich formatting ✅ Done
12 Hyperlinks + plain-text comments ✅ Done
13 Freeze panes, print settings, named ranges, tables (lossless RMW) ✅ Done
14 Distribution / CI hardening 🔲 Planned
15 Pandas engine / tabular adapter 🔲 Planned

Usage

Reading

use oxlsx::{Workbook, Value};

fn main() -> Result<(), oxlsx::OxlsxError> {
    let wb = Workbook::open("file.xlsx")?;

    // Access by index or name
    let ws = wb.sheet(0).unwrap();
    let ws = wb.sheet_by_name("Employees").unwrap();

    println!("Sheets: {:?}", wb.sheetnames());
    println!("Active: {}", wb.active().unwrap().title());

    if let Some(cell) = ws.cell("A1") {
        match cell.value() {
            Value::String(s)  => println!("text: {s}"),
            Value::Number(n)  => println!("number: {n}"),
            Value::Bool(b)    => println!("bool: {b}"),
            Value::Date(d)    => println!("date: {d}"),
            Value::Empty      => println!("empty"),
        }

        if let Some(font) = cell.font() {
            println!("bold: {}", font.bold);
        }
        if let Some(fill) = cell.fill() {
            println!("fill: {}", fill.pattern_type);
        }
    }

    Ok(())
}

Writing

use oxlsx::{Color, Fill, Font, Workbook};

fn main() -> Result<(), oxlsx::OxlsxError> {
    let mut wb = Workbook::new();

    let ws = wb.active_mut().unwrap();
    ws.set_cell("A1", "Hello");
    ws.set_cell("B1", 42.0_f64);
    ws.set_cell("C1", true);
    ws.set_cell_font("A1", Font { bold: true, ..Default::default() });
    ws.set_cell_fill("A1", Fill {
        pattern_type: "solid".to_string(),
        fg_color: Some(Color::Argb("FFFF00".to_string())),
        bg_color: None,
    });

    wb.create_sheet("Data").set_cell("A1", "Sheet 2");

    wb.save("output.xlsx")?;
    Ok(())
}

API (openpyxl parity)

openpyxl (Python) oxlsx (Rust)
load_workbook("f.xlsx") Workbook::open("f.xlsx")
Workbook() Workbook::new()
wb.worksheets wb.worksheets()
wb.sheetnames wb.sheetnames()
wb.active wb.active() / wb.active_mut()
wb["Sheet1"] wb.sheet_by_name("Sheet1")
ws.title ws.title()
ws["A1"] ws.cell("A1")
ws["A1"] = value ws.set_cell("A1", value)
cell.valuestr/int/float/date cell.value()&Value
cell.font.bold cell.font()?.bold
cell.fill.fgColor cell.fill()?.fg_color
cell.comment cell.comment() / ws.set_cell_comment(...)
cell.hyperlink cell.hyperlink() / ws.set_cell_hyperlink(...)
ws.freeze_panes ws.freeze_panes() / ws.set_freeze_panes(...)
ws.print_area ws.print_area() / ws.set_print_area(...)
ws.print_title_rows / print_title_cols ws.print_title_rows() / ws.print_title_cols() (+ setters)
wb.defined_names wb.defined_names() (global) / ws.defined_names() (sheet-local)
ws.add_table(Table(...)) ws.add_table(Table::new(...))
ws.tables / del ws.tables["T"] ws.tables() / ws.remove_table("T")
wb.save("out.xlsx") wb.save("out.xlsx")

Benchmarks

Measured against openpyxl 3.1.2 on a 10k-row file (5 columns, mixed types). Absolute numbers are machine-dependent; the ratios are what matter. Reproduce with cargo bench + python3 benches/openpyxl_comparison.py.

Write

Operation openpyxl oxlsx Speedup
Single cell 1.90ms 0.18ms ~10.8x
1k rows × 5 cols 23.2ms 3.68ms ~6.3x
10k rows × 5 cols (50k cells) 222.4ms 54.1ms ~4.1x
3 sheets × 1k rows 35.9ms 6.80ms ~5.3x

Read (10k rows, 5 cols)

Operation openpyxl oxlsx Speedup
Metadata only (read_only / open_readonly()) 1.47ms 0.072ms ~20x
Open + iterate all cells 151ms (read_only) / 216ms (full) 33.4ms ~4.5–6.5x
Eager open() metadata, 10k 1.47ms 28ms tradeoff: eager loads the whole sheet for random access — use open_readonly() for streaming metadata

For metadata or large-file streaming, use open_readonly() (lazy, ~20x faster than openpyxl). Eager open() loads the full sheet to enable random ws.cell("A1") access.

Run benchmarks yourself:

cargo bench                          # Rust (criterion)
python3 benches/openpyxl_comparison.py  # Python baseline

Data Model

Type Description
Workbook Entry point — open or create, access sheets
Worksheet Access / set cells by Excel reference ("A1", "B3")
Cell Holds Value + StyleId + shared Arc<StyleSheet>
Value Empty | String(String) | Number(f64) | Bool(bool) | Date(NaiveDate) | Formula(String)
StyleSheet Registry mapping StyleIdFont + Fill
StyleId Newtype (usize) — prevents index confusion at compile time
Font Bold, italic, size, family, color
Fill Pattern type, foreground/background color
Color Argb(String) | Indexed(u32) | Theme(u32)
DefinedName Named range — name, value (+ comment, hidden); global or sheet-local
Table Worksheet table — display_name, ref, columns, optional TableStyleInfo; lossless RMW for unmodeled features

Architecture

  • SAX onlyquick-xml event loop, no DOM, no full-file loading
  • ZIP direct — reads from the container, no temp disk extraction
  • Arc<StyleSheet> — shared across Workbook → Worksheet → Cell, no lifetime params
  • PyO3-ready — all public structs are 'static compatible
  • Newtype patternStyleId(usize) prevents index confusion at compile time

Dependencies

Crate Role
quick-xml SAX XML parser + writer
zip OOXML container (deflate only, minimal wheel size)
thiserror Typed error enum
chrono Date resolution (NaiveDate)

Roadmap

✅ Done

  • ZIP container reading + SAX parsing
  • Styles: fonts, fills, colors, StyleId newtype
  • Shared strings → Value::String
  • Value::Bool, Value::Date (resolved at parse time)
  • Multi-sheet: wb.sheetnames(), wb.sheet_by_name(), ws.title()
  • Write support: Workbook::new(), ws.set_cell(), wb.save()
  • Style round-trip: bold font + solid fill survive write → open cycle
  • Criterion benchmarks + openpyxl comparison script
  • Streaming reader (open_readonly()), PyO3 bindings, read-modify-write
  • Extended write: formulas, merged cells, column widths, date numFmt
  • Iteration API (iter_rows/iter_cols/rows/columns/values, bounds)
  • Bulk write (append, insert/delete rows/cols, move_range) — Rust + Python; merges/dims shift, table-intersection guard, formulas verbatim
  • Rich formatting (number_format, alignment, border, protection)
  • Hyperlinks + plain-text comments
  • Freeze panes; print area / print titles
  • Named ranges — wb.defined_names() (global) + ws.defined_names() (sheet-local), conservative lossless passthrough
  • Tables — ws.add_table(), ws.tables, TableStyleInfo; lossless RMW (unmodeled table features preserved verbatim when untouched)

🔲 Planned

  • Phase 14 — PyPI publish + CI/CD

    • maturin publish workflow for PyPI releases
    • GitHub Actions wheel matrix for Linux, macOS, and Windows
    • Version management — Cargo.toml + pyproject.toml in sync
    • crates.io publish for the Rust library
  • Phase 15 — Pandas engine / tabular adapter

    • engine="oxlsx" integration via a dedicated adapter
    • DataFrame ↔ XLSX mapping, headers/index handling, and type conversions
    • Explicit adapter instead of monkeypatching by default

License

Licensed under either of:

at your option.

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

oxlsx-0.1.1.tar.gz (346.6 kB view details)

Uploaded Source

Built Distributions

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

oxlsx-0.1.1-cp38-abi3-win_amd64.whl (692.2 kB view details)

Uploaded CPython 3.8+Windows x86-64

oxlsx-0.1.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (871.5 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ x86-64

oxlsx-0.1.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (865.4 kB view details)

Uploaded CPython 3.8+manylinux: glibc 2.17+ ARM64

oxlsx-0.1.1-cp38-abi3-macosx_11_0_arm64.whl (788.7 kB view details)

Uploaded CPython 3.8+macOS 11.0+ ARM64

oxlsx-0.1.1-cp38-abi3-macosx_10_12_x86_64.whl (810.8 kB view details)

Uploaded CPython 3.8+macOS 10.12+ x86-64

File details

Details for the file oxlsx-0.1.1.tar.gz.

File metadata

  • Download URL: oxlsx-0.1.1.tar.gz
  • Upload date:
  • Size: 346.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oxlsx-0.1.1.tar.gz
Algorithm Hash digest
SHA256 c6c24260e55f43fede70b0f8ad5bce93a48e58bb091e1fb65df7e21fc03d41da
MD5 9ba3370c0e7324c7e5631284e867aa40
BLAKE2b-256 0a65c869d74e5a1fd6374c06890c96bc137fc662853c74cb2fe876a5166d52e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxlsx-0.1.1.tar.gz:

Publisher: release.yml on alvaroroco/oxlsx

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

File details

Details for the file oxlsx-0.1.1-cp38-abi3-win_amd64.whl.

File metadata

  • Download URL: oxlsx-0.1.1-cp38-abi3-win_amd64.whl
  • Upload date:
  • Size: 692.2 kB
  • Tags: CPython 3.8+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oxlsx-0.1.1-cp38-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 9bd0d94914ba8883c2175fc757f2414cfc4ab17a2c2bfb2d873a0216c135f244
MD5 2181fb28320eb35a68ef4919313799e6
BLAKE2b-256 bce8483ca61de78ade31fe0151d93f989ab4c06ba5099a5e1e410810f7423472

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxlsx-0.1.1-cp38-abi3-win_amd64.whl:

Publisher: release.yml on alvaroroco/oxlsx

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

File details

Details for the file oxlsx-0.1.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for oxlsx-0.1.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 7dd8202fb588fc905f6624fb40acf3562b053508e9f636f579ca964303875fe3
MD5 e6bd721bfec975c6688f59957370bca5
BLAKE2b-256 774f187d0b1139245c7184df7b3adc67389bf038c5b3bffb400dda77928cbb3f

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxlsx-0.1.1-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: release.yml on alvaroroco/oxlsx

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

File details

Details for the file oxlsx-0.1.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for oxlsx-0.1.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 8a0619e7b643773147fb26d3600dddcc49fbb68b6fbb40d247bb4f901bc41230
MD5 2bf7dbe531b2f6db2bc1b09e641d8464
BLAKE2b-256 db61b8f6aef48639478d0900d1b86eb343292898f9c02680ba58aaed143a3581

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxlsx-0.1.1-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: release.yml on alvaroroco/oxlsx

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

File details

Details for the file oxlsx-0.1.1-cp38-abi3-macosx_11_0_arm64.whl.

File metadata

  • Download URL: oxlsx-0.1.1-cp38-abi3-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 788.7 kB
  • Tags: CPython 3.8+, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for oxlsx-0.1.1-cp38-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 8f4d5c698c0f617fd425438f78cae325f193d491a4b4d41934b805cd51d628ac
MD5 94e541212aa0ac936d29a8e7efc97573
BLAKE2b-256 593309be5c057c927318fc25b967c758d8445b6ace76f6b1ec0bc8817f934308

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxlsx-0.1.1-cp38-abi3-macosx_11_0_arm64.whl:

Publisher: release.yml on alvaroroco/oxlsx

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

File details

Details for the file oxlsx-0.1.1-cp38-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for oxlsx-0.1.1-cp38-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 59726032371fecd2ce65a21fb414125f0325950e6d58d38e3832106fce5fe4a3
MD5 898fd2b4bd4346018bc07cad64d3f902
BLAKE2b-256 5afb6f31b66f4fce617dc38e2073867f07623fd9f5290772009e2e053f572810

See more details on using hashes here.

Provenance

The following attestation bundles were made for oxlsx-0.1.1-cp38-abi3-macosx_10_12_x86_64.whl:

Publisher: release.yml on alvaroroco/oxlsx

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