Skip to content

Commit 03c2fdb

Browse files
author
Steve Canny
committed
style: add Styles.add_style()
1 parent 400b4e5 commit 03c2fdb

File tree

3 files changed

+68
-0
lines changed

3 files changed

+68
-0
lines changed

docx/oxml/parts/styles.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ class CT_Styles(BaseOxmlElement):
5454
"""
5555
style = ZeroOrMore('w:style', successors=())
5656

57+
def add_style_of_type(self, name, style_type, builtin):
58+
"""
59+
Return a newly added `w:style` element having *name* and
60+
*style_type*. `w:style/@customStyle` is set based on the value of
61+
*builtin*.
62+
"""
63+
raise NotImplementedError
64+
5765
def default_for(self, style_type):
5866
"""
5967
Return `w:style[@w:type="*{style_type}*][-1]` or |None| if not found.

docx/styles/styles.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ def __iter__(self):
4848
def __len__(self):
4949
return len(self._element.style_lst)
5050

51+
def add_style(self, name, style_type, builtin=False):
52+
"""
53+
Return a newly added style object of *style_type* and identified
54+
by *name*. A builtin style can be defined by passing True for the
55+
optional *builtin* argument.
56+
"""
57+
name = self._translate_special_case_names(name)
58+
if name in self:
59+
raise ValueError("document already contains style '%s'" % name)
60+
style = self._element.add_style_of_type(name, style_type, builtin)
61+
return StyleFactory(style)
62+
5163
def default(self, style_type):
5264
"""
5365
Return the default style for *style_type* or |None| if no default is

tests/styles/test_styles.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import pytest
1212

1313
from docx.enum.style import WD_STYLE_TYPE
14+
from docx.oxml.parts.styles import CT_Style, CT_Styles
1415
from docx.styles.style import BaseStyle
1516
from docx.styles.styles import Styles
1617

@@ -54,6 +55,23 @@ def it_raises_on_style_not_found(self, get_raises_fixture):
5455
with pytest.raises(KeyError):
5556
styles[key]
5657

58+
def it_can_add_a_new_style(self, add_fixture):
59+
styles, name, style_type, builtin = add_fixture[:4]
60+
name_, StyleFactory_, style_elm_, style_ = add_fixture[4:]
61+
62+
style = styles.add_style(name, style_type, builtin)
63+
64+
styles._element.add_style_of_type.assert_called_once_with(
65+
name_, style_type, builtin
66+
)
67+
StyleFactory_.assert_called_once_with(style_elm_)
68+
assert style is style_
69+
70+
def it_raises_when_style_name_already_used(self, add_raises_fixture):
71+
styles, name = add_raises_fixture
72+
with pytest.raises(ValueError):
73+
styles.add_style(name, None)
74+
5775
def it_can_get_the_default_style_for_a_type(self, default_fixture):
5876
styles, style_type, StyleFactory_ = default_fixture[:3]
5977
StyleFactory_calls, style_ = default_fixture[3:]
@@ -119,6 +137,28 @@ def it_raises_on_style_type_mismatch(self, id_style_raises_fixture):
119137

120138
# fixture --------------------------------------------------------
121139

140+
@pytest.fixture(params=[
141+
('Foo Bar', 'Foo Bar', WD_STYLE_TYPE.CHARACTER, False),
142+
('Heading 1', 'heading 1', WD_STYLE_TYPE.PARAGRAPH, True),
143+
])
144+
def add_fixture(self, request, styles_elm_, _getitem_, style_elm_,
145+
StyleFactory_, style_):
146+
name, name_, style_type, builtin = request.param
147+
styles = Styles(styles_elm_)
148+
_getitem_.return_value = None
149+
styles_elm_.add_style_of_type.return_value = style_elm_
150+
StyleFactory_.return_value = style_
151+
return (
152+
styles, name, style_type, builtin, name_, StyleFactory_,
153+
style_elm_, style_
154+
)
155+
156+
@pytest.fixture
157+
def add_raises_fixture(self, _getitem_):
158+
styles = Styles(element('w:styles/w:style/w:name{w:val=heading 1}'))
159+
name = 'Heading 1'
160+
return styles, name
161+
122162
@pytest.fixture(params=[
123163
('w:styles',
124164
False, WD_STYLE_TYPE.CHARACTER),
@@ -319,3 +359,11 @@ def style_(self, request):
319359
@pytest.fixture
320360
def StyleFactory_(self, request):
321361
return function_mock(request, 'docx.styles.styles.StyleFactory')
362+
363+
@pytest.fixture
364+
def style_elm_(self, request):
365+
return instance_mock(request, CT_Style)
366+
367+
@pytest.fixture
368+
def styles_elm_(self, request):
369+
return instance_mock(request, CT_Styles)

0 commit comments

Comments
 (0)