docx_plus.comments.anchor¶
Anchor a comment to a run, paragraph, or run range — and undo the same.
Writes the three body-side OOXML elements python-docx skips
(w:commentRangeStart, w:commentRangeEnd, the CommentReference
marker run) plus the comment body in comments.xml (created on first
use). delete_comment is the inverse and idempotent.
Architecture walkthrough: Anchored comments.
docx_plus.comments.anchor ¶
Anchor and remove comments — the body-side OOXML python-docx skips.
python-docx 1.x writes <w:comment> into comments.xml but
omits the three body-side elements that anchor a comment to a text
range — w:commentRangeStart, w:commentRangeEnd, and the
CommentReference marker run. As a result, comments added via
python-docx show up in the review pane but have nothing in the
document text to point at. This module fills that gap.
:func:add_comment wraps a run, paragraph, or run-range with the three
body markers and appends a matching w:comment body to
comments.xml (the comments part is created on first use).
:func:delete_comment removes everything :func:add_comment wrote.
Every comment written here is also registered as an unresolved thread
root in commentsExtended.xml — see
:mod:docx_plus.comments._extended for why that part exists and
:mod:docx_plus.comments.threads for the reply / resolve surface built
on top of it — and given a w16cid:durableId in commentsIds.xml,
its only identifier stable across edits
(:mod:docx_plus.comments._ids).
Author presence (people.xml) is not written here; it is cosmetic
and needs an identity the library cannot invent. See
:mod:docx_plus.comments.people.
This module imports only from docx_plus.core and the siblings
docx_plus.comments.registry / docx_plus.comments._extended /
docx_plus.comments._ids (SPEC §9.1).
CommentRef
dataclass
¶
Handle for an inserted comment.
Attributes:
| Name | Type | Description |
|---|---|---|
comment_id |
int
|
The |
body_element |
_Element
|
The |
CommentNotFoundError ¶
Bases: DocxPlusError, KeyError
Raised when no <w:comment> with the requested id exists.
Subclasses :class:KeyError so existing except KeyError: clauses
still catch it; also :class:DocxPlusError per SPEC §9.7.
add_comment ¶
add_comment(
target: CommentTarget,
text: str,
*,
author: str = "",
initials: str | None = None,
id_registry: CommentIdRegistry | None = None,
para_id_registry: ParaIdRegistry | None = None,
durable_id_registry: DurableIdRegistry | None = None,
) -> CommentRef
Anchor a comment to a run, paragraph, or run range.
Writes the three body-side OOXML elements python-docx skips
(w:commentRangeStart, w:commentRangeEnd, the
CommentReference marker run) plus the comment body entry in
comments.xml. The comments part is created on first use.
The comment is also registered as an unresolved thread root: its body
paragraphs are stamped with w14:paraId and a matching
<w15:commentEx> entry is written to commentsExtended.xml
(created on first use). This is what Word writes for every comment
it authors, and it is what makes the comment immediately usable with
:func:~docx_plus.comments.reply_to_comment and
:func:~docx_plus.comments.resolve_comment.
It is also given a w16cid:durableId in commentsIds.xml — the
comment's only identifier stable across edits, since w:id is a
position-dependent index Word renumbers and w14:paraId changes
whenever the body is rewritten. Read it back through
:attr:~docx_plus.comments.AnchoredComment.durable_id.
Author presence (people.xml) is deliberately not written —
see :func:~docx_plus.comments.set_author_presence.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
target
|
CommentTarget
|
Where the comment anchors.
|
required |
text
|
str
|
Comment body text. Whitespace is preserved
( |
required |
author
|
str
|
Author shown in the review pane. The empty string is
legal and is what python-docx's own |
''
|
initials
|
str | None
|
Author initials shown alongside the author. |
None
|
id_registry
|
CommentIdRegistry | None
|
Pre-existing registry to share across an editing
session (useful when inserting many comments). A fresh
:class: |
None
|
para_id_registry
|
ParaIdRegistry | None
|
Pre-existing |
None
|
durable_id_registry
|
DurableIdRegistry | None
|
Pre-existing |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
A |
CommentRef
|
class: |
CommentRef
|
to the comment body element in |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
TypeError
|
If |
Example
from docx import Document from docx_plus.comments import add_comment doc = Document() p = doc.add_paragraph("Hello world") ref = add_comment(p, "Greeting", author="Reviewer")
Source code in docx_plus/comments/anchor.py
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | |
edit_comment ¶
Replace the body text of an existing comment in place.
Removes all child block-level content of the matching <w:comment>
element and appends a fresh paragraph with text as its run. The
<w:comment> element's attributes (w:author, w:date,
w:initials) are preserved — only the body content changes. The
body-side range markers and reference run are also untouched, so the
comment stays anchored to the same text range, and the comment's
w14:paraId is carried onto the rebuilt paragraph so its position
in the thread and its resolved state survive the edit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc
|
Document
|
The python-docx :class: |
required |
comment_id
|
int
|
The |
required |
text
|
str
|
New comment body text. Whitespace is preserved
( |
required |
Raises:
| Type | Description |
|---|---|
CommentNotFoundError
|
If no comment with |
Source code in docx_plus/comments/anchor.py
delete_comment ¶
Remove all traces of a comment from the document.
Removes, for the comment and (by default) every reply beneath it:
- The
<w:comment>body incomments.xml - Every
<w:commentRangeStart>and<w:commentRangeEnd>marker in the document body - The reference run hosting
<w:commentReference> - The
<w15:commentEx>thread entry incommentsExtended.xml
Idempotent: deleting a comment id that doesn't exist is a no-op.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc
|
Document
|
The python-docx :class: |
required |
comment_id
|
int
|
The |
required |
include_replies
|
bool
|
When |
True
|
Source code in docx_plus/comments/anchor.py
clear_all_comments ¶
Remove every comment in the document.
Single-pass: walks the document body once removing every
<w:commentRangeStart>, <w:commentRangeEnd>, and
<w:commentReference> marker regardless of id, then walks
comments.xml once removing every <w:comment> entry, then
commentsExtended.xml and commentsIds.xml once each removing
every thread and durable-id entry. Idempotent: a document with no
comments is a no-op.
people.xml is left alone — see
:func:~docx_plus.comments.clear_author_presence for why author
entries are not pruned automatically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
doc
|
Document
|
The python-docx :class: |
required |
remove_part
|
bool
|
When |
False
|