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:
Davide Brunato 2019-09-06 06:42:34 +02:00
parent 4f5c819d0f
commit c530fda102
6 changed files with 273 additions and 198 deletions

View File

@ -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('\\')

View File

@ -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

View File

@ -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)

View File

@ -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:

View File

@ -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:

View File

@ -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]):