Fix defaultOpenContent and defaultAttributes parsing
This commit is contained in:
parent
a60532a3ab
commit
92de835afa
|
@ -13,8 +13,9 @@ from __future__ import unicode_literals
|
||||||
from ..exceptions import XMLSchemaValueError
|
from ..exceptions import XMLSchemaValueError
|
||||||
from ..qnames import XSD_ANNOTATION, XSD_GROUP, XSD_ATTRIBUTE_GROUP, XSD_SEQUENCE, \
|
from ..qnames import XSD_ANNOTATION, XSD_GROUP, XSD_ATTRIBUTE_GROUP, XSD_SEQUENCE, \
|
||||||
XSD_ALL, XSD_CHOICE, XSD_ANY_ATTRIBUTE, XSD_ATTRIBUTE, XSD_COMPLEX_CONTENT, \
|
XSD_ALL, XSD_CHOICE, XSD_ANY_ATTRIBUTE, XSD_ATTRIBUTE, XSD_COMPLEX_CONTENT, \
|
||||||
XSD_RESTRICTION, XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_ANY_TYPE, XSD_SIMPLE_CONTENT, \
|
XSD_RESTRICTION, XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_ANY_TYPE, XSD_OVERRIDE, \
|
||||||
XSD_ANY_SIMPLE_TYPE, XSD_OPEN_CONTENT, XSD_ASSERT, get_qname, local_name
|
XSD_SIMPLE_CONTENT, XSD_ANY_SIMPLE_TYPE, XSD_OPEN_CONTENT, XSD_ASSERT, \
|
||||||
|
get_qname, local_name
|
||||||
from ..helpers import get_xsd_derivation_attribute
|
from ..helpers import get_xsd_derivation_attribute
|
||||||
|
|
||||||
from .exceptions import XMLSchemaValidationError, XMLSchemaDecodeError
|
from .exceptions import XMLSchemaValidationError, XMLSchemaDecodeError
|
||||||
|
@ -52,6 +53,8 @@ class XsdComplexType(XsdType, ValidationMixin):
|
||||||
mixed = False
|
mixed = False
|
||||||
assertions = ()
|
assertions = ()
|
||||||
open_content = None
|
open_content = None
|
||||||
|
content_type = None
|
||||||
|
default_open_content = None
|
||||||
_block = None
|
_block = None
|
||||||
|
|
||||||
_ADMITTED_TAGS = {XSD_COMPLEX_TYPE, XSD_RESTRICTION}
|
_ADMITTED_TAGS = {XSD_COMPLEX_TYPE, XSD_RESTRICTION}
|
||||||
|
@ -138,6 +141,10 @@ class XsdComplexType(XsdType, ValidationMixin):
|
||||||
|
|
||||||
elif content_elem.tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
|
elif content_elem.tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
|
||||||
self.content_type = self.schema.BUILDERS.group_class(content_elem, self.schema, self)
|
self.content_type = self.schema.BUILDERS.group_class(content_elem, self.schema, self)
|
||||||
|
default_open_content = self.default_open_content
|
||||||
|
if default_open_content and \
|
||||||
|
(self.mixed or self.content_type or default_open_content.applies_to_empty):
|
||||||
|
self.open_content = default_open_content
|
||||||
self._parse_content_tail(elem)
|
self._parse_content_tail(elem)
|
||||||
|
|
||||||
elif content_elem.tag == XSD_SIMPLE_CONTENT:
|
elif content_elem.tag == XSD_SIMPLE_CONTENT:
|
||||||
|
@ -179,6 +186,7 @@ class XsdComplexType(XsdType, ValidationMixin):
|
||||||
self.base_type = base_type
|
self.base_type = base_type
|
||||||
elif self.redefine:
|
elif self.redefine:
|
||||||
self.base_type = self.redefine
|
self.base_type = self.redefine
|
||||||
|
self.open_content = None
|
||||||
|
|
||||||
if derivation_elem.tag == XSD_RESTRICTION:
|
if derivation_elem.tag == XSD_RESTRICTION:
|
||||||
self._parse_complex_content_restriction(derivation_elem, base_type)
|
self._parse_complex_content_restriction(derivation_elem, base_type)
|
||||||
|
@ -344,9 +352,11 @@ class XsdComplexType(XsdType, ValidationMixin):
|
||||||
"derived an empty content from base type that has not empty content.", elem
|
"derived an empty content from base type that has not empty content.", elem
|
||||||
)
|
)
|
||||||
|
|
||||||
if not self.open_content and self.schema.default_open_content:
|
if not self.open_content:
|
||||||
if content_type or self.schema.default_open_content.applies_to_empty:
|
default_open_content = self.default_open_content
|
||||||
self.open_content = self.schema.default_open_content
|
if default_open_content and \
|
||||||
|
(self.mixed or content_type or default_open_content.applies_to_empty):
|
||||||
|
self.open_content = default_open_content
|
||||||
|
|
||||||
if self.open_content and content_type and \
|
if self.open_content and content_type and \
|
||||||
not self.open_content.is_restriction(base_type.open_content):
|
not self.open_content.is_restriction(base_type.open_content):
|
||||||
|
@ -453,6 +463,8 @@ class XsdComplexType(XsdType, ValidationMixin):
|
||||||
def is_empty(self):
|
def is_empty(self):
|
||||||
if self.name == XSD_ANY_TYPE:
|
if self.name == XSD_ANY_TYPE:
|
||||||
return False
|
return False
|
||||||
|
elif self.open_content and self.open_content.mode != 'none':
|
||||||
|
return False
|
||||||
return self.content_type.is_empty()
|
return self.content_type.is_empty()
|
||||||
|
|
||||||
def is_emptiable(self):
|
def is_emptiable(self):
|
||||||
|
@ -571,6 +583,10 @@ class XsdComplexType(XsdType, ValidationMixin):
|
||||||
:return: yields a 3-tuple (simple content, complex content, attributes) containing \
|
:return: yields a 3-tuple (simple content, complex content, attributes) containing \
|
||||||
the decoded parts, eventually preceded by a sequence of validation or decoding errors.
|
the decoded parts, eventually preceded by a sequence of validation or decoding errors.
|
||||||
"""
|
"""
|
||||||
|
if self.is_empty() and elem.text:
|
||||||
|
reason = "character data between child elements not allowed because the type's content is empty"
|
||||||
|
yield self.validation_error(validation, reason, elem, **kwargs)
|
||||||
|
|
||||||
# XSD 1.1 assertions
|
# XSD 1.1 assertions
|
||||||
for assertion in self.assertions:
|
for assertion in self.assertions:
|
||||||
for error in assertion(elem, **kwargs):
|
for error in assertion(elem, **kwargs):
|
||||||
|
@ -665,6 +681,32 @@ class Xsd11ComplexType(XsdComplexType):
|
||||||
|
|
||||||
_CONTENT_TAIL_TAGS = {XSD_ATTRIBUTE_GROUP, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE, XSD_ASSERT}
|
_CONTENT_TAIL_TAGS = {XSD_ATTRIBUTE_GROUP, XSD_ATTRIBUTE, XSD_ANY_ATTRIBUTE, XSD_ASSERT}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_attributes(self):
|
||||||
|
if self.redefine is not None:
|
||||||
|
return self.schema.default_attributes
|
||||||
|
|
||||||
|
for child in filter(lambda x: x.tag == XSD_OVERRIDE, self.schema.root):
|
||||||
|
if self.elem in child:
|
||||||
|
schema = self.schema.includes[child.attrib['schemaLocation']]
|
||||||
|
if schema.override is self.schema:
|
||||||
|
return schema.default_attributes
|
||||||
|
else:
|
||||||
|
return self.schema.default_attributes
|
||||||
|
|
||||||
|
@property
|
||||||
|
def default_open_content(self):
|
||||||
|
if self.parent is not None:
|
||||||
|
return self.schema.default_open_content
|
||||||
|
|
||||||
|
for child in filter(lambda x: x.tag == XSD_OVERRIDE, self.schema.root):
|
||||||
|
if self.elem in child:
|
||||||
|
schema = self.schema.includes[child.attrib['schemaLocation']]
|
||||||
|
if schema.override is self.schema:
|
||||||
|
return schema.default_open_content
|
||||||
|
else:
|
||||||
|
return self.schema.default_open_content
|
||||||
|
|
||||||
def _parse(self):
|
def _parse(self):
|
||||||
super(Xsd11ComplexType, self)._parse()
|
super(Xsd11ComplexType, self)._parse()
|
||||||
|
|
||||||
|
@ -677,19 +719,12 @@ class Xsd11ComplexType(XsdComplexType):
|
||||||
|
|
||||||
# Add open content to complex content type
|
# Add open content to complex content type
|
||||||
if isinstance(self.content_type, XsdGroup):
|
if isinstance(self.content_type, XsdGroup):
|
||||||
open_content = self.open_content
|
if self.open_content is None:
|
||||||
if open_content is not None:
|
assert self.content_type.interleave is None and self.content_type.suffix is None
|
||||||
pass
|
elif self.open_content.mode == 'interleave':
|
||||||
elif self.schema.default_open_content is not None:
|
self.content_type.interleave = self.content_type.suffix = self.open_content.any_element
|
||||||
if self.content_type or self.schema.default_open_content.applies_to_empty:
|
elif self.open_content.mode == 'suffix':
|
||||||
open_content = self.schema.default_open_content
|
self.content_type.suffix = self.open_content.any_element
|
||||||
|
|
||||||
if open_content is None:
|
|
||||||
pass
|
|
||||||
elif open_content.mode == 'interleave':
|
|
||||||
self.content_type.interleave = self.content_type.suffix = open_content.any_element
|
|
||||||
elif open_content.mode == 'suffix':
|
|
||||||
self.content_type.suffix = open_content.any_element
|
|
||||||
|
|
||||||
# Add inheritable attributes
|
# Add inheritable attributes
|
||||||
if hasattr(self.base_type, 'attributes'):
|
if hasattr(self.base_type, 'attributes'):
|
||||||
|
@ -707,19 +742,12 @@ class Xsd11ComplexType(XsdComplexType):
|
||||||
self.default_attributes_apply = True
|
self.default_attributes_apply = True
|
||||||
|
|
||||||
# Add default attributes
|
# Add default attributes
|
||||||
if self.redefine is None:
|
if self.default_attributes_apply:
|
||||||
default_attributes = self.schema.default_attributes
|
default_attributes = self.default_attributes
|
||||||
else:
|
if default_attributes is not None:
|
||||||
default_attributes = self.redefine.schema.default_attributes
|
if self.redefine is None and any(k in self.attributes for k in default_attributes):
|
||||||
|
self.parse_error("at least a default attribute is already declared in the complex type")
|
||||||
if default_attributes is None:
|
self.attributes.update((k, v) for k, v in default_attributes.items())
|
||||||
pass
|
|
||||||
elif self.default_attributes_apply and not self.is_override():
|
|
||||||
if self.redefine is None and any(k in self.attributes for k in default_attributes):
|
|
||||||
self.parse_error("at least a default attribute is already declared in the complex type")
|
|
||||||
self.attributes.update(
|
|
||||||
(k, v) for k, v in default_attributes.items() if k not in self.attributes
|
|
||||||
)
|
|
||||||
|
|
||||||
def _parse_complex_content_extension(self, elem, base_type):
|
def _parse_complex_content_extension(self, elem, base_type):
|
||||||
# Complex content extension with simple base is forbidden XSD 1.1.
|
# Complex content extension with simple base is forbidden XSD 1.1.
|
||||||
|
@ -744,19 +772,6 @@ class Xsd11ComplexType(XsdComplexType):
|
||||||
else:
|
else:
|
||||||
group_elem = None
|
group_elem = None
|
||||||
|
|
||||||
if not self.open_content:
|
|
||||||
if self.schema.default_open_content:
|
|
||||||
self.open_content = self.schema.default_open_content
|
|
||||||
elif getattr(base_type, 'open_content', None):
|
|
||||||
self.open_content = base_type.open_content
|
|
||||||
|
|
||||||
try:
|
|
||||||
if self.open_content and not base_type.open_content.is_restriction(self.open_content):
|
|
||||||
msg = "{!r} is not an extension of the base type {!r}"
|
|
||||||
self.parse_error(msg.format(self.open_content, base_type.open_content))
|
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if not base_type.content_type:
|
if not base_type.content_type:
|
||||||
if not base_type.mixed:
|
if not base_type.mixed:
|
||||||
# Empty element-only model extension: don't create a nested sequence group.
|
# Empty element-only model extension: don't create a nested sequence group.
|
||||||
|
@ -831,6 +846,21 @@ class Xsd11ComplexType(XsdComplexType):
|
||||||
else:
|
else:
|
||||||
self.content_type = self.schema.create_empty_content_group(self)
|
self.content_type = self.schema.create_empty_content_group(self)
|
||||||
|
|
||||||
|
if not self.open_content:
|
||||||
|
default_open_content = self.default_open_content
|
||||||
|
if default_open_content and \
|
||||||
|
(self.mixed or self.content_type or default_open_content.applies_to_empty):
|
||||||
|
self.open_content = default_open_content
|
||||||
|
elif base_type.open_content:
|
||||||
|
self.open_content = base_type.open_content
|
||||||
|
|
||||||
|
if base_type.open_content and self.open_content is not base_type.open_content:
|
||||||
|
if self.open_content.mode == 'none':
|
||||||
|
self.open_content = base_type.open_content
|
||||||
|
elif not base_type.open_content.is_restriction(self.open_content):
|
||||||
|
msg = "{!r} is not an extension of the base type {!r}"
|
||||||
|
self.parse_error(msg.format(self.open_content, base_type.open_content))
|
||||||
|
|
||||||
self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes)
|
self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes)
|
||||||
|
|
||||||
def _parse_content_tail(self, elem, **kwargs):
|
def _parse_content_tail(self, elem, **kwargs):
|
||||||
|
|
|
@ -531,6 +531,10 @@ class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin)
|
||||||
yield converter.element_decode(element_data, self, level)
|
yield converter.element_decode(element_data, self, level)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if xsd_type.is_empty() and elem.text:
|
||||||
|
reason = "character data is not allowed because the type's content is empty"
|
||||||
|
yield self.validation_error(validation, reason, elem, **kwargs)
|
||||||
|
|
||||||
if not xsd_type.has_simple_content():
|
if not xsd_type.has_simple_content():
|
||||||
for assertion in xsd_type.assertions:
|
for assertion in xsd_type.assertions:
|
||||||
for error in assertion(elem, **kwargs):
|
for error in assertion(elem, **kwargs):
|
||||||
|
|
|
@ -526,7 +526,8 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
||||||
|
|
||||||
if model_element is not xsd_element and model_element.block:
|
if model_element is not xsd_element and model_element.block:
|
||||||
for derivation in model_element.block.split():
|
for derivation in model_element.block.split():
|
||||||
if xsd_type.is_derived(model_element.type, derivation):
|
if xsd_type is not model_element.type and \
|
||||||
|
xsd_type.is_derived(model_element.type, derivation):
|
||||||
reason = "usage of %r with type %s is blocked by head element"
|
reason = "usage of %r with type %s is blocked by head element"
|
||||||
raise XMLSchemaValidationError(self, reason % (xsd_element, derivation))
|
raise XMLSchemaValidationError(self, reason % (xsd_element, derivation))
|
||||||
|
|
||||||
|
@ -578,7 +579,7 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
||||||
if len(self) == 1 and isinstance(self[0], XsdAnyElement):
|
if len(self) == 1 and isinstance(self[0], XsdAnyElement):
|
||||||
pass # [XsdAnyElement()] equals to an empty complexType declaration
|
pass # [XsdAnyElement()] equals to an empty complexType declaration
|
||||||
else:
|
else:
|
||||||
reason = "character data between child elements not allowed!"
|
reason = "character data between child elements not allowed"
|
||||||
yield self.validation_error(validation, reason, elem, **kwargs)
|
yield self.validation_error(validation, reason, elem, **kwargs)
|
||||||
cdata_index = 0 # Do not decode CDATA
|
cdata_index = 0 # Do not decode CDATA
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ class XsdIdentity(XsdComponent):
|
||||||
yield XMLSchemaValidationError(self, e, "{!r} is not an element".format(xsd_element))
|
yield XMLSchemaValidationError(self, e, "{!r} is not an element".format(xsd_element))
|
||||||
xsd_fields = self.get_fields(xsd_element)
|
xsd_fields = self.get_fields(xsd_element)
|
||||||
|
|
||||||
if all(fld is None for fld in xsd_fields):
|
if not xsd_fields or all(fld is None for fld in xsd_fields):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -334,6 +334,10 @@ class XsdSimpleType(XsdType, ValidationMixin):
|
||||||
else:
|
else:
|
||||||
return self.base_type.is_derived(other, derivation)
|
return self.base_type.is_derived(other, derivation)
|
||||||
|
|
||||||
|
def is_dynamic_consistent(self, other):
|
||||||
|
return other is self.any_type or other is self.any_simple_type or self.is_derived(other) or \
|
||||||
|
hasattr(other, 'member_types') and any(self.is_derived(mt) for mt in other.member_types)
|
||||||
|
|
||||||
def normalize(self, text):
|
def normalize(self, text):
|
||||||
"""
|
"""
|
||||||
Normalize and restrict value-space with pre-lexical and lexical facets.
|
Normalize and restrict value-space with pre-lexical and lexical facets.
|
||||||
|
@ -867,7 +871,8 @@ class XsdUnion(XsdSimpleType):
|
||||||
return all(mt.is_list() for mt in self.member_types)
|
return all(mt.is_list() for mt in self.member_types)
|
||||||
|
|
||||||
def is_dynamic_consistent(self, other):
|
def is_dynamic_consistent(self, other):
|
||||||
return other.is_derived(self) or hasattr(other, 'member_types') and \
|
return other is self.any_type or other is self.any_simple_type or \
|
||||||
|
other.is_derived(self) or hasattr(other, 'member_types') and \
|
||||||
any(mt1.is_derived(mt2) for mt1 in other.member_types for mt2 in self.member_types)
|
any(mt1.is_derived(mt2) for mt1 in other.member_types for mt2 in self.member_types)
|
||||||
|
|
||||||
def iter_components(self, xsd_classes=None):
|
def iter_components(self, xsd_classes=None):
|
||||||
|
|
|
@ -782,8 +782,8 @@ class XsdOpenContent(XsdComponent):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_restriction(self, other):
|
def is_restriction(self, other):
|
||||||
if self.mode == 'none' or other is None or other.mode == 'none':
|
if other is None or other.mode == 'none':
|
||||||
return True
|
return self.mode == 'none'
|
||||||
elif self.mode == 'interleave' and other.mode == 'suffix':
|
elif self.mode == 'interleave' and other.mode == 'suffix':
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -701,8 +701,8 @@ class XsdType(XsdComponent):
|
||||||
return any(self.is_derived(xsd_type, derivation) for derivation in block)
|
return any(self.is_derived(xsd_type, derivation) for derivation in block)
|
||||||
|
|
||||||
def is_dynamic_consistent(self, other):
|
def is_dynamic_consistent(self, other):
|
||||||
return self.is_derived(other) or hasattr(other, 'member_types') and \
|
return other is self.any_type or self.is_derived(other) or \
|
||||||
any(self.is_derived(mt) for mt in other.member_types)
|
hasattr(other, 'member_types') and any(self.is_derived(mt) for mt in other.member_types)
|
||||||
|
|
||||||
def is_key(self):
|
def is_key(self):
|
||||||
return self.name == XSD_ID or self.is_derived(self.maps.types[XSD_ID])
|
return self.name == XSD_ID or self.is_derived(self.maps.types[XSD_ID])
|
||||||
|
|
Loading…
Reference in New Issue