release: 2.1.0 - #140
Merged
Merged
release: 2.1.0#140
Conversation
added 3 commits
August 5, 2026 16:24
Six findings from a re-review of the merged feature branch, each with a regression test. PathLike acceptance. `add_picture()`, `add_float_picture()`, `add_image_watermark()`, `add_alt_chunk()`, `add_embedded_object()` and `Image.from_file()` all reached `open()` through a helper testing `isinstance(x, str)` and fell through to the stream branch, so a `pathlib.Path` failed with `AttributeError: 'PosixPath' object has no attribute 'seek'` — a message that says nothing about what was wrong. The README claimed this worked without qualification; now it does. Annotations widened along the whole chain down to `StoryPart._image_rIds`. CLI error reporting. `PackageNotFoundError` is what a missing file or a non-package raises, and it is neither a `FileNotFoundError` nor a `BadZipFile`, so every command that opens a document ended in a traceback rather than the message the module docstring promises. A password-protected file now says so specifically, and a `KeyError` from `--names` or `--keep` naming a style the document does not define is reported rather than raised. `styles list --used` and `--unused` are now mutually exclusive. Passing both matched nothing and printed an empty list, which reads as an answer rather than as the contradiction it is. `copy_to()` keeps a copied drawing's `wp:docPr/@name`. Only `@id` has to be unique document-wide; the name is the shape's own — the source file name for a picture, "Chart 1" for a chart — so overwriting it discarded information and labelled a copied chart "Picture 3". A drawing with no name still gets one. `add_custom_xml_part()` accepts an `item_id`. The GUID it otherwise generates was the one thing in this library's output that was not a function of its input, which quietly cost byte-reproducibility for any document carrying a data store. Also removes two artefacts committed by accident in d3ad2cd — a Playwright page snapshot and a screenshot of the docs navbar — and gitignores the directory the first came from. Verified: 2890 unit tests and 677 behave scenarios on 3.9 and 3.12, and a document exercising every 2.1 feature validates against the ISO 29500 wml schema (document.xml, styles.xml, numbering.xml and endnotes.xml).
The feature work landed without user-guide coverage: of the twenty-six issues in it, one had a mention in the guide. Everything is documented now, and every code sample on this branch was executed before being written down. Three new pages: - **Command line** — `python -m docx`, its four subcommands, and the exit codes, since those end up in scripts and are part of the contract. - **Style usage and cleanup** — why "used" is a reachability closure rather than a scan of the body, what each of the three cleanup passes removes, and why latent styles are a separate flag. - **Copying content** — the five things a hand-written `deepcopy` breaks, and why a copied bookmark is dropped rather than renamed. Extended: tables (width and `Pct`, indent, cell margins, `tblLook`, row properties, cell text direction, alt text at construction), text (character and line units, paragraph borders, the paragraph mark, theme fonts, text direction, equations), sections (page borders), styles (docDefaults, usage, transfer), images (EXIF orientation and image extraction), numbering (writable definitions), footnotes (endnotes), fields (`add_caption()`), templates (style import and extract, OLE objects, macros), document properties (the custom XML data store) and documents (reproducible output, size, the parts index). Corrections along the way: - The README listed endnotes, equations and embedded OLE objects under "deliberately not here yet". All three shipped. - `documents.md` said embedded OLE objects had no API. - Reproducible output was claimed in the README and documented nowhere; it now has a section, including the one exception. - CONTRIBUTING.md pointed at the 2.0.0 milestone, which is closed.
Twenty-six issues from the 2.0.0 milestone, plus the fixes from re-reviewing them and the documentation for the lot. No API was removed, so this is a minor release. The two behaviour changes are in the bundled template and are visible only in newly generated documents: it is 447 KB smaller, and a hyperlink now takes the theme-linked `Hyperlink` style the template defines rather than a hardcoded-blue one synthesised on first use.
toxicphreAK
force-pushed
the
release/2.1.0
branch
from
August 5, 2026 14:42
2aa2047 to
7452660
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Prepares 2.1.0: a re-review of the merged feature work, the documentation it shipped without, and the version bump.
Review findings
Six, each with a regression test.
PathLike acceptance.
add_picture(),add_float_picture(),add_image_watermark(),add_alt_chunk(),add_embedded_object()andImage.from_file()all reachedopen()through a helper testingisinstance(x, str)and fell through to the stream branch, so apathlib.Pathfailed withAttributeError: 'PosixPath' object has no attribute 'seek'. The README claimed this worked without qualification.CLI error reporting.
PackageNotFoundError— what a missing file or a non-package raises, and the most common failure the CLI has — is neither aFileNotFoundErrornor aBadZipFile, so every command that opens a document ended in a traceback instead of the message the module docstring promises. A password-protected file now says so, and a--namesor--keepnaming an unknown style is reported rather than raised.styles list --used/--unusedare now mutually exclusive; both at once matched nothing and printed an empty list.copy_to()keepswp:docPr/@name. Only@idhas to be unique; the name is the shape's own, so overwriting it discarded the source file name and labelled a copied chart "Picture 3".add_custom_xml_part(item_id=...). The generated GUID was the one thing in this library's output that was not a function of its input, which quietly cost byte-reproducibility for any document carrying a data store.Also removes a Playwright page snapshot and a navbar screenshot committed by accident in d3ad2cd.
Documentation
Of the twenty-six issues in the feature work, one had a mention in the user guide. All of it is documented now, and every code sample was executed before being written down.
New pages: Command line, Style usage and cleanup, Copying content.
Extended: tables, text, sections, styles, images, numbering, footnotes, fields, templates, document properties, documents.
Four stale claims corrected — the README listed endnotes, equations and OLE objects as not yet implemented;
documents.mdsaid OLE objects had no API; reproducible output was claimed in the README and documented nowhere; CONTRIBUTING.md pointed at a closed milestone.Verification
ruff check .cleanmkdocs build --strictcleanwml.xsd—document.xml,styles.xml,numbering.xmlandendnotes.xmlall valid, which is the check that catches the child-order errors Word rejects a document forpython -m docxworks from itThe version is bumped but no tag is pushed — pushing
v2.1.0triggers the PyPI publish workflow, so that is left as a deliberate step.