Change stop_item() in ModelVisitor.advance()

- Removed ParticleMixin.min_occurs_reps()
  - Removed ModelGroup.group_occurs()
This commit is contained in:
Davide Brunato 2019-11-08 16:40:02 +01:00
parent 2bcf78549c
commit 7c4cd8b4d3
4 changed files with 60 additions and 54 deletions

View File

@ -516,6 +516,8 @@ class TestModelValidation(XsdValidatorTestCase):
self.check_advance_true(model) # match choice with <elem4>
self.assertIsNone(model.element)
#
# Test pathological cases
def test_empty_choice_groups(self):
schema = self.schema_class("""<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
@ -549,17 +551,35 @@ class TestModelValidation(XsdValidatorTestCase):
<xs:element name="root">
<xs:complexType>
<xs:sequence minOccurs="2" maxOccurs="unbounded">
<xs:element name="ax" maxOccurs="unbounded"/>
<xs:element name="a" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
""")
xml_data = '<root><ax/><ax/></root>'
xml_data = '<root><a/><a/></root>'
self.assertIsNone(schema.validate(xml_data))
def test_choice_model_with_extended_occurs(self):
schema = self.schema_class(
"""<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root">
<xs:complexType>
<xs:choice maxOccurs="unbounded" minOccurs="0">
<xs:element maxOccurs="5" minOccurs="3" name="ax"/>
<xs:element maxOccurs="5" minOccurs="3" name="bx"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:schema>
""")
self.assertIsNone(schema.validate('<root><ax/><ax/><ax/></root>'))
self.assertIsNone(schema.validate('<root><ax/><ax/><ax/><ax/><ax/></root>'))
self.assertIsNone(schema.validate('<root><ax/><ax/><ax/><ax/><ax/><ax/></root>'))
#
# Tests on issues
def test_issue_086(self):

View File

@ -346,7 +346,7 @@ class XMLSchemaChildrenValidationError(XMLSchemaValidationError):
if not expected_tags:
pass # reason += " No child element is expected at this point." <-- this can be misleading
elif len(expected_tags) == 1:
reason += " Tag %s expected." % expected_tags[0]
reason += " Tag %r expected." % expected_tags[0]
else:
reason += " Tag (%s) expected." % ' | '.join(expected_tags)

View File

@ -156,33 +156,6 @@ class ModelGroup(MutableSequence, ParticleMixin):
else:
return self.max_occurs * sum(e.max_occurs for e in self) <= other.max_occurs
def count_occurs(self, occurs):
"""
Calculates the current model group occurrences from the occurs of its items.
"""
group_occurs = None
if self.model == 'sequence':
for item in filter(lambda x: occurs[x], self):
if group_occurs is not None:
return 1
group_occurs = item.min_occurs_reps(occurs)
elif self.model == 'choice':
for item in filter(lambda x: occurs[x], self):
group_occurs = item.min_occurs_reps(occurs)
break
else:
for item in filter(lambda x: occurs[x], self):
group_occurs = min(1, item.min_occurs_reps(occurs))
if group_occurs is None:
return 0
elif self.is_over(group_occurs):
return self.max_occurs
else:
return group_occurs
def iter_model(self, depth=0):
"""
A generator function iterating elements and groups of a model group. Skips pointless groups,
@ -486,27 +459,47 @@ class ModelVisitor(MutableSequence):
if model == 'all':
return False
elif item_occurs:
elif model == 'choice':
if not item_occurs:
return False
self.match = True
if model == 'choice':
occurs[self.group] += max(1, self.group.count_occurs(self.occurs))
group_occurs = min(1, occurs[item] // (item.min_occurs or 1))
if self.group.is_over(group_occurs):
group_occurs = self.group.max_occurs
occurs[self.group] += group_occurs
if group_occurs == 1:
occurs[item] = 0
self.items, self.match = self.iter_group(), False
elif model == 'sequence' and item is self.group[-1]:
self.occurs[self.group] += max(1, self.group.count_occurs(self.occurs))
else:
item_occurs %= item.min_occurs
occurs[item] = item_occurs
self.items, self.match = self.iter_group(), False
return item.is_missing(item_occurs)
elif model == 'sequence':
if self.match:
if item is self.group[-1]:
occurs[self.group] += max(1, self.group.count_occurs(self.occurs))
return not item.is_emptiable()
elif item.is_emptiable():
return False
elif self.group.min_occurs <= occurs[self.group] or self:
return stop_item(self.group)
elif item_occurs:
self.match = True
elif self.match:
pass
elif item.is_emptiable():
return False
elif self.group.min_occurs <= occurs[self.group] or self:
return stop_item(self.group)
else:
return True
if item is self.group[-1]:
if any(occurs[x] for x in self if x is not item):
group_occurs = 1
else:
return True
group_occurs = max(1, occurs[item] // (item.min_occurs or 1))
if self.group.is_over(group_occurs):
group_occurs = self.group.max_occurs
self.occurs[self.group] += max(1, group_occurs)
return item.is_missing(item_occurs)
element, occurs = self.element, self.occurs
if element is None:

View File

@ -958,13 +958,6 @@ class ParticleMixin(object):
def is_over(self, occurs):
return self.max_occurs is not None and self.max_occurs <= occurs
def min_occurs_reps(self, occurs):
"""Returns the repetitions of minimum occurrences."""
if not self.min_occurs:
return occurs[self]
else:
return occurs[self] // self.min_occurs
def has_occurs_restriction(self, other):
if self.min_occurs == self.max_occurs == 0:
return True