diff --git a/xmlschema/resources.py b/xmlschema/resources.py
index b072ab2..adb9c02 100644
--- a/xmlschema/resources.py
+++ b/xmlschema/resources.py
@@ -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)
diff --git a/xmlschema/tests/test_models.py b/xmlschema/tests/test_models.py
index 17bb15f..4f101c9 100644
--- a/xmlschema/tests/test_models.py
+++ b/xmlschema/tests/test_models.py
@@ -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_data = ''
+
+ self.assertIsNone(schema.validate(xml_data))
+
#
# Tests on issues
def test_issue_086(self):
diff --git a/xmlschema/validators/groups.py b/xmlschema/validators/groups.py
index c9ecc2e..2684135 100644
--- a/xmlschema/validators/groups.py
+++ b/xmlschema/validators/groups.py
@@ -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
diff --git a/xmlschema/validators/models.py b/xmlschema/validators/models.py
index fac02dc..fc4b9af 100644
--- a/xmlschema/validators/models.py
+++ b/xmlschema/validators/models.py
@@ -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
diff --git a/xmlschema/validators/wildcards.py b/xmlschema/validators/wildcards.py
index beb14b0..fe2e448 100644
--- a/xmlschema/validators/wildcards.py
+++ b/xmlschema/validators/wildcards.py
@@ -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:
diff --git a/xmlschema/validators/xsdbase.py b/xmlschema/validators/xsdbase.py
index aab0b89..65ae512 100644
--- a/xmlschema/validators/xsdbase.py
+++ b/xmlschema/validators/xsdbase.py
@@ -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