Repository URL to install this package:
|
Version:
0.8.9 ▾
|
# encoding: utf-8
"""
Shared code for unit test data builders
"""
from __future__ import absolute_import, print_function, unicode_literals
from docx.oxml import parse_xml
from docx.oxml.ns import nsdecls
class BaseBuilder(object):
"""
Provides common behavior for all data builders.
"""
def __init__(self):
self._empty = False
self._nsdecls = ''
self._text = ''
self._xmlattrs = []
self._xmlattr_method_map = {}
for attr_name in self.__attrs__:
base_name = (
attr_name.split(':')[1] if ':' in attr_name else attr_name
)
method_name = 'with_%s' % base_name
self._xmlattr_method_map[method_name] = attr_name
self._child_bldrs = []
def __getattr__(self, name):
"""
Intercept attribute access to generalize "with_{xmlattr_name}()"
methods.
"""
if name in self._xmlattr_method_map:
def with_xmlattr(value):
xmlattr_name = self._xmlattr_method_map[name]
self._set_xmlattr(xmlattr_name, value)
return self
return with_xmlattr
else:
tmpl = "'%s' object has no attribute '%s'"
raise AttributeError(tmpl % (self.__class__.__name__, name))
def clear(self):
"""
Reset this builder back to initial state so it can be reused within
a single test.
"""
BaseBuilder.__init__(self)
return self
@property
def element(self):
"""
Element parsed from XML generated by builder in current state
"""
elm = parse_xml(self.xml())
return elm
def with_child(self, child_bldr):
"""
Cause new child element specified by *child_bldr* to be appended to
the children of this element.
"""
self._child_bldrs.append(child_bldr)
return self
def with_text(self, text):
"""
Cause *text* to be placed between the start and end tags of this
element. Not robust enough for mixed elements, intended only for
elements having no child elements.
"""
self._text = text
return self
def with_nsdecls(self, *nspfxs):
"""
Cause the element to contain namespace declarations. By default, the
namespace prefixes defined in the Builder class are used. These can
be overridden by providing exlicit prefixes, e.g.
``with_nsdecls('a', 'r')``.
"""
if not nspfxs:
nspfxs = self.__nspfxs__
self._nsdecls = ' %s' % nsdecls(*nspfxs)
return self
def xml(self, indent=0):
"""
Return element XML based on attribute settings
"""
indent_str = ' ' * indent
if self._is_empty:
xml = '%s%s\n' % (indent_str, self._empty_element_tag)
else:
xml = '%s\n' % self._non_empty_element_xml(indent)
return xml
def xml_bytes(self, indent=0):
return self.xml(indent=indent).encode('utf-8')
@property
def _empty_element_tag(self):
return '<%s%s%s/>' % (self.__tag__, self._nsdecls, self._xmlattrs_str)
@property
def _end_tag(self):
return '</%s>' % self.__tag__
@property
def _is_empty(self):
return len(self._child_bldrs) == 0 and len(self._text) == 0
def _non_empty_element_xml(self, indent):
indent_str = ' ' * indent
if self._text:
xml = ('%s%s%s%s' %
(indent_str, self._start_tag, self._text, self._end_tag))
else:
xml = '%s%s\n' % (indent_str, self._start_tag)
for child_bldr in self._child_bldrs:
xml += child_bldr.xml(indent+2)
xml += '%s%s' % (indent_str, self._end_tag)
return xml
def _set_xmlattr(self, xmlattr_name, value):
xmlattr_str = ' %s="%s"' % (xmlattr_name, str(value))
self._xmlattrs.append(xmlattr_str)
@property
def _start_tag(self):
return '<%s%s%s>' % (self.__tag__, self._nsdecls, self._xmlattrs_str)
@property
def _xmlattrs_str(self):
"""
Return all element attributes as a string, like ' foo="bar" x="1"'.
"""
return ''.join(self._xmlattrs)