Package xmlschema_acue :: Package validators :: Module wildcards

Source Code for Module xmlschema_acue.validators.wildcards

  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 classes for XML Schema wildcards. 
 13  """ 
 14  from __future__ import unicode_literals 
 15   
 16  from xmlschema_acue.exceptions import XMLSchemaValueError 
 17  from xmlschema_acue.qnames import XSD_ANY, XSD_ANY_ATTRIBUTE, XSD_OPEN_CONTENT, XSD_DEFAULT_OPEN_CONTENT 
 18  from xmlschema_acue.helpers import get_namespace 
 19  from xmlschema_acue.namespaces import XSI_NAMESPACE 
 20  from xmlschema_acue.xpath import ElementPathMixin 
 21   
 22  from xmlschema_acue.validators.exceptions import XMLSchemaNotBuiltError 
 23  from xmlschema_acue.validators.xsdbase import ValidationMixin, XsdComponent, ParticleMixin 
24 25 26 -class XsdWildcard(XsdComponent, ValidationMixin):
27 names = {} 28
29 - def __init__(self, elem, schema, parent):
30 if parent is None: 31 raise XMLSchemaValueError("'parent' attribute is None but %r cannot be global!" % self) 32 super(XsdWildcard, self).__init__(elem, schema, parent)
33
34 - def __repr__(self):
35 return '%s(namespace=%r, process_contents=%r)' % ( 36 self.__class__.__name__, self.namespace, self.process_contents 37 )
38
39 - def _parse(self):
40 super(XsdWildcard, self)._parse() 41 42 # Parse namespace and processContents 43 namespace = self.elem.get('namespace', '##any') 44 items = namespace.strip().split() 45 if len(items) == 1 and items[0] in ('##any', '##other', '##local', '##targetNamespace'): 46 self.namespace = namespace.strip() 47 elif not all(not s.startswith('##') or s in {'##local', '##targetNamespace'} for s in items): 48 self.parse_error("wrong value %r for 'namespace' attribute." % namespace) 49 self.namespace = '##any' 50 else: 51 self.namespace = namespace.strip() 52 53 self.process_contents = self.elem.get('processContents', 'strict') 54 if self.process_contents not in {'lax', 'skip', 'strict'}: 55 self.parse_error("wrong value %r for 'processContents' attribute." % self.process_contents)
56
57 - def _load_namespace(self, namespace):
58 if namespace in self.schema.maps.namespaces: 59 return 60 61 for url in self.schema.get_locations(namespace): 62 try: 63 schema = self.schema.import_schema(namespace, url, base_url=self.schema.base_url) 64 if schema is not None: 65 try: 66 schema.maps.build() 67 except XMLSchemaNotBuiltError: 68 # Namespace build fails: remove unbuilt schemas and the url hint 69 schema.maps.clear(remove_schemas=True, only_unbuilt=True) 70 self.schema.locations[namespace].remove(url) 71 else: 72 break 73 except (OSError, IOError): 74 pass
75 76 @property
77 - def built(self):
78 return True
79
80 - def iter_namespaces(self):
81 if self.namespace in ('##any', '##other'): 82 return 83 for ns in self.namespace.split(): 84 if ns == '##local': 85 yield '' 86 elif ns == '##targetNamespace': 87 yield self.target_namespace 88 else: 89 yield ns
90
91 - def is_matching(self, name, default_namespace=None):
92 if name is None: 93 return False 94 elif not name or name[0] == '{': 95 return self.is_namespace_allowed(get_namespace(name)) 96 elif default_namespace is None: 97 return self.is_namespace_allowed('') 98 else: 99 return self.is_namespace_allowed(default_namespace)
100
101 - def is_namespace_allowed(self, namespace):
102 if self.namespace == '##any' or namespace == XSI_NAMESPACE: 103 return True 104 elif self.namespace == '##other': 105 if namespace: 106 return namespace != self.target_namespace 107 else: 108 return False 109 else: 110 any_namespaces = self.namespace.split() 111 if '##local' in any_namespaces and namespace == '': 112 return True 113 elif '##targetNamespace' in any_namespaces and namespace == self.target_namespace: 114 return True 115 else: 116 return namespace in any_namespaces
117
118 - def is_restriction(self, other, check_occurs=True):
119 if check_occurs and isinstance(self, ParticleMixin) and not self.has_occurs_restriction(other): 120 return False 121 elif not isinstance(other, type(self)): 122 return False 123 elif other.process_contents == 'strict' and self.process_contents != 'strict': 124 return False 125 elif other.process_contents == 'lax' and self.process_contents == 'skip': 126 return False 127 elif self.namespace == other.namespace: 128 return True 129 elif other.namespace == '##any': 130 return True 131 elif self.namespace == '##any': 132 return False 133 134 other_namespaces = other.namespace.split() 135 for ns in self.namespace.split(): 136 if ns in other_namespaces: 137 continue 138 elif ns == self.target_namespace: 139 if '##targetNamespace' in other_namespaces: 140 continue 141 elif not ns.startswith('##') and '##other' in other_namespaces: 142 continue 143 return False 144 return True
145
146 - def iter_decode(self, source, validation='lax', *args, **kwargs):
147 raise NotImplementedError
148
149 - def iter_encode(self, obj, validation='lax', *args, **kwargs):
150 raise NotImplementedError
151
152 153 -class XsdAnyElement(XsdWildcard, ParticleMixin, ElementPathMixin):
154 """ 155 Class for XSD 1.0 'any' wildcards. 156 157 <any 158 id = ID 159 maxOccurs = (nonNegativeInteger | unbounded) : 1 160 minOccurs = nonNegativeInteger : 1 161 namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) ) : ##any 162 processContents = (lax | skip | strict) : strict 163 {any attributes with non-schema namespace . . .}> 164 Content: (annotation?) 165 </any> 166 """ 167 _admitted_tags = {XSD_ANY} 168
169 - def __repr__(self):
170 return '%s(namespace=%r, process_contents=%r, occurs=%r)' % ( 171 self.__class__.__name__, self.namespace, self.process_contents, self.occurs 172 )
173
174 - def _parse(self):
175 super(XsdAnyElement, self)._parse() 176 self._parse_particle(self.elem)
177
178 - def is_emptiable(self):
179 return self.min_occurs == 0 or self.process_contents != 'strict'
180
181 - def match(self, name, default_namespace=None):
182 if self.is_matching(name, default_namespace): 183 try: 184 if name[0] != '{' and default_namespace: 185 return self.maps.lookup_element('{%s}%s' % (default_namespace, name)) 186 else: 187 return self.maps.lookup_element(name) 188 except LookupError: 189 pass
190
191 - def __iter__(self):
192 return iter(())
193
194 - def iter(self, tag=None):
195 return iter(())
196
197 - def iterchildren(self, tag=None):
198 return iter(())
199 200 @staticmethod
201 - def iter_substitutes():
202 return iter(())
203
204 - def iter_decode(self, elem, validation='lax', converter=None, **kwargs):
205 if self.process_contents == 'skip': 206 return 207 208 namespace = get_namespace(elem.tag) 209 if self.is_namespace_allowed(namespace): 210 self._load_namespace(namespace) 211 try: 212 xsd_element = self.maps.lookup_element(elem.tag) 213 except LookupError: 214 if self.process_contents == 'strict' and validation != 'skip': 215 reason = "element %r not found." % elem.tag 216 yield self.validation_error(validation, reason, elem, **kwargs) 217 else: 218 for result in xsd_element.iter_decode(elem, validation, converter, **kwargs): 219 yield result 220 elif validation != 'skip': 221 reason = "element %r not allowed here." % elem.tag 222 yield self.validation_error(validation, reason, elem, **kwargs)
223
224 - def iter_encode(self, obj, validation='lax', converter=None, **kwargs):
225 if self.process_contents == 'skip': 226 return 227 228 name, value = obj 229 namespace = get_namespace(name) 230 if self.is_namespace_allowed(namespace): 231 self._load_namespace(namespace) 232 try: 233 xsd_element = self.maps.lookup_element(name) 234 except LookupError: 235 if self.process_contents == 'strict' and validation != 'skip': 236 reason = "element %r not found." % name 237 yield self.validation_error(validation, reason, **kwargs) 238 else: 239 for result in xsd_element.iter_encode(value, validation, converter, **kwargs): 240 yield result 241 elif validation != 'skip': 242 reason = "element %r not allowed here." % name 243 yield self.validation_error(validation, reason, value, **kwargs)
244
245 - def overlap(self, other):
246 if not isinstance(other, XsdAnyElement): 247 return other.overlap(self) 248 elif self.namespace == other.namespace: 249 return True 250 elif self.namespace == '##any' or other.namespace == '##any': 251 return True 252 elif self.namespace == '##other': 253 return any(not ns.startswith('##') and ns != self.target_namespace for ns in other.namespace.split()) 254 elif other.namespace == '##other': 255 return any(not ns.startswith('##') and ns != other.target_namespace for ns in self.namespace.split()) 256 257 any_namespaces = self.namespace.split() 258 return any(ns in any_namespaces for ns in other.namespace.split())
259
260 261 -class XsdAnyAttribute(XsdWildcard):
262 """ 263 Class for XSD 1.0 'anyAttribute' wildcards. 264 265 <anyAttribute 266 id = ID 267 namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) ) 268 processContents = (lax | skip | strict) : strict 269 {any attributes with non-schema namespace . . .}> 270 Content: (annotation?) 271 </anyAttribute> 272 """ 273 _admitted_tags = {XSD_ANY_ATTRIBUTE} 274
275 - def extend_namespace(self, other):
276 if self.namespace == '##any' or self.namespace == other.namespace: 277 return 278 elif other.namespace == '##any': 279 self.namespace = other.namespace 280 return 281 elif other.namespace == '##other': 282 w1, w2 = other, self 283 elif self.namespace == '##other': 284 w1, w2 = self, other 285 elif self.target_namespace == other.target_namespace: 286 self.namespace = ' '.join(set(other.namespace.split() + self.namespace.split())) 287 return 288 else: 289 self.namespace = ' '.join(set(list(other.iter_namespaces()) + self.namespace.split())) 290 return 291 292 namespaces = set(w2.iter_namespaces()) 293 if w1.target_namespace in namespaces and '' in namespaces: 294 self.namespace = '##any' 295 elif '' not in namespaces and w1.target_namespace == w2.target_namespace: 296 self.namespace = '##other' 297 else: 298 msg = "not expressible wildcard namespace union: {!r} V {!r}:" 299 raise XMLSchemaValueError(msg.format(other.namespace, self.namespace))
300
301 - def match(self, name, default_namespace=None):
302 if self.is_matching(name, default_namespace): 303 try: 304 if name[0] != '{' and default_namespace: 305 return self.maps.lookup_attribute('{%s}%s' % (default_namespace, name)) 306 else: 307 return self.maps.lookup_attribute(name) 308 except LookupError: 309 pass
310
311 - def iter_decode(self, attribute, validation='lax', **kwargs):
312 if self.process_contents == 'skip': 313 return 314 315 name, value = attribute 316 namespace = get_namespace(name) 317 if self.is_namespace_allowed(namespace): 318 self._load_namespace(namespace) 319 try: 320 xsd_attribute = self.maps.lookup_attribute(name) 321 except LookupError: 322 if self.process_contents == 'strict' and validation != 'skip': 323 reason = "attribute %r not found." % name 324 yield self.validation_error(validation, reason, attribute, **kwargs) 325 else: 326 for result in xsd_attribute.iter_decode(value, validation, **kwargs): 327 yield result 328 elif validation != 'skip': 329 reason = "attribute %r not allowed." % name 330 yield self.validation_error(validation, reason, attribute, **kwargs)
331
332 - def iter_encode(self, attribute, validation='lax', **kwargs):
333 if self.process_contents == 'skip': 334 return 335 336 name, value = attribute 337 namespace = get_namespace(name) 338 if self.is_namespace_allowed(namespace): 339 self._load_namespace(namespace) 340 try: 341 xsd_attribute = self.maps.lookup_attribute(name) 342 except LookupError: 343 if self.process_contents == 'strict' and validation != 'skip': 344 reason = "attribute %r not found." % name 345 yield self.validation_error(validation, reason, attribute, **kwargs) 346 else: 347 for result in xsd_attribute.iter_encode(value, validation, **kwargs): 348 yield result 349 elif validation != 'skip': 350 reason = "attribute %r not allowed." % name 351 yield self.validation_error(validation, reason, attribute, **kwargs)
352
353 354 -class Xsd11Wildcard(XsdWildcard):
355
356 - def _parse(self):
357 super(Xsd11Wildcard, self)._parse() 358 359 # Parse notNamespace attribute 360 try: 361 not_namespace = self.elem.attrib['notNamespace'].strip() 362 except KeyError: 363 self.not_namespace = None 364 else: 365 if 'namespace' in self.elem.attrib: 366 self.not_namespace = None 367 self.parse_error("'namespace' and 'notNamespace' attributes are mutually exclusive.") 368 elif not_namespace in ('##local', '##targetNamespace'): 369 self.not_namespace = not_namespace 370 else: 371 self.not_namespace = not_namespace.split() 372 373 # Parse notQName attribute 374 try: 375 not_qname = self.elem.attrib['notQName'].strip() 376 except KeyError: 377 self.not_qname = None 378 else: 379 if not_qname in ('##defined', '##definedSibling'): 380 self.not_qname = not_qname 381 else: 382 self.not_qname = not_qname.split()
383
384 - def is_namespace_allowed(self, namespace):
385 if self.namespace == '##any' or namespace == XSI_NAMESPACE: 386 return True 387 elif self.namespace == '##other': 388 if namespace: 389 return namespace != self.target_namespace 390 else: 391 return False 392 else: 393 any_namespaces = self.namespace.split() 394 if '##local' in any_namespaces and namespace == '': 395 return True 396 elif '##targetNamespace' in any_namespaces and namespace == self.target_namespace: 397 return True 398 else: 399 return namespace in any_namespaces
400
401 402 -class Xsd11AnyElement(XsdAnyElement):
403 """ 404 Class for XSD 1.1 'any' declarations. 405 406 <any 407 id = ID 408 maxOccurs = (nonNegativeInteger | unbounded) : 1 409 minOccurs = nonNegativeInteger : 1 410 namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) ) 411 notNamespace = List of (anyURI | (##targetNamespace | ##local)) 412 notQName = List of (QName | (##defined | ##definedSibling)) 413 processContents = (lax | skip | strict) : strict 414 {any attributes with non-schema namespace . . .}> 415 Content: (annotation?) 416 </any> 417 """ 418 pass
419
420 421 -class Xsd11AnyAttribute(XsdAnyAttribute):
422 """ 423 Class for XSD 1.1 'anyAttribute' declarations. 424 425 <anyAttribute 426 id = ID 427 namespace = ((##any | ##other) | List of (anyURI | (##targetNamespace | ##local)) ) 428 notNamespace = List of (anyURI | (##targetNamespace | ##local)) 429 notQName = List of (QName | ##defined) 430 processContents = (lax | skip | strict) : strict 431 {any attributes with non-schema namespace . . .}> 432 Content: (annotation?) 433 </anyAttribute> 434 """ 435 pass
436
437 438 -class XsdOpenContent(XsdComponent):
439 """ 440 Class for XSD 1.1 'openContent' model definitions. 441 442 <openContent 443 id = ID 444 mode = (none | interleave | suffix) : interleave 445 {any attributes with non-schema namespace . . .}> 446 Content: (annotation?), (any?) 447 </openContent> 448 """ 449 _admitted_tags = {XSD_OPEN_CONTENT, XSD_DEFAULT_OPEN_CONTENT} 450
451 - def __init__(self, elem, schema, parent):
452 super(XsdOpenContent, self).__init__(elem, schema, parent) 453 self.mode = self.elem.get('mode', 'interleave') 454 if self.mode not in ('none', 'interleave', 'suffix'): 455 self.parse_error("wrong value %r for 'mode' attribute." % self.mode)
456
457 - def _parse(self):
458 super(XsdOpenContent, self)._parse() 459 child = self._parse_component(self.elem) 460 if child is None: 461 if self.elem.tag == XSD_DEFAULT_OPEN_CONTENT: 462 self.parse_error("a %r declaration cannot be empty:" % self.elem.tag, self.elem) 463 self.any_element = None 464 elif child.tag == XSD_ANY: 465 self.any_element = Xsd11AnyElement(child, self.schema, self) 466 else: 467 self.any_element = None 468 if self.schema.validation == 'skip': 469 # Also generated by meta-schema validation for 'lax' and 'strict' modes 470 self.parse_error("unexpected tag %r for openContent child:" % child.tag, self.elem)
471