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 ..qnames import XSD_ANNOTATION, XSD_GROUP, XSD_ATTRIBUTE_GROUP, XSD_SEQUENCE, \
|
||||
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_ANY_SIMPLE_TYPE, XSD_OPEN_CONTENT, XSD_ASSERT, get_qname, local_name
|
||||
XSD_RESTRICTION, XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_ANY_TYPE, XSD_OVERRIDE, \
|
||||
XSD_SIMPLE_CONTENT, XSD_ANY_SIMPLE_TYPE, XSD_OPEN_CONTENT, XSD_ASSERT, \
|
||||
get_qname, local_name
|
||||
from ..helpers import get_xsd_derivation_attribute
|
||||
|
||||
from .exceptions import XMLSchemaValidationError, XMLSchemaDecodeError
|
||||
|
@ -52,6 +53,8 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
mixed = False
|
||||
assertions = ()
|
||||
open_content = None
|
||||
content_type = None
|
||||
default_open_content = None
|
||||
_block = None
|
||||
|
||||
_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}:
|
||||
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)
|
||||
|
||||
elif content_elem.tag == XSD_SIMPLE_CONTENT:
|
||||
|
@ -179,6 +186,7 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
self.base_type = base_type
|
||||
elif self.redefine:
|
||||
self.base_type = self.redefine
|
||||
self.open_content = None
|
||||
|
||||
if derivation_elem.tag == XSD_RESTRICTION:
|
||||
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
|
||||
)
|
||||
|
||||
if not self.open_content and self.schema.default_open_content:
|
||||
if content_type or self.schema.default_open_content.applies_to_empty:
|
||||
self.open_content = self.schema.default_open_content
|
||||
if not self.open_content:
|
||||
default_open_content = self.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 \
|
||||
not self.open_content.is_restriction(base_type.open_content):
|
||||
|
@ -453,6 +463,8 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
def is_empty(self):
|
||||
if self.name == XSD_ANY_TYPE:
|
||||
return False
|
||||
elif self.open_content and self.open_content.mode != 'none':
|
||||
return False
|
||||
return self.content_type.is_empty()
|
||||
|
||||
def is_emptiable(self):
|
||||
|
@ -571,6 +583,10 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
:return: yields a 3-tuple (simple content, complex content, attributes) containing \
|
||||
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
|
||||
for assertion in self.assertions:
|
||||
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}
|
||||
|
||||
@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):
|
||||
super(Xsd11ComplexType, self)._parse()
|
||||
|
||||
|
@ -677,19 +719,12 @@ class Xsd11ComplexType(XsdComplexType):
|
|||
|
||||
# Add open content to complex content type
|
||||
if isinstance(self.content_type, XsdGroup):
|
||||
open_content = self.open_content
|
||||
if open_content is not None:
|
||||
pass
|
||||
elif self.schema.default_open_content is not None:
|
||||
if self.content_type or self.schema.default_open_content.applies_to_empty:
|
||||
open_content = self.schema.default_open_content
|
||||
|
||||
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
|
||||
if self.open_content is None:
|
||||
assert self.content_type.interleave is None and self.content_type.suffix is None
|
||||
elif self.open_content.mode == 'interleave':
|
||||
self.content_type.interleave = self.content_type.suffix = self.open_content.any_element
|
||||
elif self.open_content.mode == 'suffix':
|
||||
self.content_type.suffix = self.open_content.any_element
|
||||
|
||||
# Add inheritable attributes
|
||||
if hasattr(self.base_type, 'attributes'):
|
||||
|
@ -707,19 +742,12 @@ class Xsd11ComplexType(XsdComplexType):
|
|||
self.default_attributes_apply = True
|
||||
|
||||
# Add default attributes
|
||||
if self.redefine is None:
|
||||
default_attributes = self.schema.default_attributes
|
||||
else:
|
||||
default_attributes = self.redefine.schema.default_attributes
|
||||
|
||||
if default_attributes is None:
|
||||
pass
|
||||
elif self.default_attributes_apply and not self.is_override():
|
||||
if self.default_attributes_apply:
|
||||
default_attributes = self.default_attributes
|
||||
if default_attributes is not None:
|
||||
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
|
||||
)
|
||||
self.attributes.update((k, v) for k, v in default_attributes.items())
|
||||
|
||||
def _parse_complex_content_extension(self, elem, base_type):
|
||||
# Complex content extension with simple base is forbidden XSD 1.1.
|
||||
|
@ -744,19 +772,6 @@ class Xsd11ComplexType(XsdComplexType):
|
|||
else:
|
||||
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.mixed:
|
||||
# Empty element-only model extension: don't create a nested sequence group.
|
||||
|
@ -831,6 +846,21 @@ class Xsd11ComplexType(XsdComplexType):
|
|||
else:
|
||||
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)
|
||||
|
||||
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)
|
||||
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():
|
||||
for assertion in xsd_type.assertions:
|
||||
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:
|
||||
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"
|
||||
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):
|
||||
pass # [XsdAnyElement()] equals to an empty complexType declaration
|
||||
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)
|
||||
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))
|
||||
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
|
||||
|
||||
try:
|
||||
|
|
|
@ -334,6 +334,10 @@ class XsdSimpleType(XsdType, ValidationMixin):
|
|||
else:
|
||||
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):
|
||||
"""
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
def iter_components(self, xsd_classes=None):
|
||||
|
|
|
@ -782,8 +782,8 @@ class XsdOpenContent(XsdComponent):
|
|||
return True
|
||||
|
||||
def is_restriction(self, other):
|
||||
if self.mode == 'none' or other is None or other.mode == 'none':
|
||||
return True
|
||||
if other is None or other.mode == 'none':
|
||||
return self.mode == 'none'
|
||||
elif self.mode == 'interleave' and other.mode == 'suffix':
|
||||
return False
|
||||
else:
|
||||
|
|
|
@ -701,8 +701,8 @@ class XsdType(XsdComponent):
|
|||
return any(self.is_derived(xsd_type, derivation) for derivation in block)
|
||||
|
||||
def is_dynamic_consistent(self, other):
|
||||
return self.is_derived(other) or hasattr(other, 'member_types') and \
|
||||
any(self.is_derived(mt) for mt in other.member_types)
|
||||
return other is self.any_type or self.is_derived(other) or \
|
||||
hasattr(other, 'member_types') and any(self.is_derived(mt) for mt in other.member_types)
|
||||
|
||||
def is_key(self):
|
||||
return self.name == XSD_ID or self.is_derived(self.maps.types[XSD_ID])
|
||||
|
|
Loading…
Reference in New Issue