From 2b1497860b1e339cef454eaed783fe212c38427d Mon Sep 17 00:00:00 2001 From: Davide Brunato Date: Wed, 30 Oct 2019 07:13:47 +0100 Subject: [PATCH] Fix 'all' model groups visiting - Also at each match the group element is changed (TODO: check if it's better to restart like choice groups) - In XSD 1.1 __iter__ now yields wildcards at the end for 'all' and 'choice' model groups --- xmlschema/tests/test_models.py | 56 +++++++++++++++++++++++++++++++++- xmlschema/validators/groups.py | 5 +++ xmlschema/validators/models.py | 11 +++++-- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/xmlschema/tests/test_models.py b/xmlschema/tests/test_models.py index 3748ead..e8e41c4 100644 --- a/xmlschema/tests/test_models.py +++ b/xmlschema/tests/test_models.py @@ -15,13 +15,15 @@ This module runs tests concerning model groups validation. import unittest from xmlschema import XMLSchema10, XMLSchema11 -from xmlschema.validators import ModelVisitor +from xmlschema.validators import XsdElement, ModelVisitor from xmlschema.compat import ordered_dict_class from xmlschema.tests import casepath, XsdValidatorTestCase class TestModelValidation(XsdValidatorTestCase): + schema_class = XMLSchema10 + # --- Test helper functions --- def check_advance_true(self, model, expected=None): @@ -514,6 +516,32 @@ class TestModelValidation(XsdValidatorTestCase): self.check_advance_true(model) # match choice with self.assertIsNone(model.element) + def test_empty_choice_groups(self): + schema = self.schema_class(""" + + + + + + + + + + + + + + + + + """) + + xml_data = "" + model = ModelVisitor(schema.elements['root'].type.content_type) + self.assertIsInstance(model.element, XsdElement) + self.assertEqual(model.element.name, 'elem1') + self.assertIsNone(schema.validate(xml_data)) + # # Tests on issues def test_issue_086(self): @@ -576,6 +604,32 @@ class TestModelValidation(XsdValidatorTestCase): class TestModelValidation11(TestModelValidation): schema_class = XMLSchema11 + def test_all_model_with_wildcard(self): + schema = self.schema_class( + """ + + + + + + + + + + + """) + + xml_data = """ + + + 1 + + + + """ + + self.assertIsNone(schema.validate(xml_data)) + class TestModelBasedSorting(XsdValidatorTestCase): diff --git a/xmlschema/validators/groups.py b/xmlschema/validators/groups.py index 2dfe9c9..738df4f 100644 --- a/xmlschema/validators/groups.py +++ b/xmlschema/validators/groups.py @@ -821,6 +821,11 @@ 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': diff --git a/xmlschema/validators/models.py b/xmlschema/validators/models.py index 77c237f..46263a2 100644 --- a/xmlschema/validators/models.py +++ b/xmlschema/validators/models.py @@ -379,7 +379,12 @@ class ModelVisitor(MutableSequence): def _start(self): while True: item = next(self.items, None) - if item is None or not isinstance(item, ModelGroup): + if item is None: + if not self: + break + else: + self.group, self.items, self.match = self.pop() + elif not isinstance(item, ModelGroup): self.element = item break elif item: @@ -464,7 +469,9 @@ class ModelVisitor(MutableSequence): if match: occurs[element] += 1 self.match = True - if not element.is_over(occurs[element]): + if self.group.model == 'all': + pass + elif not element.is_over(occurs[element]): return obj = None