Skip to content

Commit 8ed60ee

Browse files
committed
hdr: add related_hdrftr_body
1 parent 3bcfd5c commit 8ed60ee

File tree

6 files changed

+64
-4
lines changed

6 files changed

+64
-4
lines changed

docx/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from docx.opc.parts.coreprops import CorePropertiesPart
1313

1414
from docx.parts.document import DocumentPart
15+
from docx.parts.header import HeaderPart
1516
from docx.parts.image import ImagePart
1617
from docx.parts.numbering import NumberingPart
1718
from docx.parts.settings import SettingsPart
@@ -30,6 +31,7 @@ def part_class_selector(content_type, reltype):
3031
PartFactory.part_type_for[CT.WML_NUMBERING] = NumberingPart
3132
PartFactory.part_type_for[CT.WML_SETTINGS] = SettingsPart
3233
PartFactory.part_type_for[CT.WML_STYLES] = StylesPart
34+
PartFactory.part_type_for[CT.WML_HEADER] = HeaderPart
3335

3436
del (
3537
CT, CorePropertiesPart, DocumentPart, NumberingPart, PartFactory,

docx/header.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
absolute_import, division, print_function, unicode_literals
99
)
1010

11+
from .blkcntnr import BlockItemContainer
1112
from .shared import ElementProxy, lazyproperty
1213

1314

@@ -51,7 +52,7 @@ class Header(_BaseHeaderFooter):
5152
"""
5253

5354

54-
class HeaderFooterBody(object):
55+
class HeaderFooterBody(BlockItemContainer):
5556
"""
5657
The rich-text body of a header or footer. Supports the same rich text
5758
operations as a document, such as paragraphs and tables.

docx/parts/document.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,15 @@ def related_hdrftr_body(self, rId):
126126
Return the |HeaderFooterBody| object corresponding to the related
127127
part identified by *rId*.
128128
"""
129-
raise NotImplementedError
129+
part = self.get_related_part(rId)
130+
return part.body
131+
132+
def get_related_part(self, rId):
133+
""" HACK this isn't strictly necessary
134+
just adding it because it seems much easier to mock than
135+
self.rels.related_parts
136+
"""
137+
return self.rels.related_parts[rId]
130138

131139
def save(self, path_or_stream):
132140
"""

docx/parts/header.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# encoding: utf-8
2+
3+
"""
4+
|HeaderPart| and closely related objects
5+
"""
6+
7+
from __future__ import (
8+
absolute_import, division, print_function, unicode_literals
9+
)
10+
11+
from ..header import HeaderFooterBody
12+
from ..opc.part import XmlPart
13+
14+
15+
class HeaderPart(XmlPart):
16+
@property
17+
def body(self):
18+
"""
19+
A |HeaderFooterBody| proxy object for the `w:hdr` element in this part,
20+
"""
21+
# TODO write CT_HeaderFooter
22+
# element = CT_HeaderFooter(self.element)
23+
# how to access parent here? is it necessary?
24+
return HeaderFooterBody(self.element, None)

features/steps/header.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def then_header_body_contains_the_text_of_the_header(context):
3939
@then('header.body is a BlockItemContainer object')
4040
def then_header_body_is_a_BlockItemContainer_object(context):
4141
header = context.header
42-
assert type(header.body).__name__ == 'BlockItemContainer'
42+
assert type(header.body).__name__ == 'HeaderFooterBody'
4343

4444

4545
@then('header.is_linked_to_previous is {value}')

tests/test_header.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@
1313
from docx.enum.header import WD_HEADER_FOOTER
1414
from docx.header import _BaseHeaderFooter, Header, HeaderFooterBody
1515
from docx.parts.document import DocumentPart
16+
from docx.parts.header import HeaderPart
1617

1718
from .unitutil.cxml import element
18-
from .unitutil.mock import call, instance_mock, property_mock
19+
from .unitutil.mock import call, instance_mock, method_mock, property_mock
1920

2021

2122
class Describe_BaseHeaderFooter(object):
@@ -30,8 +31,28 @@ def it_provides_access_to_its_body(self, body_fixture):
3031
assert header.part.related_hdrftr_body.call_args_list == calls
3132
assert body == expected_value
3233

34+
def it_provides_access_to_the_related_hdrftr_body(self, hdrftr_fixture):
35+
document_part, get_related_parts, header_part_ = hdrftr_fixture
36+
rId = 'rId1'
37+
body = document_part.related_hdrftr_body(rId)
38+
get_related_parts.assert_called_once_with(rId)
39+
assert body == header_part_.body
40+
3341
# fixtures -------------------------------------------------------
3442

43+
@pytest.fixture
44+
def hdrftr_fixture(self, request, header_part_, body_):
45+
header_part_.body = body_
46+
document_part = DocumentPart(None, None, None, None)
47+
48+
get_related_part = method_mock(
49+
request,
50+
DocumentPart,
51+
'get_related_part')
52+
get_related_part.return_value = header_part_
53+
54+
return document_part, get_related_part, header_part_
55+
3556
@pytest.fixture(params=[
3657
('w:sectPr', None),
3758
('w:sectPr/w:headerReference{w:type=even,r:id=rId6}', None),
@@ -64,6 +85,10 @@ def body_(self, request):
6485
def document_part_(self, request):
6586
return instance_mock(request, DocumentPart)
6687

88+
@pytest.fixture
89+
def header_part_(self, request):
90+
return instance_mock(request, HeaderPart)
91+
6792
@pytest.fixture
6893
def part_prop_(self, request, document_part_):
6994
return property_mock(

0 commit comments

Comments
 (0)