Add count_occurs() to ModelGroup
This commit is contained in:
parent
d0f3a0f6c8
commit
2bcf78549c
|
@ -135,7 +135,6 @@ def normalize_url(url, base_url=None, keep_relative=False):
|
|||
url_parts.query,
|
||||
url_parts.fragment,
|
||||
))
|
||||
|
||||
return filter_url(normalized_url)
|
||||
|
||||
|
||||
|
|
|
@ -542,6 +542,24 @@ class TestModelValidation(XsdValidatorTestCase):
|
|||
self.assertEqual(model.element.name, 'elem1')
|
||||
self.assertIsNone(schema.validate(xml_data))
|
||||
|
||||
def test_sequence_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:sequence minOccurs="2" maxOccurs="unbounded">
|
||||
<xs:element name="ax" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
""")
|
||||
|
||||
xml_data = '<root><ax/><ax/></root>'
|
||||
|
||||
self.assertIsNone(schema.validate(xml_data))
|
||||
|
||||
#
|
||||
# Tests on issues
|
||||
def test_issue_086(self):
|
||||
|
|
|
@ -988,16 +988,18 @@ class Xsd11Group(XsdGroup):
|
|||
for item in restriction_items:
|
||||
if other_item is item or item.is_restriction(other_item, check_occurs):
|
||||
if max_occurs is not None:
|
||||
if item.effective_max_occurs is None:
|
||||
effective_max_occurs = item.effective_max_occurs
|
||||
if effective_max_occurs is None:
|
||||
max_occurs = None
|
||||
else:
|
||||
max_occurs = counter_func(max_occurs, item.effective_max_occurs)
|
||||
max_occurs = counter_func(max_occurs, effective_max_occurs)
|
||||
|
||||
if other_max_occurs is not None:
|
||||
if other_item.effective_max_occurs is None:
|
||||
effective_max_occurs = other_item.effective_max_occurs
|
||||
if effective_max_occurs is None:
|
||||
other_max_occurs = None
|
||||
else:
|
||||
other_max_occurs = max(other_max_occurs, other_item.effective_max_occurs)
|
||||
other_max_occurs = max(other_max_occurs, effective_max_occurs)
|
||||
break
|
||||
else:
|
||||
continue
|
||||
|
|
|
@ -156,6 +156,33 @@ 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,
|
||||
|
@ -462,17 +489,17 @@ class ModelVisitor(MutableSequence):
|
|||
elif item_occurs:
|
||||
self.match = True
|
||||
if model == 'choice':
|
||||
occurs[self.group] += max(1, self.group.count_occurs(self.occurs))
|
||||
occurs[item] = 0
|
||||
occurs[self.group] += 1
|
||||
self.items, self.match = self.iter_group(), False
|
||||
elif model == 'sequence' and item is self.group[-1]:
|
||||
self.occurs[self.group] += 1
|
||||
self.occurs[self.group] += max(1, self.group.count_occurs(self.occurs))
|
||||
return item.is_missing(item_occurs)
|
||||
|
||||
elif model == 'sequence':
|
||||
if self.match:
|
||||
if item is self.group[-1]:
|
||||
occurs[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
|
||||
|
|
|
@ -141,7 +141,7 @@ class XsdWildcard(XsdComponent, ValidationMixin):
|
|||
return self.is_namespace_allowed('')
|
||||
else:
|
||||
return self.is_namespace_allowed('') or \
|
||||
self.is_namespace_allowed(default_namespace)
|
||||
self.is_namespace_allowed(default_namespace)
|
||||
|
||||
def is_namespace_allowed(self, namespace):
|
||||
if self.not_namespace:
|
||||
|
|
|
@ -958,6 +958,13 @@ 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
|
||||
|
|
Loading…
Reference in New Issue