Package xmlschema_acue ::
Package validators ::
Module elements
1
2
3
4
5
6
7
8
9
10
11 """
12 This module contains classes for XML Schema elements, complex types and model groups.
13 """
14 from __future__ import unicode_literals
15 from decimal import Decimal
16 from elementpath import XPath2Parser, ElementPathSyntaxError, XPathContext
17 from elementpath.xpath_helpers import boolean_value
18 from elementpath.datatypes import AbstractDateTime, Duration
19
20 from xmlschema_acue.exceptions import XMLSchemaAttributeError
21 from xmlschema_acue.qnames import XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE, XSD_ATTRIBUTE_GROUP, \
22 XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE, XSD_ALTERNATIVE, XSD_ELEMENT, XSD_ANY_TYPE, XSD_UNIQUE, \
23 XSD_KEY, XSD_KEYREF, XSI_NIL, XSI_TYPE, XSD_ID
24 from xmlschema_acue.helpers import get_qname, get_xml_bool_attribute, get_xsd_derivation_attribute, \
25 get_xsd_form_attribute, ParticleCounter
26 from xmlschema_acue.etree import etree_element
27 from xmlschema_acue.converters import ElementData, raw_xml_encode, XMLSchemaConverter
28 from xmlschema_acue.xpath import ElementPathMixin
29
30 from xmlschema_acue.validators.exceptions import XMLSchemaValidationError
31 from xmlschema_acue.validators.xsdbase import XsdComponent, XsdType, ValidationMixin, ParticleMixin
32 from xmlschema_acue.validators.identities import XsdUnique, XsdKey, XsdKeyref
33 from xmlschema_acue.validators.wildcards import XsdAnyElement
34
35
36 XSD_MODEL_GROUP_TAGS = {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}
37 XSD_ATTRIBUTE_GROUP_ELEMENT = etree_element(XSD_ATTRIBUTE_GROUP)
38
39
40 -class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin):
41 """
42 Class for XSD 1.0 'element' declarations.
43
44 <element
45 abstract = boolean : false
46 block = (#all | List of (extension | restriction | substitution))
47 default = string
48 final = (#all | List of (extension | restriction))
49 fixed = string
50 form = (qualified | unqualified)
51 id = ID
52 maxOccurs = (nonNegativeInteger | unbounded) : 1
53 minOccurs = nonNegativeInteger : 1
54 name = NCName
55 nillable = boolean : false
56 ref = QName
57 substitutionGroup = QName
58 type = QName
59 {any attributes with non-schema namespace . . .}>
60 Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*))
61 </element>
62 """
63 _admitted_tags = {XSD_ELEMENT}
64 qualified = False
65 _ref = None
66 _abstract = False
67 _block = None
68 _final = None
69 _substitution_group = None
70
71 - def __init__(self, elem, schema, parent, name=None):
78
80 if self.ref is None:
81 return '%s(name=%r, occurs=%r)' % (self.__class__.__name__, self.prefixed_name, self.occurs)
82 else:
83 return '%s(ref=%r, occurs=%r)' % (self.__class__.__name__, self.prefixed_name, self.occurs)
84
95
100
102 XsdComponent._parse(self)
103 self._parse_attributes()
104 index = self._parse_type()
105 self._parse_identity_constraints(index)
106 if self.parent is None:
107 self._parse_substitution_group()
108
110 elem = self.elem
111 attrib = elem.attrib
112 self._parse_particle(elem)
113
114 try:
115 self.qualified = (self.form or self.schema.element_form_default) == 'qualified'
116 except ValueError as err:
117 self.parse_error(err)
118
119 name = elem.get('name')
120 if name is not None:
121 if self.parent is None or self.qualified:
122 self.name = get_qname(self.target_namespace, attrib['name'])
123 else:
124 self.name = attrib['name']
125 elif self.parent is None:
126 self.parse_error("missing 'name' in a global element declaration")
127 self.name = elem.get('ref', 'nameless_%s' % str(id(self)))
128 elif 'ref' not in attrib:
129 self.parse_error("missing both 'name' and 'ref' attributes")
130 self.name = elem.get('nameless_%s' % str(id(self)))
131 else:
132 try:
133 element_name = self.schema.resolve_qname(attrib['ref'])
134 except ValueError as err:
135 self.parse_error(err)
136 self.type = self.maps.types[XSD_ANY_TYPE]
137 self.name = elem.get('nameless_%s' % str(id(self)))
138 else:
139 if not element_name:
140 self.parse_error("empty 'ref' attribute")
141 self.type = self.maps.types[XSD_ANY_TYPE]
142 self.name = elem.get('nameless_%s' % str(id(self)))
143 else:
144 try:
145 xsd_element = self.maps.lookup_element(element_name)
146 except KeyError:
147 self.parse_error('unknown element %r' % element_name)
148 self.name = element_name
149 self.type = self.maps.types[XSD_ANY_TYPE]
150 else:
151 self._ref = xsd_element
152 self.name = xsd_element.name
153 self.type = xsd_element.type
154 self.qualified = xsd_element.qualified
155
156 for attr_name in ('name', 'type', 'nillable', 'default', 'fixed', 'form',
157 'block', 'abstract', 'final', 'substitutionGroup'):
158 if attr_name in attrib:
159 self.parse_error("attribute %r is not allowed when element reference is used." % attr_name)
160 return
161
162 if 'default' in attrib and 'fixed' in attrib:
163 self.parse_error("'default' and 'fixed' attributes are mutually exclusive.")
164
165 if 'abstract' in elem.attrib:
166 try:
167 self._abstract = get_xml_bool_attribute(elem, 'abstract')
168 except ValueError as err:
169 self.parse_error(err, elem)
170 else:
171 if self.parent is not None:
172 self.parse_error("local scope elements cannot have abstract attribute")
173
174 if 'block' in elem.attrib:
175 try:
176 self._block = get_xsd_derivation_attribute(
177 elem, 'block', ('extension', 'restriction', 'substitution')
178 )
179 except ValueError as err:
180 self.parse_error(err, elem)
181
182 if self.parent is None:
183 self._parse_properties('nillable')
184
185 if 'final' in elem.attrib:
186 try:
187 self._final = get_xsd_derivation_attribute(elem, 'final', ('extension', 'restriction'))
188 except ValueError as err:
189 self.parse_error(err, elem)
190
191 for attr_name in ('ref', 'form', 'minOccurs', 'maxOccurs'):
192 if attr_name in attrib:
193 self.parse_error("attribute %r not allowed in a global element declaration" % attr_name)
194 else:
195 self._parse_properties('form', 'nillable')
196
197 for attr_name in ('final', 'substitutionGroup'):
198 if attr_name in attrib:
199 self.parse_error("attribute %r not allowed in a local element declaration" % attr_name)
200
202 attrib = self.elem.attrib
203 if self.ref:
204 if self._parse_component(self.elem, required=False, strict=False) is not None:
205 self.parse_error("element reference declaration can't has children.")
206 elif 'type' in attrib:
207 try:
208 self.type = self.maps.lookup_type(self.schema.resolve_qname(attrib['type']))
209 except KeyError:
210 self.parse_error('unknown type %r' % attrib['type'])
211 self.type = self.maps.types[XSD_ANY_TYPE]
212 except ValueError as err:
213 self.parse_error(err)
214 self.type = self.maps.types[XSD_ANY_TYPE]
215 finally:
216 child = self._parse_component(self.elem, required=False, strict=False)
217 if child is not None and child.tag in (XSD_COMPLEX_TYPE, XSD_SIMPLE_TYPE):
218 msg = "the attribute 'type' and the <%s> local declaration are mutually exclusive"
219 self.parse_error(msg % child.tag.split('}')[-1])
220 else:
221 child = self._parse_component(self.elem, required=False, strict=False)
222 if child is not None:
223 if child.tag == XSD_COMPLEX_TYPE:
224 self.type = self.schema.BUILDERS.complex_type_class(child, self.schema, self)
225 elif child.tag == XSD_SIMPLE_TYPE:
226 self.type = self.schema.BUILDERS.simple_type_factory(child, self.schema, self)
227 else:
228 self.type = self.maps.lookup_type(XSD_ANY_TYPE)
229 return 0
230
231
232 if 'default' in attrib and not self.type.is_valid(attrib['default']):
233 msg = "'default' value %r is not compatible with the type of the element"
234 self.parse_error(msg % attrib['default'])
235 elif 'fixed' in attrib and not self.type.is_valid(attrib['fixed']):
236 msg = "'fixed' value %r is not compatible with the type of the element"
237 self.parse_error(msg % attrib['fixed'])
238
239 return 1
240 else:
241 self.type = self.maps.lookup_type(XSD_ANY_TYPE)
242 return 0
243
244
245 if 'default' in attrib:
246 if not self.type.is_valid(attrib['default']):
247 msg = "'default' value {!r} is not compatible with the type {!r}"
248 self.parse_error(msg.format(attrib['default'], self.type))
249 elif self.schema.XSD_VERSION == '1.0' and (
250 self.type.name == XSD_ID or self.type.is_derived(self.schema.meta_schema.types['ID'])):
251 self.parse_error("'xs:ID' or a type derived from 'xs:ID' cannot has a 'default'")
252 elif 'fixed' in attrib:
253 if not self.type.is_valid(attrib['fixed']):
254 msg = "'fixed' value {!r} is not compatible with the type {!r}"
255 self.parse_error(msg.format(attrib['fixed'], self.type))
256 elif self.schema.XSD_VERSION == '1.0' and (
257 self.type.name == XSD_ID or self.type.is_derived(self.schema.meta_schema.types['ID'])):
258 self.parse_error("'xs:ID' or a type derived from 'xs:ID' cannot has a 'default'")
259
260 return 0
261
263 self.constraints = {}
264 for child in self._iterparse_components(self.elem, start=index):
265 if child.tag == XSD_UNIQUE:
266 constraint = XsdUnique(child, self.schema, self)
267 elif child.tag == XSD_KEY:
268 constraint = XsdKey(child, self.schema, self)
269 elif child.tag == XSD_KEYREF:
270 constraint = XsdKeyref(child, self.schema, self)
271 else:
272 continue
273
274 try:
275 if child != self.maps.constraints[constraint.name]:
276 self.parse_error("duplicated identity constraint %r:" % constraint.name, child)
277 except KeyError:
278 self.maps.constraints[constraint.name] = constraint
279 finally:
280 self.constraints[constraint.name] = constraint
281
333
334 @property
337
338 @property
344
345
346 @property
348 return self.elem.get('ref')
349
350
351 @property
354
355 @property
358
359 @property
362
363 @property
366
367 @property
370
371 @property
374
375 @property
378
379 @property
384
389
392
393 - def get_path(self, ancestor=None, reverse=False):
394 """
395 Returns the XPath expression of the element. The path is relative to the schema instance
396 in which the element is contained or is relative to a specific ancestor passed as argument.
397 In the latter case returns `None` if the argument is not an ancestor.
398
399 :param ancestor: optional XSD component of the same schema, that may be an ancestor of the element.
400 :param reverse: if set to `True` returns the reverse path, from the element to ancestor.
401 """
402 path = []
403 xsd_component = self
404 while xsd_component is not None:
405 if xsd_component is ancestor:
406 return '/'.join(reversed(path)) or '.'
407 elif hasattr(xsd_component, 'tag'):
408 path.append('..' if reverse else xsd_component.name)
409 xsd_component = xsd_component.parent
410 else:
411 if ancestor is None:
412 return '/'.join(reversed(path)) or '.'
413
415 if xsd_classes is None:
416 yield self
417 for obj in self.constraints.values():
418 yield obj
419 else:
420 if isinstance(self, xsd_classes):
421 yield self
422 for obj in self.constraints.values():
423 if isinstance(obj, xsd_classes):
424 yield obj
425
426 if self.ref is None and self.type.parent is not None:
427 for obj in self.type.iter_components(xsd_classes):
428 yield obj
429
431 for xsd_element in self.maps.substitution_groups.get(self.name, ()):
432 yield xsd_element
433 for e in xsd_element.iter_substitutes():
434 yield e
435
436 - def iter_decode(self, elem, validation='lax', converter=None, **kwargs):
437 """
438 Creates an iterator for decoding an Element instance.
439
440 :param elem: the Element that has to be decoded.
441 :param validation: the validation mode, can be 'lax', 'strict' or 'skip.
442 :param converter: an :class:`XMLSchemaConverter` subclass or instance.
443 :param kwargs: keyword arguments for the decoding process.
444 :return: yields a decoded object, eventually preceded by a sequence of \
445 validation or decoding errors.
446 """
447 if not isinstance(converter, XMLSchemaConverter):
448 converter = self.schema.get_converter(converter, **kwargs)
449 level = kwargs.pop('level', 0)
450 use_defaults = kwargs.get('use_defaults', False)
451 value = content = attributes = None
452
453
454 if XSI_TYPE not in elem.attrib:
455 xsd_type = self.get_type(elem)
456 else:
457 xsi_type = elem.attrib[XSI_TYPE]
458 try:
459 xsd_type = self.maps.lookup_type(converter.unmap_qname(xsi_type))
460 except KeyError:
461 yield self.validation_error(validation, "unknown type %r" % xsi_type, elem, **kwargs)
462 xsd_type = self.get_type(elem)
463
464
465 attribute_group = getattr(xsd_type, 'attributes', self.attributes)
466 for result in attribute_group.iter_decode(elem.attrib, validation, **kwargs):
467 if isinstance(result, XMLSchemaValidationError):
468 yield self.validation_error(validation, result, elem, **kwargs)
469 else:
470 attributes = result
471
472
473 if validation != 'skip' and XSI_NIL in elem.attrib:
474 if not self.nillable:
475 yield self.validation_error(validation, "element is not nillable.", elem, **kwargs)
476 try:
477 if get_xml_bool_attribute(elem, XSI_NIL):
478 if elem.text is not None:
479 reason = "xsi:nil='true' but the element is not empty."
480 yield self.validation_error(validation, reason, elem, **kwargs)
481 else:
482 element_data = ElementData(elem.tag, None, None, attributes)
483 yield converter.element_decode(element_data, self, level)
484 return
485 except TypeError:
486 reason = "xsi:nil attribute must has a boolean value."
487 yield self.validation_error(validation, reason, elem, **kwargs)
488
489 if xsd_type.is_simple():
490 if len(elem) and validation != 'skip':
491 reason = "a simpleType element can't has child elements."
492 yield self.validation_error(validation, reason, elem, **kwargs)
493
494 text = elem.text
495 if self.fixed is not None:
496 if text is None:
497 text = self.fixed
498 elif text != self.fixed:
499 reason = "must has the fixed value %r." % self.fixed
500 yield self.validation_error(validation, reason, elem, **kwargs)
501 elif not text and use_defaults and self.default is not None:
502 text = self.default
503
504 if text is None:
505 for result in xsd_type.iter_decode('', validation, **kwargs):
506 if isinstance(result, XMLSchemaValidationError):
507 yield self.validation_error(validation, result, elem, **kwargs)
508 else:
509 for result in xsd_type.iter_decode(text, validation, **kwargs):
510 if isinstance(result, XMLSchemaValidationError):
511 yield self.validation_error(validation, result, elem, **kwargs)
512 else:
513 value = result
514
515 elif xsd_type.has_simple_content():
516 if len(elem) and validation != 'skip':
517 reason = "a simple content element can't has child elements."
518 yield self.validation_error(validation, reason, elem, **kwargs)
519
520 if elem.text is not None:
521 text = elem.text or self.default if use_defaults else elem.text
522 for result in xsd_type.content_type.iter_decode(text, validation, **kwargs):
523 if isinstance(result, XMLSchemaValidationError):
524 yield self.validation_error(validation, result, elem, **kwargs)
525 else:
526 value = result
527 else:
528 for result in xsd_type.content_type.iter_decode(elem, validation, converter, level=level + 1, **kwargs):
529 if isinstance(result, XMLSchemaValidationError):
530 yield self.validation_error(validation, result, elem, **kwargs)
531 else:
532 content = result
533
534 if isinstance(value, Decimal):
535 try:
536 value = kwargs['decimal_type'](value)
537 except (KeyError, TypeError):
538 pass
539 elif isinstance(value, (AbstractDateTime, Duration)):
540 try:
541 if kwargs['datetime_types'] is not True:
542 value = elem.text
543 except KeyError:
544 value = elem.text
545
546 element_data = ElementData(elem.tag, value, content, attributes)
547 yield converter.element_decode(element_data, self, level)
548 if content is not None:
549 del content
550
551 if validation != 'skip':
552 for constraint in self.constraints.values():
553 for error in constraint(elem):
554 yield self.validation_error(validation, error, elem, **kwargs)
555
556 - def iter_encode(self, obj, validation='lax', converter=None, **kwargs):
557 """
558 Creates an iterator for encoding data to an Element.
559
560 :param obj: the data that has to be encoded.
561 :param validation: the validation mode: can be 'lax', 'strict' or 'skip'.
562 :param converter: an :class:`XMLSchemaConverter` subclass or instance.
563 :param kwargs: keyword arguments for the encoding process.
564 :return: yields an Element, eventually preceded by a sequence of \
565 validation or encoding errors.
566 """
567 if not isinstance(converter, XMLSchemaConverter):
568 converter = self.schema.get_converter(converter, **kwargs)
569 level = kwargs.pop('level', 0)
570 element_data = converter.element_encode(obj, self, level)
571
572 errors = []
573 tag = element_data.tag
574 text = None
575 children = element_data.content
576 attributes = ()
577
578 if element_data.attributes and XSI_TYPE in element_data.attributes:
579 xsi_type = element_data.attributes[XSI_TYPE]
580 try:
581 xsd_type = self.maps.lookup_type(converter.unmap_qname(xsi_type))
582 except KeyError:
583 errors.append("unknown type %r" % xsi_type)
584 xsd_type = self.get_type(element_data)
585 else:
586 xsd_type = self.get_type(element_data)
587
588 attribute_group = getattr(xsd_type, 'attributes', self.attributes)
589 for result in attribute_group.iter_encode(element_data.attributes, validation, **kwargs):
590 if isinstance(result, XMLSchemaValidationError):
591 errors.append(result)
592 else:
593 attributes = result
594
595 if validation != 'skip' and XSI_NIL in element_data.attributes:
596 if not self.nillable:
597 errors.append("element is not nillable.")
598 xsi_nil = element_data.attributes[XSI_NIL]
599 if xsi_nil.strip() not in ('0', '1', 'true', 'false'):
600 errors.append("xsi:nil attribute must has a boolean value.")
601 if element_data.text is not None:
602 errors.append("xsi:nil='true' but the element is not empty.")
603 else:
604 elem = converter.etree_element(element_data.tag, attrib=attributes, level=level)
605 for e in errors:
606 yield self.validation_error(validation, e, elem, **kwargs)
607 yield elem
608 return
609
610 if xsd_type.is_simple():
611 if element_data.content:
612 errors.append("a simpleType element can't has child elements.")
613
614 if element_data.text is None:
615 pass
616 else:
617 for result in xsd_type.iter_encode(element_data.text, validation, **kwargs):
618 if isinstance(result, XMLSchemaValidationError):
619 errors.append(result)
620 else:
621 text = result
622
623 elif xsd_type.has_simple_content():
624 if element_data.text is not None:
625 for result in xsd_type.content_type.iter_encode(element_data.text, validation, **kwargs):
626 if isinstance(result, XMLSchemaValidationError):
627 errors.append(result)
628 else:
629 text = result
630 else:
631 for result in xsd_type.content_type.iter_encode(
632 element_data, validation, converter, level=level+1, **kwargs):
633 if isinstance(result, XMLSchemaValidationError):
634 errors.append(result)
635 elif result:
636 text, children = result
637
638 elem = converter.etree_element(tag, text, children, attributes, level)
639
640 if validation != 'skip' and errors:
641 for e in errors:
642 yield self.validation_error(validation, e, elem, **kwargs)
643 yield elem
644 del element_data
645
711
725
728 """
729 Class for XSD 1.1 'element' declarations.
730
731 <element
732 abstract = boolean : false
733 block = (#all | List of (extension | restriction | substitution))
734 default = string
735 final = (#all | List of (extension | restriction))
736 fixed = string
737 form = (qualified | unqualified)
738 id = ID
739 maxOccurs = (nonNegativeInteger | unbounded) : 1
740 minOccurs = nonNegativeInteger : 1
741 name = NCName
742 nillable = boolean : false
743 ref = QName
744 substitutionGroup = List of QName
745 targetNamespace = anyURI
746 type = QName
747 {any attributes with non-schema namespace . . .}>
748 Content: (annotation?, ((simpleType | complexType)?, alternative*, (unique | key | keyref)*))
749 </element>
750 """
752 XsdComponent._parse(self)
753 self._parse_attributes()
754 index = self._parse_type()
755 index = self._parse_alternatives(index)
756 self._parse_identity_constraints(index)
757 if self.parent is None:
758 self._parse_substitution_group()
759 self._parse_target_namespace()
760
762 if self._ref is not None:
763 self.alternatives = self._ref.alternatives
764 else:
765 self.alternatives = []
766 for child in self._iterparse_components(self.elem, start=index):
767 if child.tag == XSD_ALTERNATIVE:
768 self.alternatives.append(XsdAlternative(child, self.schema, self))
769 index += 1
770 else:
771 break
772 return index
773
774 @property
780
796
804
807 """
808 <alternative
809 id = ID
810 test = an XPath expression
811 type = QName
812 xpathDefaultNamespace = (anyURI | (##defaultNamespace | ##targetNamespace | ##local))
813 {any attributes with non-schema namespace . . .}>
814 Content: (annotation?, (simpleType | complexType)?)
815 </alternative>
816 """
817 _admitted_tags = {XSD_ALTERNATIVE}
818 type = None
819
821 return '%s(type=%r, test=%r)' % (self.__class__.__name__, self.elem.get('type'), self.elem.get('test'))
822
859
860 @property
862 raise NotImplementedError
863