revisions
revisions
¶
The tracked-changes API — reading, accepting and rejecting revisions.
Any document that has been through review carries revision markup, and before this
existed the library's handling of it was silently wrong rather than loudly broken:
inserted text was dropped, deleted text was dropped, and Paragraph.text returned
something that matched neither the original nor the final version of the document.
The text model is now defined:
- Paragraph.text is the document as it now reads — every revision accepted.
Insertions are included; deletions are not. This is what almost every caller wants and
what makes
.textconsistent with what a reader sees with markup hidden. - Paragraph.original_text is the document as it read before the revisions. Deletions are included; insertions are not.
Accepting a revision makes the first reading permanent; rejecting it makes the second.
In scope: w:ins, w:del, w:moveFrom and w:moveTo, whether they wrap content,
mark a paragraph mark as inserted or deleted (which is how a paragraph split or merge is
tracked), or mark a table row; and the *Change elements recording a formatting change.
Not in scope: w:numberingChange, and the cell-level merge revisions
(w:cellMerge). Both are rare and neither has a well-defined accept that this library
could perform without guessing.
Revision
¶
Revision(
element: CT_TrackChange, parent: ProvidesStoryPart
)
Bases: StoryChild
One tracked change — an insertion, a deletion, a move or a formatting change.
Not constructed directly; reached through Document.revisions or Paragraph.revisions.
Source code in src/docx/revisions.py
author
property
¶
The name of whoever made this change.
The empty string when the document does not say, which is the case for a
w:tblGridChange and for a document stripped of personal information.
date
property
¶
When the change was made, None when the document does not say.
None too for an unparseable timestamp, which an anonymised document has.
id
property
¶
The w:id of this revision, None when it carries none.
Unique among the revisions of a document when present.
is_paragraph_mark
property
¶
True when this revision is of a paragraph mark rather than of content.
An inserted paragraph mark is a paragraph split; a deleted one is a merge with the paragraph that follows. Accepting or rejecting one therefore joins or splits paragraphs rather than adding or removing text.
is_row
property
¶
True when this revision marks a whole table row as inserted or deleted.
text
property
¶
The text this revision covers, the empty string when it covers none.
Empty for a paragraph-mark revision, a row revision and a formatting change, none of which cover text of their own.
accept
¶
Keep this change, and remove the record of it.
An insertion's content stays and stops being marked as new; a deletion's content goes; a formatting change's record goes, leaving the current formatting in place. Accepting a deleted paragraph mark merges the paragraph with the one after it, which is what the deletion recorded.
Source code in src/docx/revisions.py
reject
¶
Undo this change, and remove the record of it.
An insertion's content goes; a deletion's content comes back, its w:delText
turned back into w:t; a formatting change puts the recorded previous
properties back. Rejecting an inserted paragraph mark merges the paragraph with
the one after it, undoing the split.
Source code in src/docx/revisions.py
iter_revisions
¶
iter_revisions(
element: BaseOxmlElement, parent: ProvidesStoryPart
) -> Iterator[Revision]
Generate a Revision for each tracked change in the subtree of element.
Source code in src/docx/revisions.py
apply_all
¶
apply_all(
element: BaseOxmlElement,
parent: ProvidesStoryPart,
accept: bool,
) -> int
Accept or reject every revision in element, returning how many were applied.
Applied innermost-last and in reverse document order, so that unwrapping or removing one revision cannot invalidate another that has not been reached yet — a nested revision is dealt with before the one containing it.