Skip to content

Commit e493474

Browse files
committed
fix: XmlPart._rel_ref_count
`.rel_ref_count()` as implemented was only applicable to `XmlPart` where references to a related part could be present in the XML. Longer term it probably makes sense to override `Part.drop_rel()` in `XmlPart` and not have a `_rel_ref_count()` method in `part` at all, but this works and is less potentially disruptive.
1 parent 3f56b7d commit e493474

File tree

2 files changed

+38
-20
lines changed

2 files changed

+38
-20
lines changed

src/docx/opc/part.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from __future__ import annotations
44

5-
from typing import TYPE_CHECKING, Callable, Dict, Type
5+
from typing import TYPE_CHECKING, Callable, Dict, Type, cast
66

77
from docx.opc.oxml import serialize_part_xml
88
from docx.opc.packuri import PackURI
@@ -149,11 +149,12 @@ def target_ref(self, rId: str) -> str:
149149
rel = self.rels[rId]
150150
return rel.target_ref
151151

152-
def _rel_ref_count(self, rId):
153-
"""Return the count of references in this part's XML to the relationship
154-
identified by `rId`."""
155-
rIds = self._element.xpath("//@r:id")
156-
return len([_rId for _rId in rIds if _rId == rId])
152+
def _rel_ref_count(self, rId: str) -> int:
153+
"""Return the count of references in this part to the relationship identified by `rId`.
154+
155+
Only an XML part can contain references, so this is 0 for `Part`.
156+
"""
157+
return 0
157158

158159

159160
class PartFactory:
@@ -231,3 +232,9 @@ def part(self):
231232
That chain of delegation ends here for child objects.
232233
"""
233234
return self
235+
236+
def _rel_ref_count(self, rId: str) -> int:
237+
"""Return the count of references in this part's XML to the relationship
238+
identified by `rId`."""
239+
rIds = cast("list[str]", self._element.xpath("//@r:id"))
240+
return len([_rId for _rId in rIds if _rId == rId])

tests/opc/test_part.py

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -169,24 +169,13 @@ def it_can_establish_an_external_relationship(self, rels_prop_: Mock, rels_: Moc
169169
rels_.get_or_add_ext_rel.assert_called_once_with("http://rel/type", "https://hyper/link")
170170
assert rId == "rId27"
171171

172-
@pytest.mark.parametrize(
173-
("part_cxml", "rel_should_be_dropped"),
174-
[
175-
("w:p", True),
176-
("w:p/r:a{r:id=rId42}", True),
177-
("w:p/r:a{r:id=rId42}/r:b{r:id=rId42}", False),
178-
],
179-
)
180-
def it_can_drop_a_relationship(
181-
self, part_cxml: str, rel_should_be_dropped: bool, rels_prop_: Mock
182-
):
172+
def it_can_drop_a_relationship(self, rels_prop_: Mock):
183173
rels_prop_.return_value = {"rId42": None}
184-
part = Part("partname", "content_type")
185-
part._element = element(part_cxml) # pyright: ignore[reportAttributeAccessIssue]
174+
part = Part(PackURI("/partname"), "content_type")
186175

187176
part.drop_rel("rId42")
188177

189-
assert ("rId42" not in part.rels) is rel_should_be_dropped
178+
assert "rId42" not in part.rels
190179

191180
def it_can_find_a_related_part_by_reltype(
192181
self, rels_prop_: Mock, rels_: Mock, other_part_: Mock
@@ -411,6 +400,24 @@ def it_knows_its_the_part_for_its_child_objects(self, part_fixture):
411400
xml_part = part_fixture
412401
assert xml_part.part is xml_part
413402

403+
@pytest.mark.parametrize(
404+
("part_cxml", "rel_should_be_dropped"),
405+
[
406+
("w:p", True),
407+
("w:p/r:a{r:id=rId42}", True),
408+
("w:p/r:a{r:id=rId42}/r:b{r:id=rId42}", False),
409+
],
410+
)
411+
def it_only_drops_a_relationship_with_zero_reference_count(
412+
self, part_cxml: str, rel_should_be_dropped: bool, rels_prop_: Mock, package_: Mock
413+
):
414+
rels_prop_.return_value = {"rId42": None}
415+
part = XmlPart(PackURI("/partname"), "content_type", element(part_cxml), package_)
416+
417+
part.drop_rel("rId42")
418+
419+
assert ("rId42" not in part.rels) is rel_should_be_dropped
420+
414421
# fixtures -------------------------------------------------------
415422

416423
@pytest.fixture
@@ -452,6 +459,10 @@ def parse_xml_(self, request, element_):
452459
def partname_(self, request):
453460
return instance_mock(request, PackURI)
454461

462+
@pytest.fixture
463+
def rels_prop_(self, request: FixtureRequest):
464+
return property_mock(request, XmlPart, "rels")
465+
455466
@pytest.fixture
456467
def serialize_part_xml_(self, request):
457468
return function_mock(request, "docx.opc.part.serialize_part_xml")

0 commit comments

Comments
 (0)