From 2bcf78549ccea9a7a7e8aefbd1cc72d02d6ca58e Mon Sep 17 00:00:00 2001 From: Davide Brunato Date: Fri, 8 Nov 2019 07:17:29 +0100 Subject: [PATCH] Add count_occurs() to ModelGroup --- xmlschema/resources.py | 1 - xmlschema/tests/test_models.py | 18 +++++++++++++++++ xmlschema/validators/groups.py | 10 ++++++---- xmlschema/validators/models.py | 33 ++++++++++++++++++++++++++++--- xmlschema/validators/wildcards.py | 2 +- xmlschema/validators/xsdbase.py | 7 +++++++ 6 files changed, 62 insertions(+), 9 deletions(-) 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