Since 1.2 added Document.add_comment(), assigning paragraph.text removes the comment's anchor without any indication. Paragraph.clear() drops every child except w:pPr, which includes w:commentRangeStart / w:commentRangeEnd (these are paragraph children, not runs) and the run holding w:commentReference. The <w:comment> stays in comments.xml, anchored to nothing. Word opens the file with no warning and just doesn't show the comment.
Self-contained repro, python-docx 1.2.0:
from docx import Document
import zipfile, re
d = Document()
p = d.add_paragraph("The fee is EUR 12,000 per milestone.")
d.add_comment(runs=p.runs[0], text="Confirm this figure before circulation", author="Reviewer")
d.save("before.docx")
d = Document("before.docx")
d.paragraphs[0].text = "The fee is EUR 14,000 per milestone."
d.save("after.docx")
for f in ("before.docx", "after.docx"):
z = zipfile.ZipFile(f)
doc = z.read("word/document.xml").decode()
cm = z.read("word/comments.xml").decode()
print(f, len(re.findall(r"commentRange(Start|End)|commentReference", doc)),
"markers,", len(re.findall(r"<w:comment ", cm)), "comments")
# before.docx 3 markers, 1 comments
# after.docx 0 markers, 1 comments
The same happens to w:footnoteReference (the footnote body survives in footnotes.xml, the reference in the text is gone) and to any w:ins/w:del wrapping the runs. Probably related to #1519, which reports comments disappearing on save without a repro — I suspect this is the mechanism.
I understand .text = ... is documented as replacing the paragraph's content, and that preserving markers across an arbitrary text replacement is not well-defined. But this is the first thing every code sample and every LLM agent reaches for, and the failure is invisible. Options, from small to large:
- Keep
w:commentRangeStart/w:commentRangeEnd in clear() (they are not content), and re-emit w:commentReference for each surviving range at the end of the new run. The comment then anchors to the whole new text, which is what Word does when you retype a commented passage.
- At minimum, a warning in the
text setter docstring and in the comments section of the docs that setting text removes comment anchors, footnote references and tracked-change markup in that paragraph.
- Emit a
warnings.warn when clear() is about to drop a comment range or footnote reference.
Happy to open a PR for (1) or (2) if either is acceptable. Found while measuring what verification steps catch on agent-edited documents (github.com/Dmitry-Kov/ooxml-integrity).
python-docx 1.2.0, Python 3.9.6, macOS
Since 1.2 added
Document.add_comment(), assigningparagraph.textremoves the comment's anchor without any indication.Paragraph.clear()drops every child exceptw:pPr, which includesw:commentRangeStart/w:commentRangeEnd(these are paragraph children, not runs) and the run holdingw:commentReference. The<w:comment>stays incomments.xml, anchored to nothing. Word opens the file with no warning and just doesn't show the comment.Self-contained repro, python-docx 1.2.0:
The same happens to
w:footnoteReference(the footnote body survives infootnotes.xml, the reference in the text is gone) and to anyw:ins/w:delwrapping the runs. Probably related to #1519, which reports comments disappearing on save without a repro — I suspect this is the mechanism.I understand
.text = ...is documented as replacing the paragraph's content, and that preserving markers across an arbitrary text replacement is not well-defined. But this is the first thing every code sample and every LLM agent reaches for, and the failure is invisible. Options, from small to large:w:commentRangeStart/w:commentRangeEndinclear()(they are not content), and re-emitw:commentReferencefor each surviving range at the end of the new run. The comment then anchors to the whole new text, which is what Word does when you retype a commented passage.textsetter docstring and in the comments section of the docs that setting text removes comment anchors, footnote references and tracked-change markup in that paragraph.warnings.warnwhenclear()is about to drop a comment range or footnote reference.Happy to open a PR for (1) or (2) if either is acceptable. Found while measuring what verification steps catch on agent-edited documents (github.com/Dmitry-Kov/ooxml-integrity).
python-docx 1.2.0, Python 3.9.6, macOS