Skip to content

Commit 6c0024c

Browse files
committed
comments: add Comments.__len__()
1 parent 595decc commit 6c0024c

File tree

5 files changed

+85
-12
lines changed

5 files changed

+85
-12
lines changed

features/doc-comments.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ Feature: Document.comments
1515
| no |
1616

1717

18-
@wip
1918
Scenario Outline: Comments.__len__()
2019
Given a Comments object with <count> comments
2120
Then len(comments) == <count>

src/docx/comments.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ def __init__(self, comments_elm: CT_Comments, comments_part: CommentsPart):
1818
self._comments_elm = comments_elm
1919
self._comments_part = comments_part
2020

21+
def __len__(self) -> int:
22+
"""The number of comments in this collection."""
23+
return len(self._comments_elm.comment_lst)
24+
2125

2226
class Comment(BlockItemContainer):
2327
"""Proxy for a single comment in the document.

src/docx/oxml/__init__.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# ruff: noqa: E402, I001
2+
13
"""Initializes oxml sub-package.
24
35
This including registering custom element classes corresponding to Open XML elements.
@@ -84,16 +86,21 @@
8486
# ---------------------------------------------------------------------------
8587
# other custom element class mappings
8688

87-
from .coreprops import CT_CoreProperties # noqa
89+
from .comments import CT_Comments, CT_Comment
90+
91+
register_element_cls("w:comments", CT_Comments)
92+
register_element_cls("w:comment", CT_Comment)
93+
94+
from .coreprops import CT_CoreProperties
8895

8996
register_element_cls("cp:coreProperties", CT_CoreProperties)
9097

91-
from .document import CT_Body, CT_Document # noqa
98+
from .document import CT_Body, CT_Document
9299

93100
register_element_cls("w:body", CT_Body)
94101
register_element_cls("w:document", CT_Document)
95102

96-
from .numbering import CT_Num, CT_Numbering, CT_NumLvl, CT_NumPr # noqa
103+
from .numbering import CT_Num, CT_Numbering, CT_NumLvl, CT_NumPr
97104

98105
register_element_cls("w:abstractNumId", CT_DecimalNumber)
99106
register_element_cls("w:ilvl", CT_DecimalNumber)
@@ -104,7 +111,7 @@
104111
register_element_cls("w:numbering", CT_Numbering)
105112
register_element_cls("w:startOverride", CT_DecimalNumber)
106113

107-
from .section import ( # noqa
114+
from .section import (
108115
CT_HdrFtr,
109116
CT_HdrFtrRef,
110117
CT_PageMar,
@@ -122,11 +129,11 @@
122129
register_element_cls("w:sectPr", CT_SectPr)
123130
register_element_cls("w:type", CT_SectType)
124131

125-
from .settings import CT_Settings # noqa
132+
from .settings import CT_Settings
126133

127134
register_element_cls("w:settings", CT_Settings)
128135

129-
from .styles import CT_LatentStyles, CT_LsdException, CT_Style, CT_Styles # noqa
136+
from .styles import CT_LatentStyles, CT_LsdException, CT_Style, CT_Styles
130137

131138
register_element_cls("w:basedOn", CT_String)
132139
register_element_cls("w:latentStyles", CT_LatentStyles)
@@ -141,7 +148,7 @@
141148
register_element_cls("w:uiPriority", CT_DecimalNumber)
142149
register_element_cls("w:unhideWhenUsed", CT_OnOff)
143150

144-
from .table import ( # noqa
151+
from .table import (
145152
CT_Height,
146153
CT_Row,
147154
CT_Tbl,
@@ -178,7 +185,7 @@
178185
register_element_cls("w:vAlign", CT_VerticalJc)
179186
register_element_cls("w:vMerge", CT_VMerge)
180187

181-
from .text.font import ( # noqa
188+
from .text.font import (
182189
CT_Color,
183190
CT_Fonts,
184191
CT_Highlight,
@@ -217,11 +224,11 @@
217224
register_element_cls("w:vertAlign", CT_VerticalAlignRun)
218225
register_element_cls("w:webHidden", CT_OnOff)
219226

220-
from .text.paragraph import CT_P # noqa
227+
from .text.paragraph import CT_P
221228

222229
register_element_cls("w:p", CT_P)
223230

224-
from .text.parfmt import ( # noqa
231+
from .text.parfmt import (
225232
CT_Ind,
226233
CT_Jc,
227234
CT_PPr,

src/docx/oxml/comments.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from docx.oxml.xmlchemy import BaseOxmlElement
5+
from docx.oxml.xmlchemy import BaseOxmlElement, ZeroOrMore
66

77

88
class CT_Comments(BaseOxmlElement):
@@ -13,3 +13,17 @@ class CT_Comments(BaseOxmlElement):
1313
from the document text. The offset of the comment in this collection is arbitrary; it is
1414
essentially a _set_ implemented as a list.
1515
"""
16+
17+
# -- type-declarations to fill in the gaps for metaclass-added methods --
18+
comment_lst: list[CT_Comment]
19+
20+
comment = ZeroOrMore("w:comment")
21+
22+
23+
class CT_Comment(BaseOxmlElement):
24+
"""`w:comment` element, representing a single comment.
25+
26+
A comment is a so-called "story" and can contain paragraphs and tables much like a table-cell.
27+
While probably most often used for a single sentence or phrase, a comment can contain rich
28+
content, including multiple rich-text paragraphs, hyperlinks, images, and tables.
29+
"""

tests/test_comments.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
"""Unit test suite for the docx.comments module."""
2+
3+
from __future__ import annotations
4+
5+
from typing import cast
6+
7+
import pytest
8+
9+
from docx.comments import Comments
10+
from docx.opc.constants import CONTENT_TYPE as CT
11+
from docx.opc.packuri import PackURI
12+
from docx.oxml.comments import CT_Comments
13+
from docx.package import Package
14+
from docx.parts.comments import CommentsPart
15+
16+
from .unitutil.cxml import element
17+
from .unitutil.mock import FixtureRequest, Mock, instance_mock
18+
19+
20+
class DescribeComments:
21+
"""Unit-test suite for `docx.comments.Comments`."""
22+
23+
@pytest.mark.parametrize(
24+
("cxml", "count"),
25+
[
26+
("w:comments", 0),
27+
("w:comments/w:comment", 1),
28+
("w:comments/(w:comment,w:comment,w:comment)", 3),
29+
],
30+
)
31+
def it_knows_how_many_comments_it_contains(self, cxml: str, count: int, package_: Mock):
32+
comments_elm = cast(CT_Comments, element(cxml))
33+
comments = Comments(
34+
comments_elm,
35+
CommentsPart(
36+
PackURI("/word/comments.xml"),
37+
CT.WML_COMMENTS,
38+
comments_elm,
39+
package_,
40+
),
41+
)
42+
43+
assert len(comments) == count
44+
45+
# -- fixtures --------------------------------------------------------------------------------
46+
47+
@pytest.fixture
48+
def package_(self, request: FixtureRequest):
49+
return instance_mock(request, Package)

0 commit comments

Comments
 (0)