Skip to content

Commit b8abd27

Browse files
author
Steve Canny
committed
style: add _CharacterStyle.base_style getter
1 parent fd51c87 commit b8abd27

File tree

5 files changed

+73
-5
lines changed

5 files changed

+73
-5
lines changed

docx/oxml/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,10 @@ def OxmlElement(nsptag_str, attrs=None, nsdecls=None):
114114
register_element_cls('wp:inline', CT_Inline)
115115

116116
from .styles import CT_Style, CT_Styles
117-
register_element_cls('w:name', CT_String)
118-
register_element_cls('w:style', CT_Style)
119-
register_element_cls('w:styles', CT_Styles)
117+
register_element_cls('w:basedOn', CT_String)
118+
register_element_cls('w:name', CT_String)
119+
register_element_cls('w:style', CT_Style)
120+
register_element_cls('w:styles', CT_Styles)
120121

121122
from .table import (
122123
CT_Row, CT_Tbl, CT_TblGrid, CT_TblGridCol, CT_TblLayoutType, CT_TblPr,

docx/oxml/styles.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,29 @@ class CT_Style(BaseOxmlElement):
4242
'w:tblPr', 'w:trPr', 'w:tcPr', 'w:tblStylePr'
4343
)
4444
name = ZeroOrOne('w:name', successors=_tag_seq[1:])
45+
basedOn = ZeroOrOne('w:basedOn', successors=_tag_seq[3:])
4546
pPr = ZeroOrOne('w:pPr', successors=_tag_seq[17:])
4647
type = OptionalAttribute('w:type', WD_STYLE_TYPE)
4748
styleId = OptionalAttribute('w:styleId', ST_String)
4849
default = OptionalAttribute('w:default', ST_OnOff)
4950
customStyle = OptionalAttribute('w:customStyle', ST_OnOff)
5051
del _tag_seq
5152

53+
@property
54+
def base_style(self):
55+
"""
56+
Sibling CT_Style element this style is based on or |None| if no base
57+
style or base style not found.
58+
"""
59+
basedOn = self.basedOn
60+
if basedOn is None:
61+
return None
62+
styles = self.getparent()
63+
base_style = styles.get_by_id(basedOn.val)
64+
if base_style is None:
65+
return None
66+
return base_style
67+
5268
def delete(self):
5369
"""
5470
Remove this `w:style` element from its parent `w:styles` element.

docx/styles/style.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,17 @@ class _CharacterStyle(BaseStyle):
118118

119119
__slots__ = ()
120120

121+
@property
122+
def base_style(self):
123+
"""
124+
Style object this style inherits from or |None| if this style is
125+
not based on another style.
126+
"""
127+
base_style = self._element.base_style
128+
if base_style is None:
129+
return None
130+
return StyleFactory(base_style)
131+
121132

122133
class _ParagraphStyle(_CharacterStyle):
123134
"""

features/sty-style-props.feature

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Feature: Get and set style properties
44
I need a set of read/write style properties
55

66

7-
@wip
87
Scenario Outline: Get base style
98
Given a style based on <base-style>
109
Then style.base_style is <value>

tests/styles/test_style.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
)
1818

1919
from ..unitutil.cxml import element, xml
20-
from ..unitutil.mock import class_mock, instance_mock
20+
from ..unitutil.mock import call, class_mock, function_mock, instance_mock
2121

2222

2323
class DescribeStyleFactory(object):
@@ -198,3 +198,44 @@ def type_get_fixture(self, request):
198198
style_cxml, expected_value = request.param
199199
style = BaseStyle(element(style_cxml))
200200
return style, expected_value
201+
202+
203+
class Describe_CharacterStyle(object):
204+
205+
def it_knows_which_style_it_is_based_on(self, base_get_fixture):
206+
style, StyleFactory_, StyleFactory_calls, base_style_ = (
207+
base_get_fixture
208+
)
209+
base_style = style.base_style
210+
211+
assert StyleFactory_.call_args_list == StyleFactory_calls
212+
assert base_style == base_style_
213+
214+
# fixture --------------------------------------------------------
215+
216+
@pytest.fixture(params=[
217+
('w:styles/(w:style{w:styleId=Foo},w:style/w:basedOn{w:val=Foo})',
218+
1, 0),
219+
('w:styles/(w:style{w:styleId=Foo},w:style/w:basedOn{w:val=Bar})',
220+
1, -1),
221+
('w:styles/w:style',
222+
0, -1),
223+
])
224+
def base_get_fixture(self, request, StyleFactory_):
225+
styles_cxml, style_idx, base_style_idx = request.param
226+
styles = element(styles_cxml)
227+
style = _CharacterStyle(styles[style_idx])
228+
if base_style_idx >= 0:
229+
base_style = styles[base_style_idx]
230+
StyleFactory_calls = [call(base_style)]
231+
expected_value = StyleFactory_.return_value
232+
else:
233+
StyleFactory_calls = []
234+
expected_value = None
235+
return style, StyleFactory_, StyleFactory_calls, expected_value
236+
237+
# fixture components ---------------------------------------------
238+
239+
@pytest.fixture
240+
def StyleFactory_(self, request):
241+
return function_mock(request, 'docx.styles.style.StyleFactory')

0 commit comments

Comments
 (0)