From dd2ab7265467eadecf3c13c1b5850d8f9b35d74c Mon Sep 17 00:00:00 2001 From: Davide Brunato Date: Tue, 5 Nov 2019 11:09:34 +0100 Subject: [PATCH] Add iter_group() to ModelVisitor --- xmlschema/tests/test_models.py | 45 ++++++++++++++++++++++++++++++ xmlschema/validators/attributes.py | 3 +- xmlschema/validators/groups.py | 7 +---- xmlschema/validators/models.py | 31 +++++++++++++++----- 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/xmlschema/tests/test_models.py b/xmlschema/tests/test_models.py index d47c691..17bb15f 100644 --- a/xmlschema/tests/test_models.py +++ b/xmlschema/tests/test_models.py @@ -651,6 +651,51 @@ class TestModelValidation11(TestModelValidation): self.assertIsNone(schema.validate(xml_data)) + def test_all_model_with_relaxed_occurs(self): + schema = self.schema_class( + """ + + + + + + + + + + + + + """) + + xml_data = '' + + self.assertIsNone(schema.validate(xml_data)) + + schema = self.schema_class( + """ + + + + + + + + + + + + + + + + + + + """) + + self.assertIsNone(schema.validate(xml_data)) + class TestModelBasedSorting(XsdValidatorTestCase): diff --git a/xmlschema/validators/attributes.py b/xmlschema/validators/attributes.py index ecb203b..051d69d 100644 --- a/xmlschema/validators/attributes.py +++ b/xmlschema/validators/attributes.py @@ -235,7 +235,8 @@ class XsdAttribute(XsdComponent, ValidationMixin): elif text == self.fixed or validation == 'skip': pass elif self.type.text_decode(text) != self.type.text_decode(self.fixed): - yield self.validation_error(validation, "value differs from fixed value", text, **kwargs) + msg = "attribute {!r} has a fixed value {!r}".format(self.name, self.fixed) + yield self.validation_error(validation, msg, text, **kwargs) for result in self.type.iter_decode(text, validation, **kwargs): if isinstance(result, XMLSchemaValidationError): diff --git a/xmlschema/validators/groups.py b/xmlschema/validators/groups.py index 738df4f..c9ecc2e 100644 --- a/xmlschema/validators/groups.py +++ b/xmlschema/validators/groups.py @@ -821,11 +821,6 @@ class Xsd11Group(XsdGroup): Content: (annotation?, (element | any | group)*) """ - def __iter__(self): - if self.model == 'sequence': - return iter(self._group) - return iter(sorted(self._group, key=lambda x: isinstance(x, XsdAnyElement))) - def _parse_content_model(self, content_model): self.model = local_name(content_model.tag) if self.model == 'all': @@ -855,7 +850,7 @@ class Xsd11Group(XsdGroup): if ref != self.name: self.append(Xsd11Group(child, self.schema, self)) if (self.model != 'all') ^ (self[-1].model != 'all'): - msg = "an xs:%s group cannot reference to an x:%s group" + msg = "an xs:%s group cannot include a reference to an x:%s group" self.parse_error(msg % (self.model, self[-1].model)) self.pop() diff --git a/xmlschema/validators/models.py b/xmlschema/validators/models.py index df921b0..fac02dc 100644 --- a/xmlschema/validators/models.py +++ b/xmlschema/validators/models.py @@ -338,7 +338,9 @@ class ModelVisitor(MutableSequence): self.occurs = Counter() self._subgroups = [] self.element = None - self.group, self.items, self.match = root, iter(root), False + self.group = root + self.items = self.iter_group() + self.match = False self._start() def __str__(self): @@ -374,7 +376,9 @@ class ModelVisitor(MutableSequence): del self._subgroups[:] self.occurs.clear() self.element = None - self.group, self.items, self.match = self.root, iter(self.root), False + self.group = self.root + self.items = self.iter_group() + self.match = False def _start(self): while True: @@ -421,6 +425,18 @@ class ModelVisitor(MutableSequence): for e in self.advance(): yield e + def iter_group(self): + if self.group.model != 'all': + for item in self.group: + yield item + elif not self.occurs: + for e in self.group.iter_elements(): + yield e + else: + for e in self.group.iter_elements(): + if not e.is_over(self.occurs[e]): + yield e + def advance(self, match=False): """ Generator function for advance to the next element. Yields tuples with @@ -448,7 +464,7 @@ class ModelVisitor(MutableSequence): if model == 'choice': occurs[item] = 0 occurs[self.group] += 1 - self.items, self.match = iter(self.group), False + self.items, self.match = self.iter_group(), False elif model == 'sequence' and item is self.group[-1]: self.occurs[self.group] += 1 return item.is_missing(item_occurs) @@ -473,7 +489,7 @@ class ModelVisitor(MutableSequence): occurs[element] += 1 self.match = True if self.group.model == 'all': - self.items = (e for e in self.group if not e.is_over(occurs[e])) + self.items = (e for e in self.group.iter_elements() if not e.is_over(occurs[e])) elif not element.is_over(occurs[element]): return @@ -490,15 +506,16 @@ class ModelVisitor(MutableSequence): if obj is None: if not self.match: if self.group.model == 'all': - if all(e.min_occurs <= occurs[e] for e in self.group): + if all(e.min_occurs <= occurs[e] for e in self.group.iter_elements()): occurs[self.group] = 1 group, expected = self.group, self.expected if stop_item(group) and expected: yield group, occurs[group], expected elif self.group.model != 'all': - self.items, self.match = iter(self.group), False + self.items, self.match = self.iter_group(), False elif any(not e.is_over(occurs[e]) for e in self.group): - self.items, self.match = (e for e in self.group if not e.is_over(occurs[e])), False + self.items = self.iter_group() + self.match = False else: occurs[self.group] = 1