Fix openContent validation ad regex character group parsing
- Put interleave mode after model match for precedence - Fix regex parsing of character group ending with '-' - Fix complexType mixed content extension
This commit is contained in:
parent
4f5c819d0f
commit
c530fda102
|
@ -194,7 +194,7 @@ def iterparse_character_group(s, expand_ranges=False):
|
|||
raise XMLSchemaRegexError("bad character %r at position %d" % (s[k], k))
|
||||
escaped = on_range = False
|
||||
char = s[k]
|
||||
if k >= length - 1 or s[k + 1] != '-':
|
||||
if k >= length - 2 or s[k + 1] != '-':
|
||||
yield ord(char)
|
||||
elif s[k] == '\\':
|
||||
if escaped:
|
||||
|
@ -209,7 +209,7 @@ def iterparse_character_group(s, expand_ranges=False):
|
|||
yield ord('\\')
|
||||
on_range = False
|
||||
char = s[k]
|
||||
if k >= length - 1 or s[k + 1] != '-':
|
||||
if k >= length - 2 or s[k + 1] != '-':
|
||||
yield ord(char)
|
||||
if escaped:
|
||||
yield ord('\\')
|
||||
|
|
|
@ -390,6 +390,10 @@ class TestPatterns(unittest.TestCase):
|
|||
self.assertEqual(regex, r'^([^\w\W])$')
|
||||
self.assertRaises(XMLSchemaRegexError, get_python_regex, '[]')
|
||||
|
||||
def test_character_class_range(self):
|
||||
regex = get_python_regex('[bc-]')
|
||||
self.assertEqual(regex, r'^([\-bc])$')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
from xmlschema.tests import print_test_header
|
||||
|
|
|
@ -16,7 +16,6 @@ from ..qnames import XSD_ANNOTATION, XSD_GROUP, XSD_ATTRIBUTE_GROUP, XSD_SEQUENC
|
|||
XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_ANY_TYPE, XSD_SIMPLE_CONTENT, XSD_ANY_SIMPLE_TYPE, \
|
||||
XSD_OPEN_CONTENT, XSD_ASSERT
|
||||
from ..helpers import get_qname, local_name, get_xsd_derivation_attribute
|
||||
from ..etree import etree_element
|
||||
|
||||
from .exceptions import XMLSchemaValidationError, XMLSchemaDecodeError
|
||||
from .xsdbase import XsdType, ValidationMixin
|
||||
|
@ -28,8 +27,6 @@ from .wildcards import XsdOpenContent
|
|||
|
||||
XSD_MODEL_GROUP_TAGS = {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}
|
||||
|
||||
SEQUENCE_ELEMENT = etree_element(XSD_SEQUENCE)
|
||||
|
||||
|
||||
class XsdComplexType(XsdType, ValidationMixin):
|
||||
"""
|
||||
|
@ -137,14 +134,10 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
|
||||
content_elem = self._parse_child_component(elem, strict=False)
|
||||
if content_elem is None or content_elem.tag in self._CONTENT_TAIL_TAGS:
|
||||
#
|
||||
# complexType with empty content
|
||||
self.content_type = self.schema.BUILDERS.group_class(SEQUENCE_ELEMENT, self.schema, self)
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
self._parse_content_tail(elem)
|
||||
|
||||
elif content_elem.tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
|
||||
#
|
||||
# complexType with child elements
|
||||
self.content_type = self.schema.BUILDERS.group_class(content_elem, self.schema, self)
|
||||
self._parse_content_tail(elem)
|
||||
|
||||
|
@ -202,7 +195,7 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
self.open_content = XsdOpenContent(content_elem, self.schema, self)
|
||||
|
||||
if content_elem is elem[-1]:
|
||||
self.content_type = self.schema.BUILDERS.group_class(SEQUENCE_ELEMENT, self.schema, self)
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
else:
|
||||
for index, child in enumerate(elem):
|
||||
if content_elem is not child:
|
||||
|
@ -210,7 +203,7 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
elif elem[index + 1].tag in {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
|
||||
self.content_type = self.schema.BUILDERS.group_class(elem[index + 1], self.schema, self)
|
||||
else:
|
||||
self.content_type = self.schema.BUILDERS.group_class(SEQUENCE_ELEMENT, self.schema, self)
|
||||
self.content_type = self.schema.self.schema.create_empty_content_group(self)
|
||||
break
|
||||
self._parse_content_tail(elem)
|
||||
|
||||
|
@ -340,9 +333,7 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
self.parse_error(msg.format(base_type.content_type.model, content_type.model))
|
||||
break
|
||||
else:
|
||||
# Empty content model
|
||||
content_type = self.schema.BUILDERS.group_class(elem, self.schema, self)
|
||||
content_type.model = base_type.content_type.model
|
||||
content_type = self.schema.create_empty_content_group(self, base_type.content_type.model)
|
||||
|
||||
if base_type.is_element_only() and content_type.mixed:
|
||||
self.parse_error(
|
||||
|
@ -371,101 +362,74 @@ class XsdComplexType(XsdType, ValidationMixin):
|
|||
if 'extension' in base_type.final:
|
||||
self.parse_error("the base type is not derivable by extension")
|
||||
|
||||
# Parse openContent
|
||||
for group_elem in filter(lambda x: x.tag != XSD_ANNOTATION, elem):
|
||||
if group_elem.tag != XSD_OPEN_CONTENT:
|
||||
break
|
||||
self.open_content = XsdOpenContent(group_elem, self.schema, self)
|
||||
try:
|
||||
self.open_content.any_element.extend(base_type.open_content.any_element)
|
||||
except AttributeError:
|
||||
pass
|
||||
break
|
||||
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 base_type.is_empty():
|
||||
# Empty model extension: don't create a nested group.
|
||||
if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
self.content_type = self.schema.BUILDERS.group_class(group_elem, self.schema, self)
|
||||
else:
|
||||
# Empty content model
|
||||
self.content_type = self.schema.BUILDERS.group_class(elem, self.schema, self)
|
||||
else:
|
||||
# Create a dummy sequence content type if the base type has not empty content model
|
||||
sequence_elem = etree_element(XSD_SEQUENCE)
|
||||
sequence_elem.text = '\n '
|
||||
content_type = self.schema.BUILDERS.group_class(sequence_elem, self.schema, self)
|
||||
if not base_type.mixed:
|
||||
# Empty element-only model extension: don't create a nested group.
|
||||
if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
self.content_type = self.schema.BUILDERS.group_class(group_elem, self.schema, self)
|
||||
elif base_type.is_simple() or base_type.has_simple_content():
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
else:
|
||||
self.content_type = self.schema.create_empty_content_group(
|
||||
parent=self, model=base_type.content_type.model
|
||||
)
|
||||
elif base_type.mixed:
|
||||
# Empty mixed model extension
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
self.content_type.append(self.schema.create_empty_content_group(self.content_type))
|
||||
|
||||
if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
# Illegal derivation from a simple content. Always forbidden in XSD 1.1
|
||||
# for XSD 1.0 applies only with not empty base and not empty extension.
|
||||
if base_type.is_simple() or base_type.has_simple_content() and self.xsd_version == '1.0':
|
||||
self.parse_error("base %r is simple or has a simple content." % base_type, elem)
|
||||
base_type = self.maps.types[XSD_ANY_TYPE]
|
||||
if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
group = self.schema.BUILDERS.group_class(group_elem, self.schema, self.content_type)
|
||||
if not self.mixed:
|
||||
self.parse_error("base has a different content type (mixed=%r) and the "
|
||||
"extension group is not empty." % base_type.mixed, elem)
|
||||
else:
|
||||
group = self.schema.create_empty_content_group(self)
|
||||
|
||||
group = self.schema.BUILDERS.group_class(group_elem, self.schema, self)
|
||||
self.content_type.append(group)
|
||||
self.content_type.elem.append(base_type.content_type.elem)
|
||||
self.content_type.elem.append(group.elem)
|
||||
|
||||
if self.xsd_version == '1.0':
|
||||
if group.model == 'all':
|
||||
self.parse_error("cannot extend a complex content with xs:all")
|
||||
if base_type.content_type.model == 'all' and group.model == 'sequence':
|
||||
self.parse_error("xs:sequence cannot extend xs:all")
|
||||
elif group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
# Derivation from a simple content is forbidden if base type is not empty.
|
||||
if base_type.is_simple() or base_type.has_simple_content():
|
||||
self.parse_error("base %r is simple or has a simple content." % base_type, elem)
|
||||
base_type = self.any_type
|
||||
|
||||
elif base_type.content_type.model == 'all':
|
||||
if group.model == 'sequence':
|
||||
self.parse_error("xs:sequence cannot extend xs:all")
|
||||
elif group.model == 'all':
|
||||
if base_type.content_type.min_occurs != group.min_occurs:
|
||||
self.parse_error(
|
||||
"when xs:all extends xs:all the minOccurs must be the same"
|
||||
)
|
||||
if base_type.content_type.mixed and not base_type.content_type:
|
||||
self.parse_error(
|
||||
"xs:all cannot extend an xs:all with mixed empty content"
|
||||
)
|
||||
group = self.schema.BUILDERS.group_class(group_elem, self.schema, self)
|
||||
|
||||
elif base_type.content_type.model == 'sequence':
|
||||
if group.model == 'all':
|
||||
self.parse_error("xs:all cannot extend a not empty xs:sequence")
|
||||
elif group.model == 'all':
|
||||
self.parse_error("xs:all cannot extend a not empty xs:choice")
|
||||
if group.model == 'all':
|
||||
self.parse_error("cannot extend a complex content with xs:all")
|
||||
if base_type.content_type.model == 'all' and group.model == 'sequence':
|
||||
self.parse_error("xs:sequence cannot extend xs:all")
|
||||
|
||||
content_type.append(base_type.content_type)
|
||||
content_type.append(group)
|
||||
sequence_elem.append(base_type.content_type.elem)
|
||||
sequence_elem.append(group.elem)
|
||||
|
||||
if base_type.content_type.model == 'all' and base_type.content_type and group:
|
||||
if self.xsd_version == '1.0':
|
||||
self.parse_error("XSD 1.0 does not allow extension of a not empty 'all' model group")
|
||||
elif group.model != 'all':
|
||||
self.parse_error("cannot extend a not empty 'all' model group with a different model")
|
||||
|
||||
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE:
|
||||
self.parse_error("base has a different content type (mixed=%r) and the "
|
||||
"extension group is not empty." % base_type.mixed, elem)
|
||||
|
||||
elif not base_type.is_simple() and not base_type.has_simple_content():
|
||||
content_type.append(base_type.content_type)
|
||||
sequence_elem.append(base_type.content_type.elem)
|
||||
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE and self.mixed:
|
||||
self.parse_error("extended type has a mixed content but the base is element-only", elem)
|
||||
content_type = self.schema.create_empty_content_group(self)
|
||||
content_type.append(base_type.content_type)
|
||||
content_type.append(group)
|
||||
content_type.elem.append(base_type.content_type.elem)
|
||||
content_type.elem.append(group.elem)
|
||||
|
||||
if base_type.content_type.model == 'all' and base_type.content_type and group:
|
||||
self.parse_error("XSD 1.0 does not allow extension of a not empty 'all' model group")
|
||||
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE:
|
||||
self.parse_error("base has a different content type (mixed=%r) and the "
|
||||
"extension group is not empty." % base_type.mixed, elem)
|
||||
self.content_type = content_type
|
||||
|
||||
elif not base_type.is_simple() and not base_type.has_simple_content():
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
self.content_type.append(base_type.content_type)
|
||||
self.content_type.elem.append(base_type.content_type.elem)
|
||||
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE and self.mixed:
|
||||
self.parse_error("extended type has a mixed content but the base is element-only", elem)
|
||||
else:
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
|
||||
self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes)
|
||||
|
||||
@property
|
||||
|
@ -752,8 +716,111 @@ class Xsd11ComplexType(XsdComplexType):
|
|||
# https://www.w3.org/TR/2012/REC-xmlschema11-1-20120405/#sec-cos-ct-extends
|
||||
if base_type.is_simple() or base_type.has_simple_content():
|
||||
self.parse_error("base %r is simple or has a simple content." % base_type, elem)
|
||||
base_type = self.maps.types[XSD_ANY_TYPE]
|
||||
super(Xsd11ComplexType, self)._parse_complex_content_extension(elem, base_type)
|
||||
base_type = self.any_type
|
||||
|
||||
if 'extension' in base_type.final:
|
||||
self.parse_error("the base type is not derivable by extension")
|
||||
|
||||
# Parse openContent
|
||||
for group_elem in filter(lambda x: x.tag != XSD_ANNOTATION, elem):
|
||||
if group_elem.tag != XSD_OPEN_CONTENT:
|
||||
break
|
||||
self.open_content = XsdOpenContent(group_elem, self.schema, self)
|
||||
try:
|
||||
self.open_content.any_element.extend(base_type.open_content.any_element)
|
||||
except AttributeError:
|
||||
pass
|
||||
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.
|
||||
if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
self.content_type = self.schema.BUILDERS.group_class(group_elem, self.schema, self)
|
||||
else:
|
||||
self.content_type = self.schema.create_empty_content_group(
|
||||
parent=self, model=base_type.content_type.model
|
||||
)
|
||||
elif base_type.mixed:
|
||||
# Empty mixed model extension
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
self.content_type.append(self.schema.create_empty_content_group(self.content_type))
|
||||
|
||||
if group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
group = self.schema.BUILDERS.group_class(group_elem, self.schema, self.content_type)
|
||||
if not self.mixed:
|
||||
self.parse_error("base has a different content type (mixed=%r) and the "
|
||||
"extension group is not empty." % base_type.mixed, elem)
|
||||
if group.model == 'all':
|
||||
self.parse_error("cannot extend an empty mixed content with an xs:all")
|
||||
else:
|
||||
group = self.schema.create_empty_content_group(self)
|
||||
|
||||
self.content_type.append(group)
|
||||
self.content_type.elem.append(base_type.content_type.elem)
|
||||
self.content_type.elem.append(group.elem)
|
||||
|
||||
elif group_elem is not None and group_elem.tag in XSD_MODEL_GROUP_TAGS:
|
||||
group = self.schema.BUILDERS.group_class(group_elem, self.schema, self)
|
||||
|
||||
if base_type.content_type.model != 'all':
|
||||
content_type = self.schema.create_empty_content_group(self)
|
||||
content_type.append(base_type.content_type)
|
||||
content_type.elem.append(base_type.content_type.elem)
|
||||
|
||||
if group.model == 'all':
|
||||
msg = "xs:all cannot extend a not empty xs:%s"
|
||||
self.parse_error(msg % base_type.content_type.model)
|
||||
else:
|
||||
content_type.append(group)
|
||||
content_type.elem.append(group.elem)
|
||||
else:
|
||||
content_type = self.schema.create_empty_content_group(self, model='all')
|
||||
content_type.extend(base_type.content_type)
|
||||
content_type.elem.extend(base_type.content_type.elem)
|
||||
|
||||
if not group:
|
||||
pass
|
||||
elif group.model != 'all':
|
||||
self.parse_error("cannot extend a not empty 'all' model group with a different model")
|
||||
elif base_type.content_type.min_occurs != group.min_occurs:
|
||||
self.parse_error("when extend an xs:all group minOccurs must be the same")
|
||||
elif base_type.mixed and not base_type.content_type:
|
||||
self.parse_error("cannot extend an xs:all group with mixed empty content")
|
||||
else:
|
||||
content_type.extend(group)
|
||||
content_type.elem.extend(group.elem)
|
||||
|
||||
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE:
|
||||
self.parse_error("base has a different content type (mixed=%r) and the "
|
||||
"extension group is not empty." % base_type.mixed, elem)
|
||||
|
||||
self.content_type = content_type
|
||||
|
||||
elif not base_type.is_simple() and not base_type.has_simple_content():
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
self.content_type.append(base_type.content_type)
|
||||
self.content_type.elem.append(base_type.content_type.elem)
|
||||
if base_type.mixed != self.mixed and base_type.name != XSD_ANY_TYPE and self.mixed:
|
||||
self.parse_error("extended type has a mixed content but the base is element-only", elem)
|
||||
else:
|
||||
self.content_type = self.schema.create_empty_content_group(self)
|
||||
|
||||
self._parse_content_tail(elem, derivation='extension', base_attributes=base_type.attributes)
|
||||
|
||||
def _parse_content_tail(self, elem, **kwargs):
|
||||
self.attributes = self.schema.BUILDERS.attribute_group_class(elem, self.schema, self, **kwargs)
|
||||
|
|
|
@ -16,8 +16,8 @@ from __future__ import unicode_literals
|
|||
from ..compat import unicode_type
|
||||
from ..exceptions import XMLSchemaValueError
|
||||
from ..etree import etree_element
|
||||
from ..qnames import XSD_ANNOTATION, XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE, \
|
||||
XSD_COMPLEX_TYPE, XSD_ELEMENT, XSD_ANY, XSD_RESTRICTION, XSD_EXTENSION
|
||||
from ..qnames import XSD_ANNOTATION, XSD_GROUP, XSD_SEQUENCE, XSD_ALL, \
|
||||
XSD_CHOICE, XSD_ELEMENT, XSD_ANY
|
||||
from xmlschema.helpers import get_qname, local_name
|
||||
|
||||
from .exceptions import XMLSchemaValidationError, XMLSchemaChildrenValidationError
|
||||
|
@ -80,9 +80,7 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
interleave = None # an Xsd11AnyElement in case of XSD 1.1 openContent with mode='interleave'
|
||||
suffix = None # an Xsd11AnyElement in case of openContent with mode='suffix' or 'interleave'
|
||||
|
||||
_ADMITTED_TAGS = {
|
||||
XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_RESTRICTION, XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE
|
||||
}
|
||||
_ADMITTED_TAGS = {XSD_GROUP, XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}
|
||||
|
||||
def __init__(self, elem, schema, parent):
|
||||
self._group = []
|
||||
|
@ -114,49 +112,53 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
def _parse(self):
|
||||
super(XsdGroup, self)._parse()
|
||||
self.clear()
|
||||
elem = self.elem
|
||||
self._parse_particle(elem)
|
||||
self._parse_particle(self.elem)
|
||||
|
||||
if elem.tag == XSD_GROUP:
|
||||
# Global group or reference
|
||||
if self._parse_reference():
|
||||
try:
|
||||
xsd_group = self.schema.maps.lookup_group(self.name)
|
||||
except KeyError:
|
||||
self.parse_error("missing group %r" % self.prefixed_name)
|
||||
xsd_group = self.schema.create_any_content_group(self, self.name)
|
||||
|
||||
if isinstance(xsd_group, tuple):
|
||||
# Disallowed circular definition, substitute with any content group.
|
||||
self.parse_error("Circular definitions detected for group %r:" % self.name, xsd_group[0])
|
||||
self.model = 'sequence'
|
||||
self.mixed = True
|
||||
self.append(self.schema.BUILDERS.any_element_class(ANY_ELEMENT, self.schema, self))
|
||||
else:
|
||||
self.model = xsd_group.model
|
||||
if self.model == 'all':
|
||||
if self.max_occurs != 1:
|
||||
self.parse_error("maxOccurs must be 1 for 'all' model groups")
|
||||
if self.min_occurs not in (0, 1):
|
||||
self.parse_error("minOccurs must be (0 | 1) for 'all' model groups")
|
||||
if self.xsd_version == '1.0' and isinstance(self.parent, XsdGroup):
|
||||
self.parse_error("in XSD 1.0 the 'all' model group cannot be nested")
|
||||
self.append(xsd_group)
|
||||
self.ref = xsd_group
|
||||
return
|
||||
if self.elem.tag != XSD_GROUP:
|
||||
# Local group (sequence|all|choice)
|
||||
if 'name' in self.elem.attrib:
|
||||
self.parse_error("attribute 'name' not allowed for a local group")
|
||||
self._parse_content_model(self.elem)
|
||||
|
||||
elif self._parse_reference():
|
||||
try:
|
||||
self.name = get_qname(self.target_namespace, elem.attrib['name'])
|
||||
xsd_group = self.schema.maps.lookup_group(self.name)
|
||||
except KeyError:
|
||||
return
|
||||
self.parse_error("missing group %r" % self.prefixed_name)
|
||||
xsd_group = self.schema.create_any_content_group(self, self.name)
|
||||
|
||||
if isinstance(xsd_group, tuple):
|
||||
# Disallowed circular definition, substitute with any content group.
|
||||
self.parse_error("Circular definitions detected for group %r:" % self.name, xsd_group[0])
|
||||
self.model = 'sequence'
|
||||
self.mixed = True
|
||||
self.append(self.schema.BUILDERS.any_element_class(ANY_ELEMENT, self.schema, self))
|
||||
else:
|
||||
content_model = self._parse_child_component(elem, strict=True)
|
||||
self.model = xsd_group.model
|
||||
if self.model == 'all':
|
||||
if self.max_occurs != 1:
|
||||
self.parse_error("maxOccurs must be 1 for 'all' model groups")
|
||||
if self.min_occurs not in (0, 1):
|
||||
self.parse_error("minOccurs must be (0 | 1) for 'all' model groups")
|
||||
if self.xsd_version == '1.0' and isinstance(self.parent, XsdGroup):
|
||||
self.parse_error("in XSD 1.0 the 'all' model group cannot be nested")
|
||||
self.append(xsd_group)
|
||||
self.ref = xsd_group
|
||||
|
||||
else:
|
||||
attrib = self.elem.attrib
|
||||
try:
|
||||
self.name = get_qname(self.target_namespace, attrib['name'])
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
content_model = self._parse_child_component(self.elem, strict=True)
|
||||
if self.parent is not None:
|
||||
self.parse_error("attribute 'name' not allowed for a local group")
|
||||
else:
|
||||
if 'minOccurs' in elem.attrib:
|
||||
if 'minOccurs' in attrib:
|
||||
self.parse_error("attribute 'minOccurs' not allowed for a global group")
|
||||
if 'maxOccurs' in elem.attrib:
|
||||
if 'maxOccurs' in attrib:
|
||||
self.parse_error("attribute 'maxOccurs' not allowed for a global group")
|
||||
if 'minOccurs' in content_model.attrib:
|
||||
self.parse_error(
|
||||
|
@ -166,26 +168,13 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
self.parse_error(
|
||||
"attribute 'maxOccurs' not allowed for the model of a global group", content_model
|
||||
)
|
||||
if content_model.tag not in {XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
|
||||
self.parse_error('unexpected tag %r' % content_model.tag, content_model)
|
||||
return
|
||||
|
||||
elif elem.tag in {XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
|
||||
# Local group (sequence|all|choice)
|
||||
if 'name' in elem.attrib:
|
||||
self.parse_error("attribute 'name' not allowed for a local group")
|
||||
content_model = elem
|
||||
self.name = None
|
||||
elif elem.tag in {XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_RESTRICTION}:
|
||||
self.name = self.model = None
|
||||
return
|
||||
else:
|
||||
self.parse_error('unexpected tag %r' % elem.tag)
|
||||
return
|
||||
if content_model.tag in {XSD_SEQUENCE, XSD_ALL, XSD_CHOICE}:
|
||||
self._parse_content_model(content_model)
|
||||
else:
|
||||
self.parse_error('unexpected tag %r' % content_model.tag, content_model)
|
||||
|
||||
self._parse_content_model(elem, content_model)
|
||||
|
||||
def _parse_content_model(self, elem, content_model):
|
||||
def _parse_content_model(self, content_model):
|
||||
self.model = local_name(content_model.tag)
|
||||
if self.model == 'all':
|
||||
if self.max_occurs != 1:
|
||||
|
@ -198,7 +187,7 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
# Builds inner elements and reference groups later, for avoids circularity.
|
||||
self.append((child, self.schema))
|
||||
elif content_model.tag == XSD_ALL:
|
||||
self.parse_error("'all' model can contains only elements.", elem)
|
||||
self.parse_error("'all' model can contains only elements.")
|
||||
elif child.tag == XSD_ANY:
|
||||
self.append(XsdAnyElement(child, self.schema, self))
|
||||
elif child.tag in (XSD_SEQUENCE, XSD_CHOICE):
|
||||
|
@ -220,11 +209,11 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
else:
|
||||
self.append(xsd_group)
|
||||
elif self.redefine is None:
|
||||
self.parse_error("Circular definition detected for group %r:" % self.name, elem)
|
||||
self.parse_error("Circular definition detected for group %r:" % self.name)
|
||||
else:
|
||||
if child.get('minOccurs', '1') != '1' or child.get('maxOccurs', '1') != '1':
|
||||
self.parse_error(
|
||||
"Redefined group reference cannot have minOccurs/maxOccurs other than 1:", elem
|
||||
"Redefined group reference cannot have minOccurs/maxOccurs other than 1:"
|
||||
)
|
||||
self.append(self.redefine)
|
||||
else:
|
||||
|
@ -538,40 +527,42 @@ class XsdGroup(XsdComponent, ModelGroup, ValidationMixin):
|
|||
if callable(child.tag):
|
||||
continue # child is a <class 'lxml.etree._Comment'>
|
||||
|
||||
if self.interleave and self.interleave.is_matching(child.tag, default_namespace, group=self):
|
||||
xsd_element = self.interleave
|
||||
else:
|
||||
while model.element is not None:
|
||||
xsd_element = model.element.match(
|
||||
child.tag, default_namespace, group=self, occurs=model.occurs
|
||||
)
|
||||
if xsd_element is None:
|
||||
for particle, occurs, expected in model.advance(False):
|
||||
errors.append((index, particle, occurs, expected))
|
||||
model.clear()
|
||||
model_broken = True # the model is broken, continues with raw decoding.
|
||||
break
|
||||
else:
|
||||
continue
|
||||
while model.element is not None:
|
||||
xsd_element = model.element.match(
|
||||
child.tag, default_namespace, group=self, occurs=model.occurs
|
||||
)
|
||||
if xsd_element is None:
|
||||
if self.interleave is not None and \
|
||||
self.interleave.is_matching(child.tag, default_namespace, self, model.occurs):
|
||||
xsd_element = self.interleave
|
||||
break
|
||||
|
||||
for particle, occurs, expected in model.advance(True):
|
||||
for particle, occurs, expected in model.advance(False):
|
||||
errors.append((index, particle, occurs, expected))
|
||||
break
|
||||
else:
|
||||
if self.suffix and self.suffix.is_matching(child.tag, default_namespace, group=self):
|
||||
xsd_element = self.suffix
|
||||
model.clear()
|
||||
model_broken = True # the model is broken, continues with raw decoding.
|
||||
break
|
||||
else:
|
||||
for xsd_element in self.iter_elements():
|
||||
if xsd_element.is_matching(child.tag, default_namespace, group=self):
|
||||
if not model_broken:
|
||||
errors.append((index, xsd_element, 0, []))
|
||||
model_broken = True
|
||||
break
|
||||
else:
|
||||
errors.append((index, self, 0, None))
|
||||
xsd_element = None
|
||||
model_broken = True
|
||||
continue
|
||||
break
|
||||
|
||||
for particle, occurs, expected in model.advance(True):
|
||||
errors.append((index, particle, occurs, expected))
|
||||
break
|
||||
else:
|
||||
if self.suffix is not None and self.suffix.is_matching(child.tag, default_namespace, self):
|
||||
xsd_element = self.suffix
|
||||
else:
|
||||
for xsd_element in self.iter_elements():
|
||||
if xsd_element.is_matching(child.tag, default_namespace, group=self):
|
||||
if not model_broken:
|
||||
errors.append((index, xsd_element, 0, []))
|
||||
model_broken = True
|
||||
break
|
||||
else:
|
||||
errors.append((index, self, 0, None))
|
||||
xsd_element = None
|
||||
model_broken = True
|
||||
|
||||
if xsd_element is None or kwargs.get('no_depth'):
|
||||
# TODO: use a default decoder str-->str??
|
||||
|
@ -736,7 +727,7 @@ class Xsd11Group(XsdGroup):
|
|||
Content: (annotation?, (element | any | group)*)
|
||||
</all>
|
||||
"""
|
||||
def _parse_content_model(self, elem, content_model):
|
||||
def _parse_content_model(self, content_model):
|
||||
self.model = local_name(content_model.tag)
|
||||
if self.model == 'all':
|
||||
if self.max_occurs not in (0, 1):
|
||||
|
@ -770,11 +761,11 @@ class Xsd11Group(XsdGroup):
|
|||
self.pop()
|
||||
|
||||
elif self.redefine is None:
|
||||
self.parse_error("Circular definition detected for group %r:" % self.name, elem)
|
||||
self.parse_error("Circular definition detected for group %r:" % self.name)
|
||||
else:
|
||||
if child.get('minOccurs', '1') != '1' or child.get('maxOccurs', '1') != '1':
|
||||
self.parse_error(
|
||||
"Redefined group reference cannot have minOccurs/maxOccurs other than 1:", elem
|
||||
"Redefined group reference cannot have minOccurs/maxOccurs other than 1:"
|
||||
)
|
||||
self.append(self.redefine)
|
||||
else:
|
||||
|
|
|
@ -27,9 +27,9 @@ from ..exceptions import XMLSchemaTypeError, XMLSchemaURLError, XMLSchemaKeyErro
|
|||
from ..qnames import VC_MIN_VERSION, VC_MAX_VERSION, VC_TYPE_AVAILABLE, \
|
||||
VC_TYPE_UNAVAILABLE, VC_FACET_AVAILABLE, VC_FACET_UNAVAILABLE, XSD_SCHEMA, \
|
||||
XSD_ANNOTATION, XSD_NOTATION, XSD_ATTRIBUTE, XSD_ATTRIBUTE_GROUP, XSD_GROUP, \
|
||||
XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE, XSD_ELEMENT, XSD_SEQUENCE, XSD_ANY, \
|
||||
XSD_ANY_ATTRIBUTE, XSD_INCLUDE, XSD_IMPORT, XSD_REDEFINE, XSD_OVERRIDE, \
|
||||
XSD_DEFAULT_OPEN_CONTENT
|
||||
XSD_SIMPLE_TYPE, XSD_COMPLEX_TYPE, XSD_ELEMENT, XSD_SEQUENCE, XSD_CHOICE, \
|
||||
XSD_ALL, XSD_ANY, XSD_ANY_ATTRIBUTE, XSD_INCLUDE, XSD_IMPORT, XSD_REDEFINE, \
|
||||
XSD_OVERRIDE, XSD_DEFAULT_OPEN_CONTENT
|
||||
from ..helpers import get_xsd_derivation_attribute, get_xsd_form_attribute
|
||||
from ..namespaces import XSD_NAMESPACE, XML_NAMESPACE, XSI_NAMESPACE, XHTML_NAMESPACE, \
|
||||
XLINK_NAMESPACE, VC_NAMESPACE, NamespaceResourcesMap, NamespaceView
|
||||
|
@ -644,6 +644,19 @@ class XMLSchemaBase(XsdValidator, ValidationMixin, ElementPathMixin):
|
|||
attribute_group[None] = self.BUILDERS.any_attribute_class(ANY_ATTRIBUTE_ELEMENT, self, attribute_group)
|
||||
return attribute_group
|
||||
|
||||
def create_empty_content_group(self, parent, model='sequence'):
|
||||
if model == 'sequence':
|
||||
group_elem = etree_element(XSD_SEQUENCE)
|
||||
elif model == 'choice':
|
||||
group_elem = etree_element(XSD_CHOICE)
|
||||
elif model == 'all':
|
||||
group_elem = etree_element(XSD_ALL)
|
||||
else:
|
||||
raise XMLSchemaValueError("'model' argument must be (sequence | choice | all)")
|
||||
|
||||
group_elem.text = '\n '
|
||||
return self.BUILDERS.group_class(group_elem, self, parent)
|
||||
|
||||
def copy(self):
|
||||
"""Makes a copy of the schema instance. The new instance has independent maps of shared XSD components."""
|
||||
schema = object.__new__(self.__class__)
|
||||
|
@ -1113,7 +1126,7 @@ class XMLSchemaBase(XsdValidator, ValidationMixin, ElementPathMixin):
|
|||
"""
|
||||
if not self.built:
|
||||
if self.meta_schema is not None:
|
||||
raise XMLSchemaNotBuiltError(self, "schema %r is not built." % self)
|
||||
raise XMLSchemaNotBuiltError(self, "schema %r is not built" % self)
|
||||
self.build()
|
||||
|
||||
if not isinstance(source, XMLResource):
|
||||
|
@ -1195,7 +1208,7 @@ class XMLSchemaBase(XsdValidator, ValidationMixin, ElementPathMixin):
|
|||
"""
|
||||
if not self.built:
|
||||
if self.meta_schema is not None:
|
||||
raise XMLSchemaNotBuiltError(self, "schema %r is not built." % self)
|
||||
raise XMLSchemaNotBuiltError(self, "schema %r is not built" % self)
|
||||
self.build()
|
||||
|
||||
if validation not in XSD_VALIDATION_MODES:
|
||||
|
@ -1272,7 +1285,7 @@ class XMLSchemaBase(XsdValidator, ValidationMixin, ElementPathMixin):
|
|||
"""
|
||||
if not self.built:
|
||||
if self.meta_schema is not None:
|
||||
raise XMLSchemaNotBuiltError(self, "schema %r is not built." % self)
|
||||
raise XMLSchemaNotBuiltError(self, "schema %r is not built" % self)
|
||||
self.build()
|
||||
|
||||
if validation not in XSD_VALIDATION_MODES:
|
||||
|
|
|
@ -588,7 +588,7 @@ class Xsd11AnyElement(XsdAnyElement):
|
|||
namespace = default_namespace
|
||||
|
||||
if group in self.precedences:
|
||||
if not occurs:
|
||||
if occurs is None:
|
||||
if any(e.is_matching(name) for e in self.precedences[group]):
|
||||
return False
|
||||
elif any(e.is_matching(name) and not e.is_over(occurs[e]) for e in self.precedences[group]):
|
||||
|
|
Loading…
Reference in New Issue