Package xmlschema_acue :: Package validators :: Module xsdbase

Source Code for Module xmlschema_acue.validators.xsdbase

  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 base functions and classes XML Schema components. 
 13  """ 
 14  from __future__ import unicode_literals 
 15  import re 
 16   
 17  from xmlschema_acue.compat import PY3, string_base_type, unicode_type 
 18  from xmlschema_acue.exceptions import XMLSchemaValueError, XMLSchemaTypeError 
 19  from xmlschema_acue.qnames import XSD_ANNOTATION, XSD_APPINFO, XSD_DOCUMENTATION, XML_LANG, XSD_ANY_TYPE, XSD_ID 
 20  from xmlschema_acue.helpers import get_qname, local_name, qname_to_prefixed, iter_xsd_components, get_xsd_component 
 21  from xmlschema_acue.etree import etree_tostring, is_etree_element 
 22  from xmlschema_acue.validators.exceptions import XMLSchemaParseError, XMLSchemaValidationError, XMLSchemaDecodeError, XMLSchemaEncodeError 
 23   
 24  XSD_VALIDATION_MODES = {'strict', 'lax', 'skip'} 
 25  """ 
 26  XML Schema validation modes 
 27  Ref.: https://www.w3.org/TR/xmlschema11-1/#key-va 
 28  """ 
29 30 31 -class XsdValidator(object):
32 """ 33 Common base class for XML Schema validator, that represents a PSVI (Post Schema Validation 34 Infoset) information item. A concrete XSD validator have to report its validity collecting 35 building errors and implementing the properties. 36 37 :param validation: defines the XSD validation mode to use for build the validator, \ 38 its value can be 'strict', 'lax' or 'skip'. Strict mode is the default. 39 :type validation: str 40 41 :ivar validation: XSD validation mode. 42 :vartype validation: str 43 :ivar errors: XSD validator building errors. 44 :vartype errors: list 45 """
46 - def __init__(self, validation='strict'):
47 if validation not in XSD_VALIDATION_MODES: 48 raise XMLSchemaValueError("validation argument can be 'strict', 'lax' or 'skip': %r" % validation) 49 self.validation = validation 50 self.errors = []
51
52 - def __str__(self):
53 # noinspection PyCompatibility,PyUnresolvedReferences 54 return unicode(self).encode("utf-8")
55
56 - def __unicode__(self):
57 return self.__repr__()
58 59 if PY3: 60 __str__ = __unicode__ 61 62 @property
63 - def built(self):
64 """ 65 Property that is ``True`` if schema validator has been fully parsed and built, ``False`` otherwise. 66 """ 67 raise NotImplementedError
68 69 @property
70 - def validation_attempted(self):
71 """ 72 Property that returns the *validation status* of the XSD validator. It can be 'full', 'partial' or 'none'. 73 74 | https://www.w3.org/TR/xmlschema-1/#e-validation_attempted 75 | https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#e-validation_attempted 76 """ 77 raise NotImplementedError
78 79 @property
80 - def validity(self):
81 """ 82 Property that returns the XSD validator's validity. It can be ‘valid’, ‘invalid’ or ‘notKnown’. 83 84 | https://www.w3.org/TR/xmlschema-1/#e-validity 85 | https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#e-validity 86 """ 87 if self.errors or any([comp.errors for comp in self.iter_components()]): 88 return 'invalid' 89 elif self.built: 90 return 'valid' 91 else: 92 return 'notKnown'
93
94 - def iter_components(self, xsd_classes=None):
95 """ 96 Creates an iterator for traversing all XSD components of the validator. 97 98 :param xsd_classes: returns only a specific class/classes of components, \ 99 otherwise returns all components. 100 """ 101 raise NotImplementedError
102 103 @property
104 - def all_errors(self):
105 """ 106 A list with all the building errors of the XSD validator and its components. 107 """ 108 errors = [] 109 for comp in self.iter_components(): 110 if comp.errors: 111 errors.extend(comp.errors) 112 return errors
113
114 - def copy(self):
115 validator = object.__new__(self.__class__) 116 validator.__dict__.update(self.__dict__) 117 validator.errors = self.errors[:] 118 return validator
119 120 __copy__ = copy 121
122 - def parse_error(self, error, elem=None):
123 """ 124 Helper method for registering parse errors. Does nothing if validation mode is 'skip'. 125 Il validation mode is 'lax' collects the error, otherwise raise the error. 126 127 :param error: can be a parse error or an error message. 128 :param elem: the Element instance related to the error, for default uses the 'elem' \ 129 attribute of the validator, if it's present. 130 """ 131 if self.validation == 'skip': 132 return 133 134 if is_etree_element(elem): 135 pass 136 elif elem is None: 137 elem = getattr(self, 'elem', None) 138 else: 139 raise XMLSchemaValueError("'elem' argument must be an Element instance, not %r." % elem) 140 141 if isinstance(error, XMLSchemaParseError): 142 error.validator = self 143 error.namespaces = getattr(self, 'namespaces', None) 144 error.elem = elem 145 error.source = getattr(self, 'source', None) 146 elif isinstance(error, Exception): 147 error = XMLSchemaParseError(self, unicode_type(error).strip('\'" '), elem) 148 elif isinstance(error, string_base_type): 149 error = XMLSchemaParseError(self, error, elem) 150 else: 151 raise XMLSchemaValueError("'error' argument must be an exception or a string, not %r." % error) 152 153 if self.validation == 'lax': 154 self.errors.append(error) 155 else: 156 raise error
157
158 - def _parse_xpath_default_namespace(self, elem):
159 """ 160 Parse XSD 1.1 xpathDefaultNamespace attribute for schema, alternative, assert, assertion 161 and selector declarations, checking if the value is conforming to the specification. In 162 case the attribute is missing or for wrong attribute values defaults to ''. 163 """ 164 try: 165 value = elem.attrib['xpathDefaultNamespace'] 166 except KeyError: 167 return '' 168 169 value = value.strip() 170 if value == '##local': 171 return '' 172 elif value == '##defaultNamespace': 173 return getattr(self, 'default_namespace') 174 elif value == '##targetNamespace': 175 return getattr(self, 'target_namespace') 176 elif len(value.split()) == 1: 177 return value 178 else: 179 admitted_values = ('##defaultNamespace', '##targetNamespace', '##local') 180 msg = "wrong value %r for 'xpathDefaultNamespace' attribute, can be (anyURI | %s)." 181 self.parse_error(msg % (value, ' | '.join(admitted_values)), elem) 182 return ''
183
184 185 -class XsdComponent(XsdValidator):
186 """ 187 Class for XSD components. See: https://www.w3.org/TR/xmlschema-ref/ 188 189 :param elem: ElementTree's node containing the definition. 190 :param schema: the XMLSchema object that owns the definition. 191 :param parent: the XSD parent, `None` means that is a global component that has the schema as parent. 192 :param name: name of the component, maybe overwritten by the parse of the `elem` argument. 193 194 :cvar qualified: for name matching, unqualified matching may be admitted only for elements and attributes. 195 :vartype qualified: bool 196 """ 197 _REGEX_SPACE = re.compile(r'\s') 198 _REGEX_SPACES = re.compile(r'\s+') 199 _admitted_tags = () 200 201 parent = None 202 name = None 203 qualified = True 204
205 - def __init__(self, elem, schema, parent=None, name=None):
206 super(XsdComponent, self).__init__(schema.validation) 207 if name is not None: 208 assert name and (name[0] == '{' or not schema.target_namespace), \ 209 "name=%r argument: must be a qualified name of the target namespace." % name 210 self.name = name 211 if parent is not None: 212 self.parent = parent 213 self.schema = schema 214 self.elem = elem
215
216 - def __setattr__(self, name, value):
217 if name == "elem": 218 if not is_etree_element(value): 219 raise XMLSchemaTypeError("%r attribute must be an Etree Element: %r" % (name, value)) 220 elif value.tag not in self._admitted_tags: 221 raise XMLSchemaValueError( 222 "wrong XSD element %r for %r, must be one of %r." % ( 223 local_name(value.tag), self, 224 [local_name(tag) for tag in self._admitted_tags] 225 ) 226 ) 227 super(XsdComponent, self).__setattr__(name, value) 228 self._parse() 229 return 230 elif name == "schema": 231 if hasattr(self, 'schema') and self.schema.target_namespace != value.target_namespace: 232 raise XMLSchemaValueError( 233 "cannot change 'schema' attribute of %r: the actual %r has a different " 234 "target namespace than %r." % (self, self.schema, value) 235 ) 236 super(XsdComponent, self).__setattr__(name, value)
237 238 @property
239 - def is_global(self):
240 """Is `True` if the instance is a global component, `False` if it's local.""" 241 return self.parent is None
242 243 @property
244 - def schema_elem(self):
245 """The reference element of the schema for the component instance.""" 246 return self.elem
247 248 @property
249 - def source(self):
250 """Property that references to schema source.""" 251 return self.schema.source
252 253 @property
254 - def target_namespace(self):
255 """Property that references to schema's targetNamespace.""" 256 return self.schema.target_namespace
257 258 @property
259 - def default_namespace(self):
260 """Property that references to schema's default namespaces.""" 261 return self.schema.namespaces.get('')
262 263 @property
264 - def namespaces(self):
265 """Property that references to schema's namespace mapping.""" 266 return self.schema.namespaces
267 268 @property
269 - def maps(self):
270 """Property that references to schema's global maps.""" 271 return self.schema.maps
272
273 - def __repr__(self):
274 if self.name is None: 275 return '<%s at %#x>' % (self.__class__.__name__, id(self)) 276 else: 277 return '%s(name=%r)' % (self.__class__.__name__, self.prefixed_name)
278
279 - def _parse(self):
280 del self.errors[:] 281 try: 282 if self.elem[0].tag == XSD_ANNOTATION: 283 self.annotation = XsdAnnotation(self.elem[0], self.schema, self) 284 else: 285 self.annotation = None 286 except (TypeError, IndexError): 287 self.annotation = None
288
289 - def _parse_component(self, elem, required=True, strict=True):
290 try: 291 return get_xsd_component(elem, required, strict) 292 except XMLSchemaValueError as err: 293 self.parse_error(err, elem)
294
295 - def _iterparse_components(self, elem, start=0):
296 try: 297 for obj in iter_xsd_components(elem, start): 298 yield obj 299 except XMLSchemaValueError as err: 300 self.parse_error(err, elem)
301
302 - def _parse_attribute(self, elem, name, values, default=None):
303 value = elem.get(name, default) 304 if value not in values: 305 self.parse_error("wrong value {} for {} attribute.".format(value, name)) 306 return default 307 return value
308
309 - def _parse_properties(self, *properties):
310 for name in properties: 311 try: 312 getattr(self, name) 313 except (ValueError, TypeError) as err: 314 self.parse_error(str(err))
315
316 - def _parse_target_namespace(self):
317 """ 318 XSD 1.1 targetNamespace attribute in elements and attributes declarations. 319 """ 320 self._target_namespace = self.elem.get('targetNamespace') 321 if self._target_namespace is not None: 322 if 'name' not in self.elem.attrib: 323 self.parse_error("attribute 'name' must be present when 'targetNamespace' attribute is provided") 324 if 'form' in self.elem.attrib: 325 self.parse_error("attribute 'form' must be absent when 'targetNamespace' attribute is provided") 326 if self.elem.attrib['targetNamespace'].strip() != self.schema.target_namespace: 327 parent = self.parent 328 if parent is None: 329 self.parse_error("a global attribute must has the same namespace as its parent schema") 330 elif not isinstance(parent, XsdType) or not parent.is_complex() or parent.derivation != 'restriction': 331 self.parse_error("a complexType restriction required for parent, found %r" % self.parent) 332 elif self.parent.base_type.name == XSD_ANY_TYPE: 333 pass 334 335 elif self.qualified: 336 self._target_namespace = self.schema.target_namespace 337 else: 338 self._target_namespace = ''
339 340 @property
341 - def local_name(self):
342 return local_name(self.name)
343 344 @property
345 - def qualified_name(self):
346 return get_qname(self.target_namespace, self.name)
347 348 @property
349 - def prefixed_name(self):
350 return qname_to_prefixed(self.name, self.namespaces)
351 352 @property
353 - def id(self):
354 """The ``'id'`` attribute of the component tag, ``None`` if missing.""" 355 return self.elem.get('id')
356 357 @property
358 - def validation_attempted(self):
359 return 'full' if self.built else 'partial'
360 361 @property
362 - def built(self):
363 raise NotImplementedError
364
365 - def is_matching(self, name, default_namespace=None):
366 """ 367 Returns `True` if the component name is matching the name provided as argument, `False` otherwise. 368 369 :param name: a local or fully-qualified name. 370 :param default_namespace: used if it's not None and not empty for completing the name \ 371 argument in case it's a local name. 372 """ 373 if not name: 374 return self.name == name 375 elif name[0] == '{': 376 return self.qualified_name == name 377 elif not default_namespace: 378 return self.name == name or not self.qualified and self.local_name == name 379 else: 380 qname = '{%s}%s' % (default_namespace, name) 381 return self.qualified_name == qname or not self.qualified and self.local_name == name
382
383 - def match(self, name, default_namespace=None):
384 """Returns the component if its name is matching the name provided as argument, `None` otherwise.""" 385 return self if self.is_matching(name, default_namespace) else None
386
387 - def get_global(self):
388 """Returns the global XSD component that contains the component instance.""" 389 if self.parent is None: 390 return self 391 component = self.parent 392 while component is not self: 393 if component.parent is None: 394 return component 395 component = component.parent
396
397 - def iter_components(self, xsd_classes=None):
398 """ 399 Creates an iterator for XSD subcomponents. 400 401 :param xsd_classes: provide a class or a tuple of classes to iterates over only a \ 402 specific classes of components. 403 """ 404 if xsd_classes is None or isinstance(self, xsd_classes): 405 yield self
406
407 - def iter_ancestors(self, xsd_classes=None):
408 """ 409 Creates an iterator for XSD ancestor components, schema excluded. Stops when the component 410 is global or if the ancestor is not an instance of the specified class/classes. 411 412 :param xsd_classes: provide a class or a tuple of classes to iterates over only a \ 413 specific classes of components. 414 """ 415 ancestor = self 416 while True: 417 ancestor = ancestor.parent 418 if ancestor is None: 419 break 420 elif xsd_classes is None or isinstance(ancestor, xsd_classes): 421 yield ancestor 422 else: 423 break
424
425 - def tostring(self, indent='', max_lines=None, spaces_for_tab=4):
426 """ 427 Returns the XML elements that declare or define the component as a string. 428 """ 429 if self.elem is None: 430 return str(None) # Incomplete component 431 return etree_tostring(self.schema_elem, self.namespaces, indent, max_lines, spaces_for_tab)
432
433 434 -class XsdAnnotation(XsdComponent):
435 """ 436 Class for XSD 'annotation' definitions. 437 438 <annotation 439 id = ID 440 {any attributes with non-schema namespace . . .}> 441 Content: (appinfo | documentation)* 442 </annotation> 443 444 <appinfo 445 source = anyURI 446 {any attributes with non-schema namespace . . .}> 447 Content: ({any})* 448 </appinfo> 449 450 <documentation 451 source = anyURI 452 xml:lang = language 453 {any attributes with non-schema namespace . . .}> 454 Content: ({any})* 455 </documentation> 456 """ 457 _admitted_tags = {XSD_ANNOTATION} 458 459 @property
460 - def built(self):
461 return True
462
463 - def _parse(self):
464 del self.errors[:] 465 self.appinfo = [] 466 self.documentation = [] 467 for child in self.elem: 468 if child.tag == XSD_APPINFO: 469 for key in child.attrib: 470 if key != 'source': 471 self.parse_error("wrong attribute %r for appinfo declaration." % key) 472 self.appinfo.append(child) 473 elif child.tag == XSD_DOCUMENTATION: 474 for key in child.attrib: 475 if key not in ['source', XML_LANG]: 476 self.parse_error("wrong attribute %r for documentation declaration." % key) 477 self.documentation.append(child)
478
479 480 -class XsdType(XsdComponent):
481 abstract = False 482 base_type = None 483 derivation = None 484 redefine = None 485 _final = None 486 487 @property
488 - def final(self):
489 return self.schema.final_default if self._final is None else self._final
490 491 @property
492 - def built(self):
493 raise NotImplementedError
494 495 @staticmethod
496 - def is_simple():
497 raise NotImplementedError
498 499 @staticmethod
500 - def is_complex():
501 raise NotImplementedError
502 503 @staticmethod
504 - def is_atomic():
505 return None
506
507 - def is_empty(self):
508 raise NotImplementedError
509
510 - def is_emptiable(self):
511 raise NotImplementedError
512
513 - def has_simple_content(self):
514 raise NotImplementedError
515
516 - def has_mixed_content(self):
517 raise NotImplementedError
518
519 - def is_element_only(self):
520 raise NotImplementedError
521 522 @property
523 - def content_type_label(self):
524 if self.is_empty(): 525 return 'empty' 526 elif self.has_simple_content(): 527 return 'simple' 528 elif self.is_element_only(): 529 return 'element-only' 530 elif self.has_mixed_content(): 531 return 'mixed' 532 else: 533 return 'unknown'
534
535 - def is_derived(self, other, derivation=None):
536 raise NotImplementedError
537
538 - def is_key(self):
539 return self.name == XSD_ID or self.is_derived(self.maps.types[XSD_ID])
540
541 542 -class ValidationMixin(object):
543 """ 544 Mixin for implementing XML data validators/decoders. A derived class must implement the 545 methods `iter_decode` and `iter_encode`. 546 """
547 - def validate(self, source, use_defaults=True, namespaces=None):
548 """ 549 Validates an XML data against the XSD schema/component instance. 550 551 :param source: the source of XML data. For a schema can be a path \ 552 to a file or an URI of a resource or an opened file-like object or an Element Tree \ 553 instance or a string containing XML data. For other XSD components can be a string \ 554 for an attribute or a simple type validators, or an ElementTree's Element otherwise. 555 :param use_defaults: indicates whether to use default values for filling missing data. 556 :param namespaces: is an optional mapping from namespace prefix to URI. 557 :raises: :exc:`XMLSchemaValidationError` if XML *data* instance is not a valid. 558 """ 559 for error in self.iter_errors(source, use_defaults=use_defaults, namespaces=namespaces): 560 raise error
561
562 - def is_valid(self, source, use_defaults=True):
563 """ 564 Like :meth:`validate` except that do not raises an exception but returns ``True`` if 565 the XML document is valid, ``False`` if it's invalid. 566 567 :param source: the source of XML data. For a schema can be a path \ 568 to a file or an URI of a resource or an opened file-like object or an Element Tree \ 569 instance or a string containing XML data. For other XSD components can be a string \ 570 for an attribute or a simple type validators, or an ElementTree's Element otherwise. 571 :param use_defaults: indicates whether to use default values for filling missing data. 572 """ 573 error = next(self.iter_errors(source, use_defaults=use_defaults), None) 574 return error is None
575
576 - def iter_errors(self, source, path=None, use_defaults=True, namespaces=None):
577 """ 578 Creates an iterator for the errors generated by the validation of an XML data 579 against the XSD schema/component instance. 580 581 :param source: the source of XML data. For a schema can be a path \ 582 to a file or an URI of a resource or an opened file-like object or an Element Tree \ 583 instance or a string containing XML data. For other XSD components can be a string \ 584 for an attribute or a simple type validators, or an ElementTree's Element otherwise. 585 :param path: is an optional XPath expression that defines the parts of the document \ 586 that have to be validated. The XPath expression considers the schema as the root element \ 587 with global elements as its children. 588 :param use_defaults: Use schema's default values for filling missing data. 589 :param namespaces: is an optional mapping from namespace prefix to URI. 590 """ 591 for result in self.iter_decode(source, path=path, use_defaults=use_defaults, namespaces=namespaces): 592 if isinstance(result, XMLSchemaValidationError): 593 yield result 594 else: 595 del result
596
597 - def decode(self, source, *args, **kwargs):
598 """ 599 Decodes XML data using the XSD schema/component. 600 601 :param source: the source of XML data. For a schema can be a path to a file or an \ 602 URI of a resource or an opened file-like object or an Element Tree \ 603 instance or a string containing XML data. For other XSD components can be a string \ 604 for an attribute or a simple type validators, or an ElementTree's Element otherwise. 605 :param args: arguments that maybe passed to :func:`XMLSchema.iter_decode`. 606 :param kwargs: keyword arguments from the ones included in the optional \ 607 arguments of the :func:`XMLSchema.iter_decode`. 608 :return: a dictionary like object if the XSD component is an element, a \ 609 group or a complex type; a list if the XSD component is an attribute group; \ 610 a simple data type object otherwise. If *validation* argument is 'lax' a 2-items \ 611 tuple is returned, where the first item is the decoded object and the second item \ 612 is a list containing the errors. 613 :raises: :exc:`XMLSchemaValidationError` if the object is not decodable by \ 614 the XSD component, or also if it's invalid when ``validation='strict'`` is provided. 615 """ 616 validation = kwargs.pop('validation', 'strict') 617 if validation not in XSD_VALIDATION_MODES: 618 raise XMLSchemaValueError("validation argument can be 'strict', 'lax' or 'skip': %r" % validation) 619 errors = [] 620 621 for result in self.iter_decode(source, validation=validation, *args, **kwargs): 622 if isinstance(result, XMLSchemaValidationError): 623 if validation == 'strict': 624 raise result 625 elif validation == 'lax': 626 errors.append(result) 627 elif validation == 'lax': 628 return result, errors 629 else: 630 return result
631 to_dict = decode 632
633 - def encode(self, obj, *args, **kwargs):
634 """ 635 Encodes data to XML using the XSD schema/component. 636 637 :param obj: the data to be encoded to XML. 638 :param args: arguments that maybe passed to :func:`XMLSchema.iter_encode`. 639 :param kwargs: keyword arguments from the ones included in the optional \ 640 arguments of the :func:`XMLSchema.iter_encode`. 641 :return: An element tree's Element if the original data is a structured data or \ 642 a string if it's simple type datum. If *validation* argument is 'lax' a 2-items \ 643 tuple is returned, where the first item is the encoded object and the second item \ 644 is a list containing the errors. 645 :raises: :exc:`XMLSchemaValidationError` if the object is not encodable by \ 646 the XSD component, or also if it's invalid when ``validation='strict'`` is provided. 647 """ 648 validation = kwargs.pop('validation', 'strict') 649 if validation not in XSD_VALIDATION_MODES: 650 raise XMLSchemaValueError("validation argument can be 'strict', 'lax' or 'skip': %r" % validation) 651 errors = [] 652 653 for result in self.iter_encode(obj, validation=validation, *args, **kwargs): 654 if isinstance(result, XMLSchemaValidationError): 655 if validation == 'strict': 656 raise result 657 elif validation == 'lax': 658 errors.append(result) 659 elif validation == 'lax': 660 return result, errors 661 else: 662 return result
663 to_etree = encode 664
665 - def iter_decode(self, source, validation='lax', *args, **kwargs):
666 """ 667 Creates an iterator for decoding an XML source to a Python object. 668 669 :param source: the XML data source. The argument type depends by implementation. 670 :param validation: the validation mode. Can be 'lax', 'strict' or 'skip. 671 :param args: additional arguments for the decoder API. 672 :param kwargs: keyword arguments for the decoder API. 673 :return: Yields a decoded object, eventually preceded by a sequence of \ 674 validation or decoding errors. 675 """ 676 raise NotImplementedError
677
678 - def iter_encode(self, obj, validation='lax', *args, **kwargs):
679 """ 680 Creates an iterator for Encode data to an Element. 681 682 :param obj: The data that has to be encoded. 683 :param validation: The validation mode. Can be 'lax', 'strict' or 'skip'. 684 :param args: additional arguments for the encoder API. 685 :param kwargs: keyword arguments for the encoder API. 686 :return: Yields an Element, eventually preceded by a sequence of validation \ 687 or encoding errors. 688 """ 689 raise NotImplementedError
690
691 - def validation_error(self, validation, error, obj=None, source=None, namespaces=None, **_kwargs):
692 """ 693 Helper method for generating and updating validation errors. Incompatible with 'skip' 694 validation mode. Il validation mode is 'lax' returns the error, otherwise raises the error. 695 696 :param validation: an error-compatible validation mode: can be 'lax' or 'strict'. 697 :param error: an error instance or the detailed reason of failed validation. 698 :param obj: the instance related to the error. 699 :param source: the XML resource related to the validation process. 700 :param namespaces: is an optional mapping from namespace prefix to URI. 701 :param _kwargs: keyword arguments of the validation process that are not used. 702 """ 703 if not isinstance(error, XMLSchemaValidationError): 704 error = XMLSchemaValidationError(self, obj, error, source, namespaces) 705 else: 706 if error.obj is None and obj is not None: 707 error.obj = obj 708 if error.namespaces is None and namespaces is not None: 709 error.namespaces = namespaces 710 if error.elem is None and is_etree_element(obj): 711 error.elem = obj 712 if error.source is None and source is not None: 713 error.source = source 714 715 if validation == 'lax': 716 return error 717 elif validation == 'strict': 718 raise error 719 elif validation == 'skip': 720 raise XMLSchemaValueError("validation mode 'skip' incompatible with error generation.") 721 else: 722 raise XMLSchemaValueError("unknown validation mode %r" % validation)
723
724 - def decode_error(self, validation, obj, decoder, reason=None, source=None, namespaces=None, **_kwargs):
725 """ 726 Helper method for generating decode errors. Incompatible with 'skip' validation mode. 727 Il validation mode is 'lax' returns the error, otherwise raises the error. 728 729 :param validation: an error-compatible validation mode: can be 'lax' or 'strict'. 730 :param obj: the not validated XML data. 731 :param decoder: the XML data decoder. 732 :param reason: the detailed reason of failed validation. 733 :param source: the XML resource that contains the error. 734 :param namespaces: is an optional mapping from namespace prefix to URI. 735 :param _kwargs: keyword arguments of the validation process that are not used. 736 """ 737 error = XMLSchemaDecodeError(self, obj, decoder, reason, source, namespaces) 738 if validation == 'lax': 739 return error 740 elif validation == 'strict': 741 raise error 742 elif validation == 'skip': 743 raise XMLSchemaValueError("validation mode 'skip' incompatible with error generation.") 744 else: 745 raise XMLSchemaValueError("unknown validation mode %r" % validation)
746
747 - def encode_error(self, validation, obj, encoder, reason=None, source=None, namespaces=None, **_kwargs):
748 """ 749 Helper method for generating encode errors. Incompatible with 'skip' validation mode. 750 Il validation mode is 'lax' returns the error, otherwise raises the error. 751 752 :param validation: an error-compatible validation mode: can be 'lax' or 'strict'. 753 :param obj: the not validated XML data. 754 :param encoder: the XML encoder. 755 :param reason: the detailed reason of failed validation. 756 :param source: the XML resource that contains the error. 757 :param namespaces: is an optional mapping from namespace prefix to URI. 758 :param _kwargs: keyword arguments of the validation process that are not used. 759 """ 760 error = XMLSchemaEncodeError(self, obj, encoder, reason, source, namespaces) 761 if validation == 'lax': 762 return error 763 elif validation == 'strict': 764 raise error 765 elif validation == 'skip': 766 raise XMLSchemaValueError("validation mode 'skip' incompatible with error generation.") 767 else: 768 raise XMLSchemaValueError("unknown validation mode %r" % validation)
769
770 771 -class ParticleMixin(object):
772 """ 773 Mixin for objects related to XSD Particle Schema Components: 774 775 https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/structures.html#p 776 https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/structures.html#t 777 """ 778 min_occurs = 1 779 max_occurs = 1 780 781 @property
782 - def occurs(self):
783 return [self.min_occurs, self.max_occurs]
784
785 - def is_emptiable(self):
786 return self.min_occurs == 0
787
788 - def is_empty(self):
789 return self.max_occurs == 0
790
791 - def is_single(self):
792 return self.max_occurs == 1
793
794 - def is_ambiguous(self):
795 return self.min_occurs != self.max_occurs
796
797 - def is_univocal(self):
798 return self.min_occurs == self.max_occurs
799
800 - def is_missing(self, occurs):
801 return not self.is_emptiable() if occurs == 0 else self.min_occurs > occurs
802
803 - def is_over(self, occurs):
804 return self.max_occurs is not None and self.max_occurs <= occurs
805
806 - def has_occurs_restriction(self, other):
807 if self.min_occurs == self.max_occurs == 0: 808 return True 809 elif self.min_occurs < other.min_occurs: 810 return False 811 elif other.max_occurs is None: 812 return True 813 elif self.max_occurs is None: 814 return False 815 else: 816 return self.max_occurs <= other.max_occurs
817 818 ### 819 # Methods used by XSD components
820 - def parse_error(self, *args, **kwargs):
821 """Helper method overridden by XsdValidator.parse_error() in XSD components.""" 822 raise XMLSchemaParseError(*args)
823
824 - def _parse_particle(self, elem):
825 if 'minOccurs' in elem.attrib: 826 try: 827 min_occurs = int(elem.attrib['minOccurs']) 828 except (TypeError, ValueError): 829 self.parse_error("minOccurs value is not an integer value") 830 else: 831 if min_occurs < 0: 832 self.parse_error("minOccurs value must be a non negative integer") 833 else: 834 self.min_occurs = min_occurs 835 836 max_occurs = elem.get('maxOccurs') 837 if max_occurs is None: 838 if self.min_occurs > 1: 839 self.parse_error("minOccurs must be lesser or equal than maxOccurs") 840 elif max_occurs == 'unbounded': 841 self.max_occurs = None 842 else: 843 try: 844 max_occurs = int(max_occurs) 845 except ValueError: 846 self.parse_error("maxOccurs value must be a non negative integer or 'unbounded'") 847 else: 848 if self.min_occurs > max_occurs: 849 self.parse_error("maxOccurs must be 'unbounded' or greater than minOccurs") 850 else: 851 self.max_occurs = max_occurs
852