File: unitdata.py

package info (click to toggle)
python-docx 0.8.11%2Bdfsg1-5
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 6,640 kB
  • sloc: xml: 25,311; python: 21,911; makefile: 168
file content (142 lines) | stat: -rw-r--r-- 4,329 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# 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)