Package xmlschema_acue ::
Package validators ::
Module xsdbase
1
2
3
4
5
6
7
8
9
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 """
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
53
54 return unicode(self).encode("utf-8")
55
58
59 if PY3:
60 __str__ = __unicode__
61
62 @property
64 """
65 Property that is ``True`` if schema validator has been fully parsed and built, ``False`` otherwise.
66 """
67 raise NotImplementedError
68
69 @property
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
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
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
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
119
120 __copy__ = copy
121
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
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
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):
215
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
240 """Is `True` if the instance is a global component, `False` if it's local."""
241 return self.parent is None
242
243 @property
245 """The reference element of the schema for the component instance."""
246 return self.elem
247
248 @property
250 """Property that references to schema source."""
251 return self.schema.source
252
253 @property
257
258 @property
260 """Property that references to schema's default namespaces."""
261 return self.schema.namespaces.get('')
262
263 @property
265 """Property that references to schema's namespace mapping."""
266 return self.schema.namespaces
267
268 @property
270 """Property that references to schema's global maps."""
271 return self.schema.maps
272
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
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
294
301
308
310 for name in properties:
311 try:
312 getattr(self, name)
313 except (ValueError, TypeError) as err:
314 self.parse_error(str(err))
315
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
343
344 @property
347
348 @property
351
352 @property
354 """The ``'id'`` attribute of the component tag, ``None`` if missing."""
355 return self.elem.get('id')
356
357 @property
359 return 'full' if self.built else 'partial'
360
361 @property
363 raise NotImplementedError
364
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
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
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
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)
431 return etree_tostring(self.schema_elem, self.namespaces, indent, max_lines, spaces_for_tab)
432
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
462
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
481 abstract = False
482 base_type = None
483 derivation = None
484 redefine = None
485 _final = None
486
487 @property
490
491 @property
493 raise NotImplementedError
494
495 @staticmethod
497 raise NotImplementedError
498
499 @staticmethod
501 raise NotImplementedError
502
503 @staticmethod
506
508 raise NotImplementedError
509
511 raise NotImplementedError
512
514 raise NotImplementedError
515
517 raise NotImplementedError
518
520 raise NotImplementedError
521
522 @property
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
536 raise NotImplementedError
537
540
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
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
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