Implement element substitution and xsi:type block in instances
This commit is contained in:
parent
22fdcc9a5a
commit
de7e2343bd
|
@ -52,10 +52,10 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
mixed = False
|
||||
assertions = ()
|
||||
open_content = None
|
||||
_block = None
|
||||
|
||||
_ADMITTED_TAGS = {XSD_COMPLEX_TYPE, XSD_RESTRICTION}
|
||||
_CONTENT_TAIL_TAGS = {XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_ANY_ATTRIBUTE}
|
||||
_block = None
|
||||
|
||||
@staticmethod
|
||||
def normalize(text):
|
||||
|
@ -149,19 +149,10 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
return
|
||||
|
||||
self.base_type = base_type = self._parse_base_type(derivation_elem)
|
||||
|
||||
block = base_type.block
|
||||
if self._block is None and block:
|
||||
self._block = block
|
||||
|
||||
if derivation_elem.tag == XSD_RESTRICTION:
|
||||
self._parse_simple_content_restriction(derivation_elem, base_type)
|
||||
if base_type.blocked or 'restriction' in block and base_type != self:
|
||||
self.blocked = True
|
||||
else:
|
||||
self._parse_simple_content_extension(derivation_elem, base_type)
|
||||
if base_type.blocked or 'extension' in block and base_type != self:
|
||||
self.blocked = True
|
||||
|
||||
if content_elem is not elem[-1]:
|
||||
k = 2 if content_elem is not elem[0] else 1
|
||||
|
@ -184,24 +175,15 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
return
|
||||
|
||||
base_type = self._parse_base_type(derivation_elem, complex_content=True)
|
||||
|
||||
if base_type is not self:
|
||||
self.base_type = base_type
|
||||
elif self.redefine:
|
||||
self.base_type = self.redefine
|
||||
|
||||
block = base_type.block
|
||||
if self._block is None and block:
|
||||
self._block = block
|
||||
|
||||
if derivation_elem.tag == XSD_RESTRICTION:
|
||||
self._parse_complex_content_restriction(derivation_elem, base_type)
|
||||
if base_type.blocked or 'restriction' in block and base_type != self:
|
||||
self.blocked = True
|
||||
else:
|
||||
self._parse_complex_content_extension(derivation_elem, base_type)
|
||||
if base_type.blocked or 'extension' in block and base_type != self:
|
||||
self.blocked = True
|
||||
|
||||
if content_elem is not elem[-1]:
|
||||
k = 2 if content_elem is not elem[0] else 1
|
||||
|
@ -450,6 +432,10 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
|
||||
self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes)
|
||||
|
||||
@property
|
||||
def block(self):
|
||||
return self.schema.block_default if self._block is None else self._block
|
||||
|
||||
@property
|
||||
def built(self):
|
||||
return self.content_type.parent is not None or self.content_type.built
|
||||
|
@ -458,10 +444,6 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
def validation_attempted(self):
|
||||
return 'full' if self.built else self.content_type.validation_attempted
|
||||
|
||||
@property
|
||||
def block(self):
|
||||
return self.schema.block_default if self._block is None else self._block
|
||||
|
||||
@staticmethod
|
||||
def is_simple():
|
||||
return False
|
||||
|
@ -514,14 +496,15 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
self.base_type.is_valid(source, use_defaults, namespaces)
|
||||
|
||||
def is_derived(self, other, derivation=None):
|
||||
if derivation and derivation == self.derivation:
|
||||
derivation = None # derivation mode checked
|
||||
|
||||
if self is other:
|
||||
return True
|
||||
elif derivation and self.derivation and derivation != self.derivation and other.is_complex():
|
||||
return False
|
||||
return derivation is None
|
||||
elif other.name == XSD_ANY_TYPE:
|
||||
return True
|
||||
elif self.base_type is other:
|
||||
return True
|
||||
return derivation is None or self.base_type.derivation == derivation
|
||||
elif hasattr(other, 'member_types'):
|
||||
return any(self.is_derived(m, derivation) for m in other.member_types)
|
||||
elif self.base_type is None:
|
||||
|
|
|
@ -358,11 +358,19 @@ class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin)
|
|||
|
||||
@property
|
||||
def final(self):
|
||||
return self._final or self.schema.final_default if self.ref is None else self.ref.final
|
||||
if self.ref is not None:
|
||||
return self.ref.final
|
||||
elif self._final is not None:
|
||||
return self._final
|
||||
return self.schema.final_default
|
||||
|
||||
@property
|
||||
def block(self):
|
||||
return self._block or self.schema.block_default if self.ref is None else self.ref.block
|
||||
if self.ref is not None:
|
||||
return self.ref.block
|
||||
elif self._block is not None:
|
||||
return self._block
|
||||
return self.schema.block_default
|
||||
|
||||
@property
|
||||
def nillable(self):
|
||||
|
@ -479,8 +487,8 @@ class XsdElement(XsdComponent, ValidationMixin, ParticleMixin, ElementPathMixin)
|
|||
except (KeyError, TypeError) as err:
|
||||
yield self.validation_error(validation, err, elem, **kwargs)
|
||||
|
||||
if xsd_type.is_blocked(self.block):
|
||||
yield self.validation_error(validation, "usage of %r is blocked" % xsd_type, elem, **kwargs)
|
||||
if xsd_type.is_blocked(self):
|
||||
yield self.validation_error(validation, "usage of %r is blocked" % xsd_type, elem, **kwargs)
|
||||
|
||||
# Decode attributes
|
||||
attribute_group = self.get_attributes(xsd_type)
|
||||
|
|
|
@ -481,6 +481,13 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
return other_max_occurs >= max_occurs * self.max_occurs
|
||||
|
||||
def check_dynamic_context(self, elem, xsd_element, model_element, converter):
|
||||
if model_element is not xsd_element:
|
||||
if 'substitution' in model_element.block \
|
||||
or xsd_element.type.is_blocked(model_element):
|
||||
raise XMLSchemaValidationError(
|
||||
model_element, "substitution of %r is blocked" % model_element
|
||||
)
|
||||
|
||||
alternatives = ()
|
||||
if isinstance(xsd_element, XsdAnyElement):
|
||||
if xsd_element.process_contents == 'skip':
|
||||
|
@ -707,8 +714,10 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
content = model.iter_unordered_content(element_data.content)
|
||||
elif converter.losslessly:
|
||||
content = element_data.content
|
||||
else:
|
||||
elif isinstance(element_data.content, list):
|
||||
content = model.iter_collapsed_content(element_data.content)
|
||||
else:
|
||||
content = []
|
||||
|
||||
for index, (name, value) in enumerate(content):
|
||||
if isinstance(name, int):
|
||||
|
@ -775,7 +784,7 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
else:
|
||||
children[-1].tail = children[-1].tail.strip() + (padding[:-indent] or '\n')
|
||||
|
||||
if validation != 'skip' and errors:
|
||||
if validation != 'skip' and (errors or not content):
|
||||
attrib = {k: unicode_type(v) for k, v in element_data.attributes.items()}
|
||||
if validation == 'lax' and converter.etree_element_class is not etree_element:
|
||||
child_tags = [converter.etree_element(e.tag, attrib=e.attrib) for e in children]
|
||||
|
@ -783,6 +792,10 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
else:
|
||||
elem = converter.etree_element(element_data.tag, text, children, attrib)
|
||||
|
||||
if not content:
|
||||
reason = "wrong content type {!r}".format(type(element_data.content))
|
||||
yield self.validation_error(validation, reason, elem, **kwargs)
|
||||
|
||||
for index, particle, occurs, expected in errors:
|
||||
yield self.children_validation_error(validation, elem, index, particle, occurs, expected, **kwargs)
|
||||
|
||||
|
|
|
@ -569,8 +569,7 @@ class XsdType(XsdComponent):
|
|||
"""Common base class for XSD types."""
|
||||
|
||||
abstract = False
|
||||
blocked = False
|
||||
block = ''
|
||||
block = None
|
||||
base_type = None
|
||||
derivation = None
|
||||
redefine = None
|
||||
|
@ -664,17 +663,20 @@ class XsdType(XsdComponent):
|
|||
def is_derived(self, other, derivation=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def is_blocked(self, block=''):
|
||||
if self.blocked:
|
||||
return True
|
||||
elif not block:
|
||||
def is_blocked(self, xsd_element):
|
||||
"""
|
||||
Returns `True` if the base type derivation is blocked, `False` otherwise.
|
||||
"""
|
||||
xsd_type = xsd_element.type
|
||||
if self is xsd_type:
|
||||
return False
|
||||
elif self.derivation and self.derivation in block:
|
||||
return True
|
||||
elif self.base_type is None:
|
||||
|
||||
block = ('%s %s' % (xsd_element.block, xsd_type.block)).strip()
|
||||
if not block:
|
||||
return False
|
||||
else:
|
||||
return self.base_type.is_blocked(block)
|
||||
block = {x for x in block.split() if x in ('extension', 'restriction')}
|
||||
|
||||
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 \
|
||||
|
|
Loading…
Reference in New Issue