diff --git a/xmlschema/validators/complex_types.py b/xmlschema/validators/complex_types.py index 1a7fe2b..e45ff30 100644 --- a/xmlschema/validators/complex_types.py +++ b/xmlschema/validators/complex_types.py @@ -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: diff --git a/xmlschema/validators/elements.py b/xmlschema/validators/elements.py index 5a39819..a5fdc3f 100644 --- a/xmlschema/validators/elements.py +++ b/xmlschema/validators/elements.py @@ -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) diff --git a/xmlschema/validators/groups.py b/xmlschema/validators/groups.py index ed27409..57dcb60 100644 --- a/xmlschema/validators/groups.py +++ b/xmlschema/validators/groups.py @@ -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) diff --git a/xmlschema/validators/xsdbase.py b/xmlschema/validators/xsdbase.py index 152f0ee..13393ee 100644 --- a/xmlschema/validators/xsdbase.py +++ b/xmlschema/validators/xsdbase.py @@ -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 \