Skip to content

Paragraph.text setter silently detaches comments (and footnote references) added with 1.2 comment support #1604

Description

@Dmitry-Kov

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:

  1. 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.
  2. 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.
  3. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions