Package xmlschema_acue ::
Module helpers
1
2
3
4
5
6
7
8
9
10
11 """
12 This module contains various helper functions and classes.
13 """
14 import re
15
16 from xmlschema_acue.exceptions import XMLSchemaValueError, XMLSchemaTypeError, XMLSchemaKeyError
17 from xmlschema_acue.qnames import XSD_ANNOTATION
18
19 XSD_FINAL_ATTRIBUTE_VALUES = {'restriction', 'extension', 'list', 'union'}
20 NAMESPACE_PATTERN = re.compile(r'{([^}]*)}')
21
22
28
29
31 """
32 Returns an expanded QName from URI and local part. If any argument has boolean value
33 `False` or if the name is already an expanded QName, returns the *name* argument.
34
35 :param uri: namespace URI
36 :param name: local or qualified name
37 :return: string or the name argument
38 """
39 if not uri or not name or name[0] in ('{', '.', '/', '['):
40 return name
41 else:
42 return '{%s}%s' % (uri, name)
43
44
46 """
47 Return the local part of an expanded QName. If the name is `None` or empty
48 returns the *name* argument.
49
50 :param qname: an expanded QName or a local name.
51 """
52 try:
53 if qname[0] != '{':
54 return qname
55 return qname[qname.rindex('}') + 1:]
56 except IndexError:
57 return ''
58 except ValueError:
59 raise XMLSchemaValueError("wrong format for a universal name! %r" % qname)
60 except TypeError:
61 if qname is None:
62 return qname
63 raise XMLSchemaTypeError("required a string-like object or None! %r" % qname)
64
65
67 """
68 Transforms a fully qualified name into a prefixed name using a namespace map. Returns the
69 *qname* argument if it's not a fully qualified name or if it has boolean value `False`.
70
71 :param qname: a fully qualified name or a local name.
72 :param namespaces: a map from prefixes to namespace URIs.
73 :return: string with a prefixed or local reference.
74 """
75 if not qname:
76 return qname
77
78 namespace = get_namespace(qname)
79 for prefix, uri in sorted(filter(lambda x: x[1] == namespace, namespaces.items()), reverse=True):
80 if not uri:
81 return '%s:%s' % (prefix, qname) if prefix else qname
82 elif prefix:
83 return qname.replace('{%s}' % uri, '%s:' % prefix)
84 else:
85 return qname.replace('{%s}' % uri, '')
86 else:
87 return qname
88
89
91 """
92 Returns the annotation of an XSD component.
93
94 :param elem: ElementTree's node
95 :return: The first child element containing an XSD annotation, `None` if \
96 the XSD information item doesn't have an annotation.
97 """
98 try:
99 return elem[0] if elem[0].tag == XSD_ANNOTATION else None
100 except (TypeError, IndexError):
101 return
102
103
105 """
106 Returns an iterator for XSD child components, excluding the annotation.
107
108 :param elem: the parent Element.
109 :param start: the start child component to yield, the optional annotation is not counted. \
110 With the default value 0 starts from the first component.
111 """
112 counter = 0
113 for child in elem:
114 if child.tag == XSD_ANNOTATION:
115 if counter > 0:
116 raise XMLSchemaValueError("XSD annotation not allowed after the first position.")
117 else:
118 if start > 0:
119 start -= 1
120 else:
121 yield child
122 counter += 1
123
124
126 try:
127 next(iter_xsd_components(elem, start))
128 except StopIteration:
129 return False
130 else:
131 return True
132
133
135 """
136 Returns the first XSD component child, excluding the annotation.
137
138 :param elem: the parent Element.
139 :param required: if `True`, that is the default, raises a *ValueError* if there \
140 is not any component; with `False` in those cases `None` is returned.
141 :param strict: raises a *ValueError* if there is more than one component.
142 """
143 components_iterator = iter_xsd_components(elem)
144 try:
145 xsd_component = next(components_iterator)
146 except StopIteration:
147 if required:
148 raise XMLSchemaValueError("missing XSD component")
149 return None
150 else:
151 if not strict:
152 return xsd_component
153 try:
154 next(components_iterator)
155 except StopIteration:
156 return xsd_component
157 else:
158 raise XMLSchemaValueError("too many XSD components")
159
160
162 """
163 Get an XML boolean attribute.
164
165 :param elem: the Element instance.
166 :param attribute: the attribute name.
167 :param default: default value, accepted values are `True` or `False`.
168 :return: `True` or `False`.
169 """
170 value = elem.get(attribute, default)
171 if value is None:
172 raise XMLSchemaKeyError(attribute)
173 elif value in ('true', '1') or value is True:
174 return True
175 elif value in ('false', '0') or value is False:
176 return False
177 else:
178 raise XMLSchemaTypeError("an XML boolean value is required for attribute %r" % attribute)
179
180
182 """
183 Get a derivation attribute (maybe 'block', 'blockDefault', 'final' or 'finalDefault')
184 checking the items with the values arguments. Returns a string.
185
186 :param elem: the Element instance.
187 :param attribute: the attribute name.
188 :param values: sequence of admitted values when the attribute value is not '#all'.
189 :return: a string.
190 """
191 value = elem.get(attribute)
192 if value is None:
193 return ''
194
195 if values is None:
196 values = XSD_FINAL_ATTRIBUTE_VALUES
197
198 items = value.split()
199 if len(items) == 1 and items[0] == '#all':
200 return ' '.join(values)
201 elif not all([s in values for s in items]):
202 raise XMLSchemaValueError("wrong value %r for attribute %r." % (value, attribute))
203 return value
204
205
222
223
224 -class ParticleCounter(object):
225 """
226 An helper class for counting total min/max occurrences of XSD particles.
227 """
228 - def __init__(self):
229 self.min_occurs = self.max_occurs = 0
230
231 - def __repr__(self):
232 return '%s(%r, %r)' % (self.__class__.__name__, self.min_occurs, self.max_occurs)
233
234 - def __add__(self, other):
235 self.min_occurs += other.min_occurs
236 if self.max_occurs is not None:
237 if other.max_occurs is None:
238 self.max_occurs = None
239 else:
240 self.max_occurs += other.max_occurs
241 return self
242
243 - def __mul__(self, other):
244 self.min_occurs *= other.min_occurs
245 if self.max_occurs is None:
246 if other.max_occurs == 0:
247 self.max_occurs = 0
248 elif other.max_occurs is None:
249 if self.max_occurs != 0:
250 self.max_occurs = None
251 else:
252 self.max_occurs *= other.max_occurs
253 return self
254
256 self.min_occurs = self.max_occurs = 0
257