A drop-in openpyxl fork for safe inspection and editing of existing Excel files
Project description
paper-xlsx is an agent-first Python library for safely inspecting and editing existing Excel (.xlsx) files. It is a strict-superset hard fork of openpyxl 3.1.5 and a drop-in replacement. The distribution is renamed; the import name stays openpyxl, so existing code keeps working unchanged.
Why it exists
openpyxl is excellent at creating a workbook from scratch. The harder problem is changing a real workbook without dropping charts or leaving formulas pointed at the wrong cells. That is silent corruption: a file that opens fine and is quietly wrong, often with numbers that still look plausible.
An agent cannot eyeball the result. It needs the workbook’s structure and every edit outcome as typed, machine-readable data, and it needs the library to refuse rather than guess.
Safety contract
Preserve mode applies the shared rule: every operation either does exactly what it claims or refuses atomically. load_workbook(path, preserve=True) keeps the original package bytes as the source of truth. Every session has one of three explicit outcomes:
a correct save: your edits are spliced into the original bytes, and everything untouched survives byte-identical;
a typed refusal: an unsafe edit changes nothing on disk or in memory and the exception names the remedy;
a loud warning: a stock-mode path reports that an operation may be lossy.
manifest, model_map, and locate expose workbook structure through versioned, JSON-compatible payloads.
Quick start
from openpyxl import Workbook, load_workbook
# create a workbook with an input and a formula
wb = Workbook()
ws = wb.active
ws["A1"], ws["B1"] = "Growth rate", 0.05
ws["A2"], ws["B2"] = "Revenue", 1000
ws["B3"] = "=B2 * (1 + B1)"
wb.save("model.xlsx")
# reopen it in preserve mode: the original bytes are the source of truth
wb = load_workbook("model.xlsx", preserve=True)
wb.manifest().to_dict() # what's in the file, what survives a save
wb.model_map().to_dict() # inputs / calculations / outputs
wb.active.locate("Growth rate") # find a value cell by its label
wb.set_input("Growth rate", 0.07) # set an input; does not overwrite formulas
receipt = wb.save("model_v2.xlsx", receipt=True)
receipt.to_dict()["cells_changed"] # {'xl/worksheets/sheet1.xml': {'B1': 'changed'}}
Structural edits follow the same contract. ws.insert_rows(5) rewrites the formulas, defined names, and chart series that point into the shifted range and returns an AddressRemap. If it cannot rewrite a reference safely, it refuses and lists every reference that would have broken.
Computing values
paper-xlsx does not implement formula calculation. When LibreOffice is installed, the library delegates calculation to it as the oracle:
import openpyxl.oracle as oracle
ev = wb.evaluate(set={"Sheet!B1": 0.10}, read=["Sheet!B3"])
ev.outputs # {'Sheet!B3': 1100}
ev.certification.status # did LibreOffice reproduce the file's caches?
check = oracle.recalc("model.xlsx", output_path="model_recalculated.xlsx")
check.errors # formula-error cells found after recalculation
oracle.write_back(path) is a separate, certification-gated operation. It splices computed caches into the original package only when the existing caches provide a trustworthy baseline, unless the caller explicitly accepts an uncertified write.
Results expose versioned, JSON-compatible payload contracts with stable schema and version fields. The Preserve mode guide (doc/paper.rst) provides the complete API overview and refusal taxonomy.
Preserve mode is opt-in today: pass preserve=True per call, or set PAPER_PRESERVE_DEFAULT=1. Preserve-by-default for the public/pandas API is release-gated.
Drop-in and name map
Only the distribution and repository are renamed. The importable package stays openpyxl. This is the same distribution/import split as Pillow (pip install pillow, import PIL), and it preserves existing code, snippets, and model priors. Existing code that says import openpyxl keeps working unchanged, and every upstream feature is still available. Preserve mode and the new API are purely additive.
GitHub repository: paper-xlsx
PyPI distribution: paper-xlsx
Built wheel/sdist names: paper_xlsx-*
Python import: openpyxl
Fork sentinel: openpyxl.__paper_version__ = "0.1.2"
Upstream base: openpyxl 3.1.5 (marker tag paper-base)
Upstream releases are merged rather than rebased. The paper-base tag records the current openpyxl fork point.
Installation
paper-xlsx is published on PyPI (this source tree carries the version in openpyxl/_paper_version.py):
python -m pip install paper-xlsx
Install it in an environment that does not also contain the openpyxl distribution. Both distributions own the same openpyxl import tree, and Python package metadata cannot make one satisfy a dependency on the other. If both are present, file ownership depends on installation order; the fork raises on import when it can detect that state. Verify any installed environment with:
paper-xlsx-doctor
The source repository remains private. Users with repository access can install the current branch from Git:
pip install "paper-xlsx @ git+https://github.com/The-LLM-Data-Company/paper-xlsx.git@main"
Verification
python -c "import openpyxl; print(openpyxl.__paper_version__)"
Expected output:
0.1.2
How it’s tested
Upstream openpyxl’s test suite runs on every change to check compatibility with existing behavior.
A frozen, hash-pinned fixture corpus under tests/paper records exact provenance. Its current files come from openpyxl, LibreOffice, or documented package surgery; the still-missing real-Excel and Google Sheets fixture buckets are listed in the corpus README.
The contract harness saves and reopens before asserting, enforces exact changed-part budgets, exercises refusal atomicity, and runs a headless LibreOffice load smoke.
License
MIT, inherited from openpyxl. Original work © the openpyxl authors; fork additions © Paper Instruments, Inc. The fork preserves the upstream license and attribution. See LICENCE.rst.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
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 paper_xlsx-0.1.2.tar.gz.
File metadata
- Download URL: paper_xlsx-0.1.2.tar.gz
- Upload date:
- Size: 378.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7fde577c3d856f424cce11aade1643efe114d402cd157aa0954edfebc30a22ef
|
|
| MD5 |
afdf9534b36c9b0df91aac29f224ced4
|
|
| BLAKE2b-256 |
cf38775af98d6dcef888b07fab39f5b23767085051aa7e62bff35c44a194301d
|
Provenance
The following attestation bundles were made for paper_xlsx-0.1.2.tar.gz:
Publisher:
release.yaml on The-LLM-Data-Company/paper-xlsx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
paper_xlsx-0.1.2.tar.gz -
Subject digest:
7fde577c3d856f424cce11aade1643efe114d402cd157aa0954edfebc30a22ef - Sigstore transparency entry: 2156979561
- Sigstore integration time:
-
Permalink:
The-LLM-Data-Company/paper-xlsx@1dc04c44e96860b137f8d1c99f0c1cd4fe6f2ad0 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/The-LLM-Data-Company
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@1dc04c44e96860b137f8d1c99f0c1cd4fe6f2ad0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file paper_xlsx-0.1.2-py3-none-any.whl.
File metadata
- Download URL: paper_xlsx-0.1.2-py3-none-any.whl
- Upload date:
- Size: 459.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
895a27fd95f9f859b68d9ea93d155f982a977ff1a0f013de937bbe13ab181d11
|
|
| MD5 |
cfbc643b76ebea9114c6cbf25a410240
|
|
| BLAKE2b-256 |
1f5453beb7c9c4192cc03f7f32af0ddefb6c981988d5f1066410c3724f9067df
|
Provenance
The following attestation bundles were made for paper_xlsx-0.1.2-py3-none-any.whl:
Publisher:
release.yaml on The-LLM-Data-Company/paper-xlsx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
paper_xlsx-0.1.2-py3-none-any.whl -
Subject digest:
895a27fd95f9f859b68d9ea93d155f982a977ff1a0f013de937bbe13ab181d11 - Sigstore transparency entry: 2156979628
- Sigstore integration time:
-
Permalink:
The-LLM-Data-Company/paper-xlsx@1dc04c44e96860b137f8d1c99f0c1cd4fe6f2ad0 -
Branch / Tag:
refs/tags/v0.1.2 - Owner: https://github.com/The-LLM-Data-Company
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@1dc04c44e96860b137f8d1c99f0c1cd4fe6f2ad0 -
Trigger Event:
push
-
Statement type: