Create, read, and update Microsoft Word .docx files.
Project description
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. Accepting the redline
reproduces the revised document; rejecting it reproduces the original. That
round-trip guarantee holds across every part of the file.
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.0"
Installation
Install from PyPI:
pip install paper-docx
Confirm the install:
python -c "import docx; print(docx.__paper_version__)"
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.
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_docx-0.1.0.tar.gz.
File metadata
- Download URL: paper_docx-0.1.0.tar.gz
- Upload date:
- Size: 7.1 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3c6690073aa3f5697aa9ec8a4cb5270b8b0cf7fe2d95b52514fa08258d0b7004
|
|
| MD5 |
50c17a8bf724522a234e940aa019f69c
|
|
| BLAKE2b-256 |
2f244b7131d6aa441d4a20bfb1325f42e89d24278274255ea4b5c055c2836a2b
|
File details
Details for the file paper_docx-0.1.0-py3-none-any.whl.
File metadata
- Download URL: paper_docx-0.1.0-py3-none-any.whl
- Upload date:
- Size: 360.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f16707d2cd4bbca829c51a29fb0c6ebacd301393e188b99c49092c09612be844
|
|
| MD5 |
4e68faef88912203ba35e77b37fd008d
|
|
| BLAKE2b-256 |
22dbda529b6f323495f15c225c851c0d5ba6f1fa92c379759b4956956b331e4f
|