Skip to main content

odsslicer

CI PyPI

Python reader and writer for .ods files (OpenDocument Spreadsheet — LibreOffice / OpenOffice Calc), with a numpy-inspired indexing API:

from odsslicer import ODSReader

table = ODSReader("workbook.ods")
sheet = table.sheet("Sheet1")

sheet["A1"].value          # typed: str / float / bool / date / datetime / time / timedelta / None
sheet["A1:B3"].to_numpy()  # any block as a numpy array
sheet[:, 0]                 # entire column A

sheet["C1"].formula = "SUM(A1:A10)"
sheet["C1"].style.bold = True
table.save("out.ods")                     # add recalculate=True to have LibreOffice compute formulas/pivots

odsslicer works directly on the ODF XML (via BeautifulSoup/lxml), so it preserves what other tools tend to drop — cell formats (currency, percentage, date, time), formulas, merged and repeated cells, styles, comments — and writes them back faithfully. It has no calculation engine of its own: like ODF itself, it describes what to compute — and can hand the file to a local LibreOffice (save(..., recalculate=True)) to compute formulas and pivot tables for you.

Full documentation with an example for every feature: DOCS.md.

Installation

pip install odsslicer

Requires Python ≥ 3.10. Dependencies (beautifulsoup4, lxml, numpy) are installed automatically. For local development:

git clone https://github.com/antnardo/odsslicer.git
cd odsslicer
pip install -e ".[test]"

What it does

Each line links to the detailed section (with examples) in DOCS.md.

Area Feature Example
Reading numpy-style indexing — addresses, slices, out-of-range returns empty cells sheet["A1"], sheet[0, 0], sheet["A1:B3"], sheet[:, 0]
Typed cells — value, displayed text, format, address cell.value, cell.text, cell.format
Arrays — to_list(), to_numpy(), numpy-like shapes sheet["A1:B3"].to_numpy()
Writing Values of every ODF type, then save() sheet["A1"].value = 42.5
Ranges at once — broadcast or element-wise sheet["A1:C1"].value = [1, 2, 3]
Auto-unrolling of repeated/merged cells on first write transparent
Auto-growth when writing past the sheet's extent sheet["Z100"].value = 1
Displayed text inferred from the document's own formats "50,00 %", "05/01/30"
Files & sheets New file from scratch ODSReader.new()
Add / rename / reorder / delete sheets — renaming fixes cross-sheet formulas table.rename_sheet("Sheet1", "Q4")
Structure Insert rows/columns — formula references and merges follow sheet.insert_rows(2, 5)
Delete rows/columns — formula references follow, batchable sheet.delete_rows([3, 7, 20])
Copy cells/ranges — value + formula + style, overlap-safe sheet.copy("A1:B2", "D5")
Sort a range — stable, None last, formulas follow their row sheet.sort("A2:C10", by=1)
Merge / unmerge + read merge state sheet.merge("A1:C2"), cell.merge_range
Formulas Write in ordinary syntax, translated to ODF's cell.formula = "IF(A1>0,1,-1)"
Read back in ordinary syntax cell.formula_friendly
Fill across a range like a fill handle cell.fill_formula("B3:B10")
{r}/{c} templates for per-cell patterns sheet["A2:A10"].formula = "$A{r-1}+1"
Pivot tables — definition written, computed by the spreadsheet sheet.create_pivot_table(...)
Recalculate with LibreOffice — formulas + pivot refresh, headless, no UNO needed; from the shell with soffice --convert-to table.save("out.ods", recalculate=True)
Styles Read and write cell styles — font, colors, alignment, borders, rotation, wrap… cell.style.bold = True
Copy a style in one shot b.style = a.style
Number formats — read, assign, or create from scratch NumberFormat.create(table, "currency", ...)
Conditional formats (e.g. negatives in red) fmt.add_condition("value()<0", red)
Row / column / sheet styles — height, width, visibility, tab color sheet.column_style(0).width = "5cm"
Annotations Comments — text, author, date, visibility cell.comment = "Check this"
Hyperlinks cell.hyperlink = "https://…"
Document Properties — title, author, keywords, typed custom properties table.properties.title = "Q4"

See Known limitations for what's deliberately out of scope (no calculation engine, no charts/images, no partial rich text…).

Are there already equivalent PyPI modules?

Package Read/Write Latest release Notes
odfpy Low-level R/W 1.4.1 — Jan 2020 Dormant, but still the brick pandas uses internally
odfdo Full R/W 3.24 — Aug 2026 Actively maintained modern fork of odfpy; generic DOM-like API for all ODF document types
pyexcel-ods3 R/W 0.6.1 — Jan 2022 Inactive; values only, no styles/formulas
ezodf R/W 0.3.2 — Dec 2015 Abandoned
pandas (engine="odf") Read (via odfpy) follows pandas Convenient for data frames; loses formulas and fine-grained formats
python-calamine Read-only (Rust), fast 0.8 — Jul 2026 The best option for fast pure reading
pandas-ods-reader Read-only → DataFrame 1.0.2 — May 2025 Maintained, limited scope

(Versions and dates as of August 2026.)

Where odsslicer sits: a spreadsheet-shaped API (sheet["A1:B3"], numpy arrays, fill handles, copy/sort/merge) rather than a generic ODF DOM, with read and write of the things data-oriented tools usually lose — formats, formulas (in ordinary syntax), styles, merged cells, comments, pivot definitions — and every write verified against a real LibreOffice. If you only need to read values fast, use python-calamine; if you need to manipulate arbitrary ODF documents (text, presentations) at the XML level, use odfdo.

Tests

pip install -e ".[test]"
pytest

The main suite (tests/test_odsslicer.py) covers every feature above plus regression tests for the bugs fixed along the way; it runs on every push/PR via GitHub Actions across Python 3.10 to 3.13.

tests/test_wild_files.py confronts the API with real-world files written by other generators — Excel 16, a 2012-era LibreOffice 3.5, recent LibreOffice on Linux and Windows, a Google Sheets export (see tests/wild/README.md for provenance) — reading, writing and round-tripping each one.

tests/test_libreoffice_consistency.py is an opt-in suite that hands files odsslicer wrote to a real, local LibreOffice (soffice --headless --convert-to fods) and inspects what LibreOffice itself made of them — the strongest available signal that a write is genuinely valid ODF, not just something our own reader happens to parse back. It skips automatically if no soffice/libreoffice binary is on PATH.

Versions

Version numbers are derived automatically from git tags (via setuptools-scm) and follow Semantic Versioning — while the major version stays 0, the API can still change between minor versions. See CHANGELOG.md (and the Releases page) for what changed in each version.

License

MIT — reuse with essentially no restriction, just keep the copyright notice.

Project name

odsslicer, to reflect the module's real differentiator — numpy-style indexing/slicing by cell address — rather than a generic "ods reader".

Release files for odsslicer 0.13.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for odsslicer 0.13.1
File Size Uploaded
odsslicer-0.13.1.tar.gz 341.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for odsslicer 0.13.1
File Interpreter ABI Platform
odsslicer-0.13.1-py3-none-any.whl Python 3 none any Details

Total release size: 429.0 kB

Release files / odsslicer-0.13.1.tar.gz

Download URL odsslicer-0.13.1.tar.gz
Size 341.2 kB
Tags Source
SHA-256 checksum
How to use checksums
3ecf89799d61fc070d8515dc1d21dfbebc61e5dbc010a9269f618ee8e148082c
BLAKE2b-256 checksum
How to use checksums
c83a05449904c3cf4be00d5922435c8d622a2146572377ecabc66e3857409228
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release files / odsslicer-0.13.1-py3-none-any.whl

Download URL odsslicer-0.13.1-py3-none-any.whl
Size 87.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
447cb2d955d28d4ac09e7ccfb6992e75ca631a352cd069989db2496cc9d7d734
BLAKE2b-256 checksum
How to use checksums
3dec2097ee2679b5c15711f0484beec338bf2863d42a5e3a8ef145b7de954496
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.

Transparency log

Release history Release notifications | RSS feed

0.13.2

2 release files

This release

0.13.1 This release

2 release files

0.13.0

2 release files

0.12.3

2 release files

0.12.2

2 release files

0.12.1

2 release files

0.12.0

2 release files

0.11.1

2 release files

0.11.0

2 release files

0.10.0

2 release files

0.9.1

2 release files

0.9.0

1 release file

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page