Fix sequence model stop criteria
This commit is contained in:
parent
8207284c5a
commit
a60532a3ab
|
@ -2,11 +2,12 @@
|
|||
CHANGELOG
|
||||
*********
|
||||
|
||||
`v1.0.16`_ (2019-10-XX)
|
||||
`v1.0.16`_ (2019-11-15)
|
||||
=======================
|
||||
* Improved XMLResource with zip files interface and lazy
|
||||
* Improved XMLResource class for working with compressed files
|
||||
* Fix for validation with XSD wildcards and 'lax' process content
|
||||
* Fix for issue #1...
|
||||
* Fix ambiguous items validation for xs:choice and xs:sequence models
|
||||
* Dozens of W3C's failed tests fixed
|
||||
|
||||
`v1.0.15`_ (2019-10-13)
|
||||
=======================
|
||||
|
@ -271,3 +272,4 @@ v0.9.6 (2017-05-05)
|
|||
.. _v1.0.13: https://github.com/brunato/xmlschema/compare/v1.0.11...v1.0.13
|
||||
.. _v1.0.14: https://github.com/brunato/xmlschema/compare/v1.0.13...v1.0.14
|
||||
.. _v1.0.15: https://github.com/brunato/xmlschema/compare/v1.0.14...v1.0.15
|
||||
.. _v1.0.16: https://github.com/brunato/xmlschema/compare/v1.0.15...v1.0.16
|
||||
|
|
|
@ -171,7 +171,7 @@ def from_json(source, schema, path=None, converter=None, json_options=None, **kw
|
|||
|
||||
:param source: can be a string or a :meth:`read()` supporting file-like object \
|
||||
containing the JSON document.
|
||||
:param schema: an :class:`XMLSchema` instance.
|
||||
:param schema: an :class:`XMLSchema` or an :class:`XMLSchema11` instance.
|
||||
:param path: is an optional XPath expression for selecting the element of the schema \
|
||||
that matches the data that has to be encoded. For default the first global element of \
|
||||
the schema is used.
|
||||
|
|
|
@ -551,14 +551,14 @@ class TestModelValidation(XsdValidatorTestCase):
|
|||
<xs:element name="root">
|
||||
<xs:complexType>
|
||||
<xs:sequence minOccurs="2" maxOccurs="unbounded">
|
||||
<xs:element name="a" maxOccurs="unbounded"/>
|
||||
<xs:element name="ax" maxOccurs="unbounded"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
""")
|
||||
|
||||
self.assertIsNone(schema.validate('<root><a/><a/></root>'))
|
||||
self.assertIsNone(schema.validate('<root><ax/><ax/></root>'))
|
||||
|
||||
schema = self.schema_class(
|
||||
"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
@ -577,6 +577,8 @@ class TestModelValidation(XsdValidatorTestCase):
|
|||
self.assertIsNone(schema.validate('<root><a/><a/><a/></root>'))
|
||||
self.assertIsNone(schema.validate('<root><a/><a/><a/><a/><a/><a/></root>'))
|
||||
|
||||
def test_sequence_model_with_nested_choice_model(self):
|
||||
|
||||
schema = self.schema_class(
|
||||
"""<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
||||
|
@ -589,7 +591,7 @@ class TestModelValidation(XsdValidatorTestCase):
|
|||
</xs:element>
|
||||
<xs:group name="group1">
|
||||
<xs:choice>
|
||||
<xs:element name="ax" maxOccurs="unbounded"/>
|
||||
<xs:element name="a" maxOccurs="unbounded"/>
|
||||
<xs:element name="b"/>
|
||||
<xs:element name="c"/>
|
||||
</xs:choice>
|
||||
|
@ -597,9 +599,26 @@ class TestModelValidation(XsdValidatorTestCase):
|
|||
</xs:schema>
|
||||
""")
|
||||
|
||||
self.assertIsNone(schema.validate('<root><ax/><ax/></root>'))
|
||||
# self.assertIsNone(schema.validate('<root><a/><a/><a/></root>'))
|
||||
# self.assertIsNone(schema.validate('<root><a/><a/><a/><a/><a/><a/></root>'))
|
||||
self.assertIsNone(schema.validate('<root><a/><a/></root>'))
|
||||
self.assertIsNone(schema.validate('<root><a/><a/><a/></root>'))
|
||||
self.assertIsNone(schema.validate('<root><a/><a/><a/><a/><a/><a/></root>'))
|
||||
|
||||
def test_sequence_model_with_optional_elements(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="2">
|
||||
<xs:element name="a" minOccurs="1" maxOccurs="2" />
|
||||
<xs:element name="b" minOccurs="0" />
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
""")
|
||||
|
||||
self.assertIsNone(schema.validate('<root><a/><a/><b/></root>'))
|
||||
|
||||
def test_choice_model_with_extended_occurs(self):
|
||||
schema = self.schema_class(
|
||||
|
|
|
@ -453,12 +453,12 @@ class ModelVisitor(MutableSequence):
|
|||
if isinstance(item, ModelGroup):
|
||||
self.group, self.items, self.match = self.pop()
|
||||
|
||||
item_occurs = occurs[item]
|
||||
if self.group.model == 'choice':
|
||||
item_occurs = occurs[item]
|
||||
if not item_occurs:
|
||||
return False
|
||||
|
||||
item_max_occurs = occurs[(item,)] or item_occurs
|
||||
|
||||
min_group_occurs = max(1, item_occurs // (item.max_occurs or item_occurs))
|
||||
max_group_occurs = max(1, item_max_occurs // (item.min_occurs or 1))
|
||||
|
||||
|
@ -468,31 +468,40 @@ class ModelVisitor(MutableSequence):
|
|||
|
||||
self.items = self.iter_group()
|
||||
self.match = False
|
||||
return item.is_missing(max(item_occurs, occurs[(item,)]))
|
||||
return item.is_missing(item_max_occurs)
|
||||
|
||||
elif self.group.model == 'all':
|
||||
return False
|
||||
elif item_occurs:
|
||||
self.match = True
|
||||
elif self.match:
|
||||
pass
|
||||
elif occurs[item]:
|
||||
self.match = True
|
||||
elif item.is_emptiable():
|
||||
return False
|
||||
elif self.group.min_occurs <= occurs[self.group] or self:
|
||||
elif self.group.min_occurs <= max(occurs[self.group], occurs[(self.group,)]) or self:
|
||||
return stop_item(self.group)
|
||||
else:
|
||||
return True
|
||||
|
||||
if item is self.group[-1]:
|
||||
if any(occurs[x] for x in self if x is not item):
|
||||
self.occurs[self.group] += 1
|
||||
else:
|
||||
group_occurs = max(1, item_occurs // (item.min_occurs or 1))
|
||||
if self.group.is_over(group_occurs):
|
||||
group_occurs = self.group.max_occurs
|
||||
self.occurs[self.group] += max(1, group_occurs)
|
||||
for k, item2 in enumerate(self.group, start=1):
|
||||
item_occurs = occurs[item2]
|
||||
if not item_occurs:
|
||||
continue
|
||||
|
||||
return item.is_missing(max(item_occurs, occurs[(item,)]))
|
||||
item_max_occurs = occurs[(item2,)] or item_occurs
|
||||
if item_max_occurs == 1 or any(not x.is_emptiable() for x in self.group[k:]):
|
||||
self.occurs[self.group] += 1
|
||||
break
|
||||
|
||||
min_group_occurs = max(1, item_occurs // (item2.max_occurs or item_occurs))
|
||||
max_group_occurs = max(1, item_max_occurs // (item2.min_occurs or 1))
|
||||
|
||||
occurs[self.group] += min_group_occurs
|
||||
occurs[(self.group,)] += max_group_occurs
|
||||
break
|
||||
|
||||
return item.is_missing(max(occurs[item], occurs[(item,)]))
|
||||
|
||||
element, occurs = self.element, self.occurs
|
||||
if element is None:
|
||||
|
@ -514,7 +523,7 @@ class ModelVisitor(MutableSequence):
|
|||
yield element, occurs[element], [element]
|
||||
|
||||
while True:
|
||||
while self.group.is_over(occurs[self.group]):
|
||||
while self.group.is_over(max(occurs[self.group], occurs[(self.group,)])):
|
||||
stop_item(self.group)
|
||||
|
||||
obj = next(self.items, None)
|
||||
|
|
Loading…
Reference in New Issue