paper-docx
paper-docx is an agent-first Python library for safely inspecting, editing,
reviewing, and composing existing Microsoft Word (.docx) documents. It is a
strict-superset hard fork of
python-docx and a drop-in
replacement. The distribution is renamed; the import name stays docx, so
existing code keeps working unchanged.
import docx # the import name is unchanged
doc = docx.Document("contract.docx")
Why it exists
python-docx is excellent at creating documents. Its lossless package layer,
disciplined XML mapping, and years of absorbed edge cases are why this fork
builds on it.
The harder problem is changing a contract or other real-world document without losing formatting, revisions, fields, or content outside the body. Hand-edited XML can produce silent corruption: a file that opens fine and is quietly wrong. An agent cannot eyeball the result, so it needs the document's structure and every edit outcome as typed, machine-readable data. It also needs the library to refuse rather than guess.
Safety contract
Every added operation either does exactly what it claims or refuses atomically.
Mutating operations validate fully before they change anything. If an operation
cannot proceed safely, it raises a typed PaperRefusal and leaves the document
byte-for-byte unchanged in memory and on disk. Callers can catch PaperRefusal
separately from programmer errors, which remain plain ValueError or
TypeError. Comparison and rewrite paths preserve meaningful whitespace,
including trailing spaces inside runs.
A short example
Create a native Word redline from two document versions:
import tempfile, docx
from docx.package import compare
tmp = tempfile.mkdtemp()
a = docx.Document()
a.add_paragraph("Payment is due within thirty calendar days of the invoice date.")
a.save(f"{tmp}/v1.docx")
b = docx.Document()
b.add_paragraph("Payment is due within thirty business days of the invoice date.")
b.save(f"{tmp}/v2.docx")
result = compare(f"{tmp}/v1.docx", f"{tmp}/v2.docx", author="Reviewer")
[(r.revision_type, r.text) for r in result.document.revisions]
# [('deletion', 'calendar'), ('insertion', 'business')]
result.document.revisions.accept_all()
result.document.paragraphs[0].text
# 'Payment is due within thirty business days of the invoice date.'
compare emits markup Word renders as tracked changes. Before returning, it
accepts and rejects private copies and verifies both outcomes. If a difference
cannot be represented safely as a redline, such as a style or package-part
change, it raises a typed refusal instead of returning an incomplete result.
What it adds
Reading and editing one document
docx.storytraverses the body, headers, footers, footnotes, endnotes, comments, tracked insertions, content controls, and text boxes. Callers can view the document as it stands, before pending revisions, or all at once.docx.searchfinds normalized text across Word's run fragmentation. A returnedSpancan replace the matched text while preserving unaffected runs, emit the replacement as a tracked change, or anchor a comment.docx.blocksinserts, deletes, or replaces whole paragraphs relative to a text anchor, plainly or as a tracked change.docx.tableops/docx.numberingprovide cell, row, and list edits that refuse on unsafe structures such as merged cells, nested tables, or undefined numbering.docx.controlsfills content controls with the correct value type and clears placeholder state so Word treats them as filled.docx.bookmarks/docx.fieldscreate bookmarks over a span and author page numbers, dates, cross-references, and tables of contents as fields with placeholder results.docx.formattingresolves effective formatting through document defaults, styles, and direct formatting, with provenance for each value.
Reviewing and finalizing
doc.revisionsenumerates and resolves tracked changes across every part: insertions, deletions, run and paragraph format changes, table-row revisions, and moves. Unresolvable markup is listed by name.doc.finalize()/doc.scrub()accepts or rejects all revisions, then removes reviewing residue and reports exactly what was removed.docx.protectionrespects Restrict-Editing. Mutating operations refuse on a protected document unless the caller explicitly overrides; the setting is preserved in the document.
Working across documents
docx.package.comparegenerates a native tracked-change redline from two documents, with the accept/reject round-trip shown above.docx.package.patch_save/diff_package/text_diffkeeps unchanged parts byte-identical and reports changed parts and text.diagnoseexplains why an unreadable file cannot be opened.docx.compositioncopies formatted content between documents, reconciles styles, numbering, media, hyperlinks, and bookmarks, and reports every part touched.docx.errorsexposes typed refusals, distinct from programmer errors.
Drop-in and name map
Only the distribution and repository are renamed. The importable package stays
docx. This is the same distribution/import split as Pillow
(pip install pillow, import PIL), and it preserves existing code, snippets,
and model priors.
- GitHub repository / PyPI distribution:
paper-docx - Python import:
docx - Fork sentinel:
docx.__paper_version__ = "0.1.2"
Installation
Install from PyPI:
python -m pip uninstall -y python-docx paper-docx
python -m pip install paper-docx
The clean uninstall is required when migrating from python-docx. Both
distributions use the frozen docx import package, and pip cannot safely
overlay or uninstall two distributions that own the same files.
Confirm the install:
paper-docx-doctor
Pip does not treat paper-docx as satisfying another package's declared
dependency on python-docx. That dependency will reinstall upstream and
overwrite shared docx files. Replace or remove the dependency, or run that
package in a separate environment.
In a controlled deployment, a constraint containing python-docx<0 makes pip
reject direct or transitive attempts to install upstream. The constraint must
be applied to every install in that environment.
Documentation
The Sphinx docs extend the upstream python-docx documentation to cover the
fork's additions: start with docs/user/paper-additions.rst and the
docs/api/paper-*.rst reference pages. Everything inherited from python-docx
works as documented at the
python-docx documentation.
How it's tested
- Upstream's pytest and behave suites run on every commit to check compatibility with existing behavior.
- A frozen, hash-pinned fixture corpus spans generated and LibreOffice-authored documents.
- The contract harness checks refusal atomicity and validates the fixture corpus with a headless LibreOffice load smoke.
License
MIT, inherited from python-docx. Original work © Steve Canny and the python-docx
contributors; fork additions © Paper Instruments, Inc. This fork preserves the
upstream license and attribution. See LICENSE.
Release files for paper-docx 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| paper_docx-0.1.2.tar.gz | 7.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| paper_docx-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 7.6 MB
Release files / paper_docx-0.1.2.tar.gz
| Download URL | paper_docx-0.1.2.tar.gz |
|---|---|
| Size | 7.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8791d7e22e2ee847ad20638c85b544ce045e35ad98759f5f8ef3559604eb8d00
|
|
BLAKE2b-256 checksum How to use checksums |
a2cf81647f53dd68d80c0bbf455f163eb29853ab2041de1106bcc57aa8b46088
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 Jul 12, 2026.
Transparency logRelease files / paper_docx-0.1.2-py3-none-any.whl
| Download URL | paper_docx-0.1.2-py3-none-any.whl |
|---|---|
| Size | 416.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d5805e491365b1494bc6434a2b636011334f811f034caea99d9ebb2cd479b9a6
|
|
BLAKE2b-256 checksum How to use checksums |
71340a6b039b5b9df2a0718a2122ed02f8d656b7e3b4466724fb4f7a08291376
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.13
|
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 Jul 12, 2026.
Transparency log