Package xmlschema_acue :: Module helpers

Source Code for Module xmlschema_acue.helpers

  1  # -*- coding: utf-8 -*- 
  2  # 
  3  # Copyright (c), 2016-2019, SISSA (International School for Advanced Studies). 
  4  # All rights reserved. 
  5  # This file is distributed under the terms of the MIT License. 
  6  # See the file 'LICENSE' in the root directory of the present 
  7  # distribution, or http://opensource.org/licenses/MIT. 
  8  # 
  9  # @author Davide Brunato <brunato@sissa.it> 
 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   
23 -def get_namespace(name):
24 try: 25 return NAMESPACE_PATTERN.match(name).group(1) 26 except (AttributeError, TypeError): 27 return ''
28 29
30 -def get_qname(uri, name):
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
45 -def local_name(qname):
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
66 -def qname_to_prefixed(qname, namespaces):
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
90 -def get_xsd_annotation(elem):
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
104 -def iter_xsd_components(elem, start=0):
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
125 -def has_xsd_components(elem, start=0):
126 try: 127 next(iter_xsd_components(elem, start)) 128 except StopIteration: 129 return False 130 else: 131 return True
132 133
134 -def get_xsd_component(elem, required=True, strict=True):
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
161 -def get_xml_bool_attribute(elem, attribute, default=None):
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
181 -def get_xsd_derivation_attribute(elem, attribute, values=None):
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
206 -def get_xsd_form_attribute(elem, attribute):
207 """ 208 Get an XSD form attribute, checking the value. If the attribute is missing returns `None` 209 210 :param elem: the Element instance. 211 :param attribute: the attribute name (maybe 'form', or 'elementFormDefault' or 'attributeFormDefault'). 212 :return: a string. 213 """ 214 value = elem.get(attribute) 215 if value is None: 216 return 217 elif value not in ('qualified', 'unqualified'): 218 raise XMLSchemaValueError( 219 "wrong value %r for attribute %r, it must be 'qualified' or 'unqualified'." % (value, attribute) 220 ) 221 return value
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
255 - def reset(self):
256 self.min_occurs = self.max_occurs = 0
257