Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-xmlschema for
openSUSE:Factory checked in at 2023-04-17 17:40:59
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-xmlschema (Old)
and /work/SRC/openSUSE:Factory/.python-xmlschema.new.2023 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-xmlschema"
Mon Apr 17 17:40:59 2023 rev:21 rq:1079725 version:2.2.3
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-xmlschema/python-xmlschema.changes
2023-03-29 23:26:35.651285561 +0200
+++
/work/SRC/openSUSE:Factory/.python-xmlschema.new.2023/python-xmlschema.changes
2023-04-17 17:41:00.706120724 +0200
@@ -1,0 +2,7 @@
+Sun Apr 16 08:16:21 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 2.2.3:
+ * Add support for Python 3.12
+ * Detach content iteration methods from ModelVisitor
+
+-------------------------------------------------------------------
Old:
----
xmlschema-2.2.2.tar.gz
New:
----
xmlschema-2.2.3.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-xmlschema.spec ++++++
--- /var/tmp/diff_new_pack.2smXCs/_old 2023-04-17 17:41:01.234123809 +0200
+++ /var/tmp/diff_new_pack.2smXCs/_new 2023-04-17 17:41:01.238123833 +0200
@@ -19,7 +19,7 @@
%{?!python_module:%define python_module() python-%{**} python3-%{**}}
%define skip_python2 1
Name: python-xmlschema
-Version: 2.2.2
+Version: 2.2.3
Release: 0
Summary: An XML Schema validator and decoder
License: MIT
++++++ xmlschema-2.2.2.tar.gz -> xmlschema-2.2.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/CHANGELOG.rst
new/xmlschema-2.2.3/CHANGELOG.rst
--- old/xmlschema-2.2.2/CHANGELOG.rst 2023-03-05 21:30:24.000000000 +0100
+++ new/xmlschema-2.2.3/CHANGELOG.rst 2023-04-14 15:49:05.000000000 +0200
@@ -2,6 +2,11 @@
CHANGELOG
*********
+`v2.2.3`_ (2023-04-14)
+======================
+* Add support for Python 3.12
+* Detach content iteration methods from ModelVisitor
+
`v2.2.2`_ (2023-03-05)
======================
* Fix mixed content extension with empty content (issue #337)
@@ -610,3 +615,4 @@
.. _v2.2.0: https://github.com/brunato/xmlschema/compare/v2.1.1...v2.2.0
.. _v2.2.1: https://github.com/brunato/xmlschema/compare/v2.2.0...v2.2.1
.. _v2.2.2: https://github.com/brunato/xmlschema/compare/v2.2.1...v2.2.2
+.. _v2.2.3: https://github.com/brunato/xmlschema/compare/v2.2.2...v2.2.3
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/PKG-INFO new/xmlschema-2.2.3/PKG-INFO
--- old/xmlschema-2.2.2/PKG-INFO 2023-03-05 22:10:04.662572100 +0100
+++ new/xmlschema-2.2.3/PKG-INFO 2023-04-14 15:52:42.466140500 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: xmlschema
-Version: 2.2.2
+Version: 2.2.3
Summary: An XML Schema validator and decoder
Home-page: https://github.com/sissaschool/xmlschema
Author: Davide Brunato
@@ -20,6 +20,7 @@
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/doc/conf.py
new/xmlschema-2.2.3/doc/conf.py
--- old/xmlschema-2.2.2/doc/conf.py 2023-03-05 21:30:24.000000000 +0100
+++ new/xmlschema-2.2.3/doc/conf.py 2023-04-14 15:49:05.000000000 +0200
@@ -81,7 +81,7 @@
# The short X.Y version.
version = '2.2'
# The full version, including alpha/beta/rc tags.
-release = '2.2.2'
+release = '2.2.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/setup.py new/xmlschema-2.2.3/setup.py
--- old/xmlschema-2.2.2/setup.py 2023-03-05 21:30:24.000000000 +0100
+++ new/xmlschema-2.2.3/setup.py 2023-04-14 15:49:05.000000000 +0200
@@ -18,7 +18,7 @@
setup(
name='xmlschema',
- version='2.2.2',
+ version='2.2.3',
packages=find_packages(include=['xmlschema*']),
package_data={
'xmlschema': ['py.typed', 'locale/**/*.mo', 'locale/**/*.po',
'schemas/*/*.xsd'],
@@ -62,6 +62,7 @@
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
+ 'Programming Language :: Python :: 3.12',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Software Development :: Libraries',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/xmlschema-2.2.2/tests/test_cases/issues/issue_341/issue_341-ext.xsd
new/xmlschema-2.2.3/tests/test_cases/issues/issue_341/issue_341-ext.xsd
--- old/xmlschema-2.2.2/tests/test_cases/issues/issue_341/issue_341-ext.xsd
1970-01-01 01:00:00.000000000 +0100
+++ new/xmlschema-2.2.3/tests/test_cases/issues/issue_341/issue_341-ext.xsd
2023-04-14 15:49:05.000000000 +0200
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
+ <xs:element name="TEST">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="TEST_EL" maxOccurs="1000">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="TEST_EL_2">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="exists_in_xml"
type="test_type"/>
+ <xs:element name="not_exists_in_xml"
type="test_type" minOccurs="0"/>
+ <xs:choice>
+ <xs:element name="choice_elem1"
minOccurs="0"/>
+ <xs:element name="choice_elem2"
minOccurs="0"/>
+ </xs:choice>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Date" type="xs:date"
use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="test_type">
+ <xs:attribute name="test_attr">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1"/>
+ <xs:maxLength value="60"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="test_attr_2">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1"/>
+ <xs:maxLength value="60"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+</xs:schema>
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/xmlschema-2.2.2/tests/test_cases/issues/issue_341/issue_341.xml
new/xmlschema-2.2.3/tests/test_cases/issues/issue_341/issue_341.xml
--- old/xmlschema-2.2.2/tests/test_cases/issues/issue_341/issue_341.xml
1970-01-01 01:00:00.000000000 +0100
+++ new/xmlschema-2.2.3/tests/test_cases/issues/issue_341/issue_341.xml
2023-04-14 15:49:05.000000000 +0200
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<TEST>
+ <TEST_EL Date="2022-10-03">
+ <TEST_EL_2>
+ <exists_in_xml test_attr="test_value_attr" />
+ </TEST_EL_2>
+ </TEST_EL>
+</TEST>
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/xmlschema-2.2.2/tests/test_cases/issues/issue_341/issue_341.xsd
new/xmlschema-2.2.3/tests/test_cases/issues/issue_341/issue_341.xsd
--- old/xmlschema-2.2.2/tests/test_cases/issues/issue_341/issue_341.xsd
1970-01-01 01:00:00.000000000 +0100
+++ new/xmlschema-2.2.3/tests/test_cases/issues/issue_341/issue_341.xsd
2023-04-14 15:49:05.000000000 +0200
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
+ <xs:element name="TEST">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="TEST_EL" maxOccurs="1000">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="TEST_EL_2">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="exists_in_xml"
type="test_type"/>
+ <xs:element name="not_exists_in_xml"
type="test_type" minOccurs="0"/>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="Date" type="xs:date"
use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:complexType name="test_type">
+ <xs:attribute name="test_attr">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1"/>
+ <xs:maxLength value="60"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:attribute name="test_attr_2">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:minLength value="1"/>
+ <xs:maxLength value="60"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ </xs:complexType>
+</xs:schema>
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/tests/validation/test_decoding.py
new/xmlschema-2.2.3/tests/validation/test_decoding.py
--- old/xmlschema-2.2.2/tests/validation/test_decoding.py 2023-02-11
11:41:50.000000000 +0100
+++ new/xmlschema-2.2.3/tests/validation/test_decoding.py 2023-04-14
15:49:05.000000000 +0200
@@ -30,7 +30,7 @@
from xmlschema.names import XSD_STRING, XSI_NIL
from xmlschema.converters import UnorderedConverter
-from xmlschema.validators import XMLSchema11
+from xmlschema.validators import XMLSchema11, ModelVisitor
from xmlschema.testing import XsdValidatorTestCase, etree_elements_assert_equal
VEHICLES_DICT = {
@@ -375,7 +375,9 @@
xml_dict = self.vh_schema.to_dict(vh_xml_tree,
namespaces=self.vh_namespaces)
self.assertEqual(xml_dict, VEHICLES_DICT)
- xml_dict = xmlschema.to_dict(vh_xml_tree, self.vh_schema.url,
namespaces=self.vh_namespaces)
+ xml_dict = xmlschema.to_dict(
+ vh_xml_tree, self.vh_schema.url, namespaces=self.vh_namespaces
+ )
self.assertEqual(xml_dict, VEHICLES_DICT)
xml_dict = self.col_schema.to_dict(col_xml_tree)
@@ -1375,6 +1377,92 @@
body_text =
result['Demonstrative_Examples']['Demonstrative_Example'][0]['Body_Text']
self.assertListEqual(body_text, expected)
+ def test_fill_missing_elements__issue_341(self):
+ xsd_file = self.casepath('issues/issue_341/issue_341.xsd')
+ xml_file = self.casepath('issues/issue_341/issue_341.xml')
+ schema = self.schema_class(xsd_file)
+
+ expected = {'TEST_EL': [
+ {'@Date': '2022-10-03',
+ 'TEST_EL_2': {
+ 'exists_in_xml': {
+ '@test_attr': 'test_value_attr', '@test_attr_2': None
+ }
+ }}
+ ]}
+ xml_dict = schema.decode(xml_file, fill_missing=True)
+ self.assertDictEqual(xml_dict, expected)
+
+ def fill_missing_content(element_data: ElementData, _xsd_element,
xsd_type):
+ group = xsd_type.model_group
+ if group is None:
+ return element_data # an element with simple content
+
+ filled_content = []
+ model = ModelVisitor(group)
+ xsd_element = None
+ for name, value, xsd_element in element_data.content:
+ if isinstance(name, int) or xsd_element is None:
+ filled_content.append((name, value, xsd_element))
+ continue
+
+ while model.element is not None:
+ if model.element is xsd_element:
+ filled_content.append((name, value, xsd_element))
+ for _err in model.advance(True):
+ pass
+ break
+
+ if model.element.max_occurs != 0:
+ filled_content.append((model.element.name, None,
model.element))
+ for _err in model.advance(False):
+ pass
+ else:
+ filled_content.append((name, value, xsd_element))
+
+ while model.element is not None:
+ if model.element is not xsd_element and
model.element.max_occurs != 0:
+ filled_content.append((model.element.name, None,
model.element))
+ for _err in model.advance(False):
+ pass
+
+ return ElementData(
+ element_data.tag,
+ element_data.text,
+ filled_content,
+ element_data.attributes
+ )
+
+ expected = {'TEST_EL': [
+ {'@Date': '2022-10-03',
+ 'TEST_EL_2': {
+ 'exists_in_xml': {
+ '@test_attr': 'test_value_attr', '@test_attr_2': None
+ },
+ 'not_exists_in_xml': None
+ }}
+ ]}
+ xml_dict = schema.decode(xml_file, element_hook=fill_missing_content,
fill_missing=True)
+ self.assertDictEqual(xml_dict, expected)
+
+ # Resolving more complex schemas requires more checks in hook function
+ xsd_file = self.casepath('issues/issue_341/issue_341-ext.xsd')
+ schema = self.schema_class(xsd_file)
+
+ expected = {'TEST_EL': [
+ {'@Date': '2022-10-03',
+ 'TEST_EL_2': {
+ 'exists_in_xml': {
+ '@test_attr': 'test_value_attr', '@test_attr_2': None
+ },
+ 'not_exists_in_xml': None,
+ 'choice_elem1': None,
+ 'choice_elem2': None, # this is wrong (at most one element
for a choice)
+ }}
+ ]}
+ xml_dict = schema.decode(xml_file, element_hook=fill_missing_content,
fill_missing=True)
+ self.assertDictEqual(xml_dict, expected)
+
class TestDecoding11(TestDecoding):
schema_class = XMLSchema11
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/tests/validators/test_models.py
new/xmlschema-2.2.3/tests/validators/test_models.py
--- old/xmlschema-2.2.2/tests/validators/test_models.py 2022-07-18
16:19:15.000000000 +0200
+++ new/xmlschema-2.2.3/tests/validators/test_models.py 2023-04-14
15:49:05.000000000 +0200
@@ -18,7 +18,8 @@
from xmlschema.exceptions import XMLSchemaValueError
from xmlschema.validators.exceptions import XMLSchemaValidationError
from xmlschema.validators.particles import ParticleMixin
-from xmlschema.validators.models import distinguishable_paths, ModelVisitor
+from xmlschema.validators.models import distinguishable_paths, ModelVisitor, \
+ sort_content, iter_collapsed_content
from xmlschema.validators.groups import XsdGroup
from xmlschema.validators.elements import XsdElement
from xmlschema.testing import XsdValidatorTestCase
@@ -115,7 +116,7 @@
model = ModelVisitor(group)
model.occurs[group[1]] = 1
- self.assertListEqual(list(model.items), group[1:])
+ self.assertEqual(list(model.items), group[1:])
group = ModelGroup('all')
group.append(ParticleMixin())
@@ -124,7 +125,7 @@
model = ModelVisitor(group)
model.occurs[group[1]] = 1
- self.assertListEqual(list(model.items), group[2:])
+ self.assertEqual(list(model.items), group[2:])
# --- Vehicles schema ---
@@ -985,33 +986,24 @@
</xs:complexType>
""")
- model = ModelVisitor(schema.types['A_type'].content)
+ group = schema.types['A_type'].content
self.assertListEqual(
- model.sort_content([('B2', 10), ('B1', 'abc'), ('B3', True)],
restart=False),
+ sort_content([('B2', 10), ('B1', 'abc'), ('B3', True)], group),
[('B1', 'abc'), ('B2', 10), ('B3', True)]
)
self.assertListEqual(
- model.sort_content([('B2', 10), ('B1', 'abc'), ('B3', True)]),
+ sort_content([('B3', True), ('B2', 10), ('B1', 'abc')], group),
[('B1', 'abc'), ('B2', 10), ('B3', True)]
)
self.assertListEqual(
- model.sort_content([('B2', 10), ('B1', 'abc'), ('B3', True)],
restart=False),
- [('B2', 10), ('B1', 'abc'), ('B3', True)]
- )
-
- self.assertListEqual(
- model.sort_content([('B3', True), ('B2', 10), ('B1', 'abc')]),
- [('B1', 'abc'), ('B2', 10), ('B3', True)]
- )
- self.assertListEqual(
- model.sort_content([('B2', 10), ('B4', None), ('B1', 'abc'),
('B3', True)]),
+ sort_content([('B2', 10), ('B4', None), ('B1', 'abc'), ('B3',
True)], group),
[('B1', 'abc'), ('B2', 10), ('B3', True), ('B4', None)]
)
content = [('B2', 10), ('B4', None), ('B1', 'abc'), (1, 'hello'),
('B3', True)]
self.assertListEqual(
- model.sort_content(content),
+ sort_content(content, group),
[(1, 'hello'), ('B1', 'abc'), ('B2', 10), ('B3', True), ('B4',
None)]
)
@@ -1019,7 +1011,7 @@
(2, 'world!'), ('B2', 10), ('B4', None), ('B1', 'abc'), (1,
'hello'), ('B3', True)
]
self.assertListEqual(
- model.sort_content(content),
+ sort_content(content, group),
[(1, 'hello'), ('B1', 'abc'), (2, 'world!'), ('B2', 10), ('B3',
True), ('B4', None)]
)
@@ -1028,7 +1020,7 @@
(5, 'five'), (4, 'four'), (2, 'two'), (3, 'three'), (1, 'one')
]
self.assertListEqual(
- model.sort_content(content),
+ sort_content(content, group),
[(1, 'one'), ('B1', 'abc'), (2, 'two'), ('B2', 10), (3, 'three'),
('B3', True), (4, 'four'), ('B4', None), (5, 'five'), (6, 'six')]
)
@@ -1036,26 +1028,30 @@
# With a dict-type argument
content = dict([('B2', [10]), ('B1', ['abc']), ('B3', [True])])
self.assertListEqual(
- model.sort_content(content), [('B1', 'abc'), ('B2', 10), ('B3',
True)]
+ sort_content(content, group), [('B1', 'abc'), ('B2', 10), ('B3',
True)]
)
content = dict([('B2', [10]), ('B1', ['abc']), ('B3', [True]), (1,
'hello')])
self.assertListEqual(
- model.sort_content(content), [(1, 'hello'), ('B1', 'abc'), ('B2',
10), ('B3', True)]
+ sort_content(content, group),
+ [(1, 'hello'), ('B1', 'abc'), ('B2', 10), ('B3', True)]
)
# With partial content
- self.assertListEqual(model.sort_content([]), [])
- self.assertListEqual(model.sort_content([('B1', 'abc')]), [('B1',
'abc')])
- self.assertListEqual(model.sort_content([('B2', 10)]), [('B2', 10)])
- self.assertListEqual(model.sort_content([('B3', True)]), [('B3',
True)])
+ self.assertListEqual(sort_content([], group), [])
+ self.assertListEqual(sort_content([('B1', 'abc')], group), [('B1',
'abc')])
+ self.assertListEqual(sort_content([('B2', 10)], group), [('B2', 10)])
+ self.assertListEqual(sort_content([('B3', True)], group), [('B3',
True)])
self.assertListEqual(
- model.sort_content([('B3', True), ('B1', 'abc')]), [('B1', 'abc'),
('B3', True)]
+ sort_content([('B3', True), ('B1', 'abc')], group),
+ [('B1', 'abc'), ('B3', True)]
)
self.assertListEqual(
- model.sort_content([('B2', 10), ('B1', 'abc')]), [('B1', 'abc'),
('B2', 10)]
+ sort_content([('B2', 10), ('B1', 'abc')], group),
+ [('B1', 'abc'), ('B2', 10)]
)
self.assertListEqual(
- model.sort_content([('B3', True), ('B2', 10)]), [('B2', 10),
('B3', True)]
+ sort_content([('B3', True), ('B2', 10)], group),
+ [('B2', 10), ('B3', True)]
)
def test_iter_collapsed_content_with_optional_elements(self):
@@ -1074,19 +1070,26 @@
</xs:complexType>
""")
- model = ModelVisitor(schema.types['A_type'].content)
+ group = schema.types['A_type'].content
+ model = ModelVisitor(group)
content = [('B3', 10), ('B4', None), ('B5', True), ('B6', 'alpha'),
('B7', 20)]
model.restart()
self.assertListEqual(
list(model.iter_collapsed_content(content)), content
)
+ self.assertListEqual(
+ list(iter_collapsed_content(content, group)), content
+ )
content = [('B3', 10), ('B5', True), ('B6', 'alpha'), ('B7', 20)] #
Missing B4
model.restart()
self.assertListEqual(
list(model.iter_collapsed_content(content)), content
)
+ self.assertListEqual(
+ list(iter_collapsed_content(content, group)), content
+ )
def test_iter_collapsed_content_with_repeated_elements(self):
schema = self.get_schema("""
@@ -1104,27 +1107,19 @@
</xs:complexType>
""")
- model = ModelVisitor(schema.types['A_type'].content)
+ group = schema.types['A_type'].content
content = [
('B3', 10), ('B4', None), ('B5', True), ('B5', False), ('B6',
'alpha'), ('B7', 20)
]
- self.assertListEqual(
- list(model.iter_collapsed_content(content)), content
- )
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B3', 10), ('B3', 11), ('B3', 12), ('B4', None), ('B5',
True),
('B5', False), ('B6', 'alpha'), ('B7', 20), ('B7', 30)]
- model.restart()
- self.assertListEqual(
- list(model.iter_collapsed_content(content)), content
- )
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B3', 10), ('B3', 11), ('B3', 12), ('B4', None), ('B5',
True), ('B5', False)]
- model.restart()
- self.assertListEqual(
- list(model.iter_collapsed_content(content)), content
- )
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
def test_iter_collapsed_content_with_repeated_groups(self):
schema = self.get_schema("""
@@ -1137,38 +1132,33 @@
</xs:complexType>
""")
- model = ModelVisitor(schema.types['A_type'].content)
+ group = schema.types['A_type'].content
content = [('B1', 1), ('B1', 2), ('B2', 3), ('B2', 4)]
self.assertListEqual(
- list(model.iter_collapsed_content(content)),
+ list(iter_collapsed_content(content, group)),
[('B1', 1), ('B2', 3), ('B1', 2), ('B2', 4)]
)
# Model broken by unknown element at start
content = [('X', None), ('B1', 1), ('B1', 2), ('B2', 3), ('B2', 4)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B1', 1), ('X', None), ('B1', 2), ('B2', 3), ('B2', 4)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B1', 1), ('B1', 2), ('X', None), ('B2', 3), ('B2', 4)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B1', 1), ('B1', 2), ('B2', 3), ('X', None), ('B2', 4)]
- model.restart()
self.assertListEqual(
- list(model.iter_collapsed_content(content)),
+ list(iter_collapsed_content(content, group)),
[('B1', 1), ('B2', 3), ('B1', 2), ('X', None), ('B2', 4)]
)
content = [('B1', 1), ('B1', 2), ('B2', 3), ('B2', 4), ('X', None)]
- model.restart()
self.assertListEqual(
- list(model.iter_collapsed_content(content)),
+ list(iter_collapsed_content(content, group)),
[('B1', 1), ('B2', 3), ('B1', 2), ('B2', 4), ('X', None)]
)
@@ -1184,34 +1174,28 @@
</xs:complexType>
""")
- model = ModelVisitor(schema.types['A_type'].content)
+ group = schema.types['A_type'].content
content = [('B1', 'abc'), ('B2', 10), ('B3', False)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B3', False), ('B1', 'abc'), ('B2', 10)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B1', 'abc'), ('B3', False), ('B2', 10)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('B1', 'abc'), ('B1', 'def'), ('B2', 10), ('B3', False)]
- model.restart()
self.assertListEqual(
- list(model.iter_collapsed_content(content)),
+ list(iter_collapsed_content(content, group)),
[('B1', 'abc'), ('B2', 10), ('B3', False), ('B1', 'def')]
)
content = [('B1', 'abc'), ('B2', 10), ('X', None)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
content = [('X', None), ('B1', 'abc'), ('B2', 10), ('B3', False)]
- model.restart()
- self.assertListEqual(list(model.iter_collapsed_content(content)),
content)
+ self.assertListEqual(list(iter_collapsed_content(content, group)),
content)
class TestModelPaths(unittest.TestCase):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/tox.ini new/xmlschema-2.2.3/tox.ini
--- old/xmlschema-2.2.2/tox.ini 2023-03-05 21:30:24.000000000 +0100
+++ new/xmlschema-2.2.3/tox.ini 2023-04-14 15:49:05.000000000 +0200
@@ -1,6 +1,6 @@
[tox]
-envlist = py{37,38,39,310,311}, pypy3, ep{40}, docs,
- flake8, mypy-py{37,38,39,310,311}, coverage, pytest
+envlist = py{37,38,39,310,311,312,py3}, ep{40}, docs,
+ flake8, mypy-py{37,38,39,310,311,312,py3}, coverage, pytest
skip_missing_interpreters = true
work_dir = {tox_root}/../.tox/xmlschema
@@ -15,10 +15,12 @@
coverage: coverage
commands =
python -m unittest
-allowlist_externals = make
-[testenv:pypy3]
-commands = python -m unittest
+[testenv:py312]
+deps =
+ elementpath>=4.0.0, <5.0.0
+ # lxml: skip for now
+ jinja2
[testenv:ep40]
deps =
@@ -31,6 +33,7 @@
make -C doc latexpdf SPHINXOPTS="-W -n"
make -C doc doctest SPHINXOPTS="-W -n"
sphinx-build -W -n -T -b man doc build/sphinx/man
+allowlist_externals = make
[flake8]
max-line-length = 100
@@ -44,17 +47,17 @@
[testenv:mypy-py37]
deps =
- mypy==1.0.1
- elementpath==4.0.1
+ mypy==1.2.0
+ elementpath==4.1.1
lxml-stubs
jinja2
commands =
mypy --config-file {toxinidir}/mypy.ini xmlschema
-[testenv:mypy-py{38,39,310,311}]
+[testenv:mypy-py{38,39,310,311,312,py3}]
deps =
- mypy==1.0.1
- elementpath==4.0.1
+ mypy==1.2.0
+ elementpath==4.1.1
lxml-stubs
jinja2
commands =
@@ -74,7 +77,7 @@
elementpath>=4.0.0, <5.0.0
lxml
jinja2
- mypy==1.0.1
+ mypy==1.2.0
lxml-stubs
commands =
pytest tests -ra
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/xmlschema/__init__.py
new/xmlschema-2.2.3/xmlschema/__init__.py
--- old/xmlschema-2.2.2/xmlschema/__init__.py 2023-03-05 21:30:24.000000000
+0100
+++ new/xmlschema-2.2.3/xmlschema/__init__.py 2023-04-14 15:49:05.000000000
+0200
@@ -31,7 +31,7 @@
XsdComponent, XsdType, XsdElement, XsdAttribute
)
-__version__ = '2.2.2'
+__version__ = '2.2.3'
__author__ = "Davide Brunato"
__contact__ = "[email protected]"
__copyright__ = "Copyright 2016-2023, SISSA"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/xmlschema/resources.py
new/xmlschema-2.2.3/xmlschema/resources.py
--- old/xmlschema-2.2.2/xmlschema/resources.py 2023-02-11 11:41:50.000000000
+0100
+++ new/xmlschema-2.2.3/xmlschema/resources.py 2023-04-14 15:49:05.000000000
+0200
@@ -9,6 +9,8 @@
#
import copy
import os.path
+import ntpath
+import posixpath
import platform
import re
import string
@@ -45,13 +47,12 @@
A version of pathlib.PurePath adapted for managing the creation
from URIs and the simple normalization of paths.
"""
- _from_parts: Any
- _flavour: Any
+ _path_module = os.path
def __new__(cls, *args: str) -> '_PurePath':
if cls is _PurePath:
cls = _PureWindowsPath if os.name == 'nt' else _PurePosixPath
- return cast('_PurePath', cls._from_parts(args))
+ return super().__new__(cls, *args)
@classmethod
def from_uri(cls, uri: str) -> '_PurePath':
@@ -98,12 +99,21 @@
def as_uri(self) -> str:
if not self.is_absolute():
- uri: str = self._flavour.make_uri(self)
- while uri.startswith('file:/'):
- uri = uri.replace('file:/', 'file:', 1)
- return uri
+ # Converts relative paths to not RFC 8089 compliant relative
+ # file URIs because urlopen() doesn't accept simple paths
+ drive = self.drive
+ if len(drive) == 2 and drive[1] == ':':
+ prefix = 'file:' + drive
+ path = self.as_posix()[2:]
+ elif drive:
+ prefix = 'file:'
+ path = self.as_posix()
+ else:
+ prefix = 'file:'
+ path = str(self)
+ return prefix + quote_from_bytes(os.fsencode(path))
- uri = cast(str, self._flavour.make_uri(self))
+ uri = super().as_uri()
if isinstance(self, _PureWindowsPath) and str(self).startswith(r'\\'):
# UNC format case: use the format where the host part is included
# in the path part, to let urlopen() works.
@@ -112,15 +122,17 @@
return uri
def normalize(self) -> '_PurePath':
- normalized_path = self._flavour.pathmod.normpath(str(self))
- return cast('_PurePath', self._from_parts((normalized_path,)))
+ normalized_path = self._path_module.normpath(str(self))
+ return self.__class__(normalized_path)
class _PurePosixPath(_PurePath, PurePosixPath):
+ _path_module = posixpath
__slots__ = ()
class _PureWindowsPath(_PurePath, PureWindowsPath):
+ _path_module = ntpath
__slots__ = ()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/xmlschema/validators/global_maps.py
new/xmlschema-2.2.3/xmlschema/validators/global_maps.py
--- old/xmlschema-2.2.2/xmlschema/validators/global_maps.py 2023-03-05
21:30:24.000000000 +0100
+++ new/xmlschema-2.2.3/xmlschema/validators/global_maps.py 2023-04-14
15:49:05.000000000 +0200
@@ -31,6 +31,7 @@
XMLSchemaParseError
from .xsdbase import XsdValidator, XsdComponent
from .builtins import xsd_builtin_types_factory
+from .models import check_model
from . import XsdAttribute, XsdSimpleType, XsdComplexType, XsdElement,
XsdAttributeGroup, \
XsdGroup, XsdNotation, XsdIdentity, XsdAssert, XsdUnion,
XsdAtomicRestriction
@@ -731,7 +732,7 @@
_group.parse_error(msg, validation=validation)
try:
- xsd_type.content.check_model()
+ check_model(xsd_type.content)
except XMLSchemaModelDepthError:
msg = _("can't verify the content model of {!r} "
"due to exceeding of maximum recursion depth")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/xmlschema/validators/groups.py
new/xmlschema-2.2.3/xmlschema/validators/groups.py
--- old/xmlschema-2.2.2/xmlschema/validators/groups.py 2023-03-05
21:41:28.000000000 +0100
+++ new/xmlschema-2.2.3/xmlschema/validators/groups.py 2023-04-14
15:49:05.000000000 +0200
@@ -34,7 +34,7 @@
from .particles import ParticleMixin, OccursCalculator
from .elements import XsdElement, XsdAlternative
from .wildcards import XsdAnyElement, Xsd11AnyElement
-from .models import ModelVisitor, distinguishable_paths
+from .models import ModelVisitor, iter_unordered_content,
iter_collapsed_content
if TYPE_CHECKING:
from .complex_types import XsdComplexType
@@ -765,78 +765,6 @@
else:
return other_max_occurs >= max_occurs * self.max_occurs
- def check_model(self) -> None:
- """
- Checks if the model group is deterministic. Element Declarations
Consistent and
- Unique Particle Attribution constraints are checked.
- :raises: an `XMLSchemaModelError` at first violated constraint.
- """
- def safe_iter_path() -> Iterator[SchemaElementType]:
- iterators: List[Iterator[ModelParticleType]] = []
- particles = iter(self)
-
- while True:
- for item in particles:
- if isinstance(item, XsdGroup):
- current_path.append(item)
- iterators.append(particles)
- particles = iter(item)
- if len(iterators) > limits.MAX_MODEL_DEPTH:
- raise XMLSchemaModelDepthError(self)
- break
- else:
- yield item
- else:
- try:
- current_path.pop()
- particles = iterators.pop()
- except IndexError:
- return
-
- paths: Any = {}
- current_path: List[ModelParticleType] = [self]
- try:
- any_element = self.parent.open_content.any_element # type:
ignore[union-attr]
- except AttributeError:
- any_element = None
-
- for e in safe_iter_path():
-
- previous_path: List[ModelParticleType]
- for pe, previous_path in paths.values():
- # EDC check
- if not e.is_consistent(pe) or any_element and not
any_element.is_consistent(pe):
- msg = _("Element Declarations Consistent violation between
{0!r} and {1!r}"
- ": match the same name but with different
types").format(e, pe)
- raise XMLSchemaModelError(self, msg)
-
- # UPA check
- if pe is e or not pe.is_overlap(e):
- continue
- elif pe.parent is e.parent:
- if pe.parent.model in {'all', 'choice'}:
- if isinstance(pe, Xsd11AnyElement) and not
isinstance(e, XsdAnyElement):
- pe.add_precedence(e, self)
- elif isinstance(e, Xsd11AnyElement) and not
isinstance(pe, XsdAnyElement):
- e.add_precedence(pe, self)
- else:
- msg = _("{0!r} and {1!r} overlap and are in the
same {2!r} group")
- raise XMLSchemaModelError(self, msg.format(pe, e,
pe.parent.model))
- elif pe.is_univocal():
- continue
-
- if distinguishable_paths(previous_path + [pe], current_path +
[e]):
- continue
- elif isinstance(pe, Xsd11AnyElement) and not isinstance(e,
XsdAnyElement):
- pe.add_precedence(e, self)
- elif isinstance(e, Xsd11AnyElement) and not isinstance(pe,
XsdAnyElement):
- e.add_precedence(pe, self)
- else:
- msg = _("Unique Particle Attribution violation between
{0!r} and {1!r}")
- raise XMLSchemaModelError(self, msg.format(pe, e))
-
- paths[e.name] = e, current_path[:]
-
def check_dynamic_context(self, elem: ElementType,
xsd_element: SchemaElementType,
model_element: SchemaElementType,
@@ -1137,9 +1065,7 @@
if not obj.content:
content = []
elif isinstance(obj.content, MutableMapping) or
kwargs.get('unordered'):
- content = ModelVisitor(self).iter_unordered_content(
- obj.content, default_namespace
- )
+ content = iter_unordered_content(obj.content, self,
default_namespace)
elif not isinstance(obj.content, MutableSequence):
wrong_content_type = True
content = []
@@ -1152,9 +1078,7 @@
elif converter.losslessly:
content = obj.content
else:
- content = ModelVisitor(self).iter_collapsed_content(
- obj.content, default_namespace
- )
+ content = iter_collapsed_content(obj.content, self,
default_namespace)
for index, (name, value) in enumerate(content):
if isinstance(name, int):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/xmlschema/validators/models.py
new/xmlschema-2.2.3/xmlschema/validators/models.py
--- old/xmlschema-2.2.2/xmlschema/validators/models.py 2022-06-23
22:06:57.000000000 +0200
+++ new/xmlschema-2.2.3/xmlschema/validators/models.py 2023-04-14
15:49:05.000000000 +0200
@@ -8,19 +8,24 @@
# @author Davide Brunato <[email protected]>
#
"""
-This module contains a function and a class for validating XSD content models.
+This module contains a function and a class for validating XSD content models,
+plus a set of functions for manipulating encoded content.
"""
from collections import defaultdict, deque
-from typing import Any, Counter, Dict, Iterable, Iterator, List, Optional,
Tuple, Union
+from typing import Any, Counter, Dict, Iterable, Iterator, List, \
+ MutableMapping, MutableSequence, Optional, Tuple, Union
from ..exceptions import XMLSchemaValueError
from ..aliases import ModelGroupType, ModelParticleType, SchemaElementType
+from ..translation import gettext as _
+from .. import limits
+from .exceptions import XMLSchemaModelError, XMLSchemaModelDepthError
+from .wildcards import XsdAnyElement, Xsd11AnyElement
from . import groups
AdvanceYieldedType = Tuple[ModelParticleType, int, List[SchemaElementType]]
-EncodedContentType = Union[Dict[Union[int, str], List[Any]],
- List[Tuple[Union[int, str], List[Any]]]]
ContentItemType = Tuple[Union[int, str], Any]
+EncodedContentType = Union[MutableMapping[Union[int, str], Any],
Iterable[ContentItemType]]
def distinguishable_paths(path1: List[ModelParticleType], path2:
List[ModelParticleType]) -> bool:
@@ -88,6 +93,81 @@
(before1 or (before2 or univocal2) and (path2[-1].is_univocal()
or after2))
+def check_model(group: ModelGroupType) -> None:
+ """
+ Checks if the model group is deterministic. Element Declarations
Consistent and
+ Unique Particle Attribution constraints are checked.
+
+ :param group: the model group to check.
+ :raises: an `XMLSchemaModelError` at first violated constraint.
+ """
+ def safe_iter_path() -> Iterator[SchemaElementType]:
+ iterators: List[Iterator[ModelParticleType]] = []
+ particles = iter(group)
+
+ while True:
+ for item in particles:
+ if isinstance(item, groups.XsdGroup):
+ current_path.append(item)
+ iterators.append(particles)
+ particles = iter(item)
+ if len(iterators) > limits.MAX_MODEL_DEPTH:
+ raise XMLSchemaModelDepthError(group)
+ break
+ else:
+ yield item
+ else:
+ try:
+ current_path.pop()
+ particles = iterators.pop()
+ except IndexError:
+ return
+
+ paths: Any = {}
+ current_path: List[ModelParticleType] = [group]
+ try:
+ any_element = group.parent.open_content.any_element # type:
ignore[union-attr]
+ except AttributeError:
+ any_element = None
+
+ for e in safe_iter_path():
+
+ previous_path: List[ModelParticleType]
+ for pe, previous_path in paths.values():
+ # EDC check
+ if not e.is_consistent(pe) or any_element and not
any_element.is_consistent(pe):
+ msg = _("Element Declarations Consistent violation between
{0!r} and {1!r}"
+ ": match the same name but with different
types").format(e, pe)
+ raise XMLSchemaModelError(group, msg)
+
+ # UPA check
+ if pe is e or not pe.is_overlap(e):
+ continue
+ elif pe.parent is e.parent:
+ if pe.parent.model in {'all', 'choice'}:
+ if isinstance(pe, Xsd11AnyElement) and not isinstance(e,
XsdAnyElement):
+ pe.add_precedence(e, group)
+ elif isinstance(e, Xsd11AnyElement) and not isinstance(pe,
XsdAnyElement):
+ e.add_precedence(pe, group)
+ else:
+ msg = _("{0!r} and {1!r} overlap and are in the same
{2!r} group")
+ raise XMLSchemaModelError(group, msg.format(pe, e,
pe.parent.model))
+ elif pe.is_univocal():
+ continue
+
+ if distinguishable_paths(previous_path + [pe], current_path + [e]):
+ continue
+ elif isinstance(pe, Xsd11AnyElement) and not isinstance(e,
XsdAnyElement):
+ pe.add_precedence(e, group)
+ elif isinstance(e, Xsd11AnyElement) and not isinstance(pe,
XsdAnyElement):
+ e.add_precedence(pe, group)
+ else:
+ msg = _("Unique Particle Attribution violation between {0!r}
and {1!r}")
+ raise XMLSchemaModelError(group, msg.format(pe, e))
+
+ paths[e.name] = e, current_path[:]
+
+
class ModelVisitor:
"""
A visitor design pattern class that can be used for validating XML data
related to an XSD
@@ -105,6 +185,8 @@
_groups: List[Tuple[ModelGroupType, Iterator[ModelParticleType], bool]]
element: Optional[SchemaElementType]
+ __slots__ = '_groups', 'root', 'occurs', 'element', 'group', 'items',
'match'
+
def __init__(self, root: ModelGroupType) -> None:
self._groups = []
self.root = root
@@ -246,7 +328,7 @@
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
+ occurs[self.group] += 1
break
min_group_occurs = max(1, item_occurs // (item2.max_occurs
or item_occurs))
@@ -258,25 +340,25 @@
return item.is_missing(max(occurs[item], occurs[(item,)]))
- element, occurs = self.element, self.occurs
- if element is None:
+ occurs = self.occurs
+ if self.element is None:
raise XMLSchemaValueError("cannot advance, %r is ended!" % self)
if match:
- occurs[element] += 1
+ occurs[self.element] += 1
self.match = True
if self.group.model == 'all':
self.items = (e for e in self.group.iter_elements() if not
e.is_over(occurs[e]))
- elif not element.is_over(occurs[element]):
+ elif not self.element.is_over(occurs[self.element]):
return
- elif self.group.model == 'choice' and element.is_ambiguous():
+ elif self.group.model == 'choice' and self.element.is_ambiguous():
return
obj = None
try:
- element_occurs = occurs[element]
- if stop_item(element):
- yield element, element_occurs, [element]
+ element_occurs = occurs[self.element]
+ if stop_item(self.element):
+ yield self.element, element_occurs, [self.element]
while True:
while self.group.is_over(max(occurs[self.group],
occurs[(self.group,)])):
@@ -333,131 +415,156 @@
elif self.group.max_occurs is not None and self.group.max_occurs <
occurs[self.group]:
yield self.group, occurs[self.group], self.expected
- def sort_content(self, content: EncodedContentType, restart: bool = True) \
- -> List[ContentItemType]:
- if restart:
- self.restart()
- return [(name, value) for name, value in
self.iter_unordered_content(content)]
-
+ # Kept for backward compatibility
def iter_unordered_content(
self, content: EncodedContentType,
default_namespace: Optional[str] = None) ->
Iterator[ContentItemType]:
- """
- Takes an unordered content stored in a dictionary of lists and yields
the
- content elements sorted with the ordering defined by the model.
Character
- data parts are yielded at start and between child elements.
-
- Ordering is inferred from ModelVisitor instance with any elements that
- don't fit the schema placed at the end of the returned sequence.
Checking
- the yielded content validity is the responsibility of method
*iter_encode*
- of class :class:`XsdGroup`.
-
- :param content: a dictionary of element names to list of element
contents \
- or an iterable composed of couples of name and value. In case of a \
- dictionary the values must be lists where each item is the content \
- of a single element.
- :param default_namespace: the default namespace to apply for matching
names.
- """
- consumable_content: Dict[str, Any]
+ return iter_unordered_content(content, self.root, default_namespace)
- if isinstance(content, dict):
- cdata_content = sorted(
- ((k, v) for k, v in content.items() if isinstance(k, int)),
reverse=True
- )
- consumable_content = {k: deque(v) for k, v in content.items() if
not isinstance(k, int)}
- else:
- cdata_content = sorted(((k, v) for k, v in content if
isinstance(k, int)), reverse=True)
- consumable_content = defaultdict(deque)
- for k, v in content:
- if isinstance(k, str):
- consumable_content[k].append(v)
-
- if cdata_content:
- yield cdata_content.pop()
-
- while self.element is not None and consumable_content: # pragma: no
cover
- for name in consumable_content:
- if self.element.is_matching(name, default_namespace,
group=self.group):
- yield name, consumable_content[name].popleft()
- if not consumable_content[name]:
- del consumable_content[name]
- for _ in self.advance(True):
- pass
- if cdata_content:
- yield cdata_content.pop()
- break
- else:
- # Consume the return of advance otherwise we get stuck in an
infinite loop.
- for _ in self.advance(False):
- pass
+ def iter_collapsed_content(
+ self, content: Iterable[ContentItemType],
+ default_namespace: Optional[str] = None) ->
Iterator[ContentItemType]:
+ return iter_collapsed_content(content, self.root, default_namespace)
- # Add the remaining consumable content onto the end of the data.
- for name, values in consumable_content.items():
- for v in values:
- yield name, v
+
+#
+# Functions for manipulating encoded content
+
+def iter_unordered_content(
+ content: EncodedContentType,
+ group: ModelGroupType,
+ default_namespace: Optional[str] = None) -> Iterator[ContentItemType]:
+ """
+ Takes an unordered content stored in a dictionary of lists and yields the
+ content elements sorted with the ordering defined by the model group.
Character
+ data parts are yielded at start and between child elements.
+
+ Ordering is inferred from ModelVisitor instance with any elements that
+ don't fit the schema placed at the end of the returned sequence. Checking
+ the yielded content validity is the responsibility of method *iter_encode*
+ of class :class:`XsdGroup`.
+
+ :param content: a dictionary of element names to list of element contents \
+ or an iterable composed of couples of name and value. In case of a \
+ dictionary the values must be lists where each item is the content \
+ of a single element.
+ :param group: the model group related to content.
+ :param default_namespace: the default namespace to apply for matching
names.
+ """
+ consumable_content: Dict[str, Any]
+
+ if isinstance(content, MutableMapping):
+ cdata_content = sorted(
+ ((k, v) for k, v in content.items() if isinstance(k, int)),
reverse=True
+ )
+ consumable_content = {
+ k: deque(v) if isinstance(v, MutableSequence) else deque([v])
+ for k, v in content.items() if not isinstance(k, int)
+ }
+ else:
+ cdata_content = sorted(((k, v) for k, v in content if isinstance(k,
int)), reverse=True)
+ consumable_content = defaultdict(deque)
+ for k, v in content:
+ if isinstance(k, str):
+ consumable_content[k].append(v)
+
+ if cdata_content:
+ yield cdata_content.pop()
+
+ model = ModelVisitor(group)
+ while model.element is not None and consumable_content: # pragma: no cover
+ for name in consumable_content:
+ if model.element.is_matching(name, default_namespace, group=group):
+ yield name, consumable_content[name].popleft()
+ if not consumable_content[name]:
+ del consumable_content[name]
+ for _err in model.advance(True):
+ pass
if cdata_content:
yield cdata_content.pop()
+ break
+ else:
+ # Consume the return of advance otherwise we get stuck in an
infinite loop.
+ for _err in model.advance(False):
+ pass
- while cdata_content:
- yield cdata_content.pop()
-
- def iter_collapsed_content(
- self, content: Iterable[Tuple[Union[int, str], Any]],
- default_namespace: Optional[str] = None) ->
Iterator[ContentItemType]:
- """
- Iterates a content stored in a sequence of couples *(name, value)*,
yielding
- items in the same order of the sequence, except for repetitions of the
same
- tag that don't match with the current element of the
:class:`ModelVisitor`
- instance. These items are included in an unsorted buffer and yielded
asap
- when there is a match with the model's element or at the end of the
iteration.
-
- This iteration mode, in cooperation with the method *iter_encode* of
the class
- XsdGroup, facilitates the encoding of content formatted with a
convention that
- collapses the children with the same tag into a list (eg. BadgerFish).
+ # Add the remaining consumable content onto the end of the data.
+ for name, values in consumable_content.items():
+ for v in values:
+ yield name, v
+ if cdata_content:
+ yield cdata_content.pop()
+
+ while cdata_content:
+ yield cdata_content.pop()
+
+
+def sort_content(content: EncodedContentType,
+ group: ModelGroupType,
+ default_namespace: Optional[str] = None) ->
List[ContentItemType]:
+ return [x for x in iter_unordered_content(content, group,
default_namespace)]
+
+
+def iter_collapsed_content(
+ content: Iterable[ContentItemType],
+ group: ModelGroupType,
+ default_namespace: Optional[str] = None) -> Iterator[ContentItemType]:
+ """
+ Iterates a content stored in a sequence of couples *(name, value)*,
yielding
+ items in the same order of the sequence, except for repetitions of the same
+ tag that don't match with the current element of the :class:`ModelVisitor`
+ instance. These items are included in an unsorted buffer and yielded asap
+ when there is a match with the model's element or at the end of the
iteration.
+
+ This iteration mode, in cooperation with the method *iter_encode* of the
class
+ XsdGroup, facilitates the encoding of content formatted with a convention
that
+ collapses the children with the same tag into a list (eg. BadgerFish).
+
+ :param content: an iterable containing couples of names and values.
+ :param group: the model group related to content.
+ :param default_namespace: the default namespace to apply for matching
names.
+ """
+ prev_name = None
+ unordered_content: Dict[str, Any] = defaultdict(deque)
- :param content: an iterable containing couples of names and values.
- :param default_namespace: the default namespace to apply for matching
names.
- """
- prev_name = None
- unordered_content: Dict[str, Any] = defaultdict(deque)
+ model = ModelVisitor(group)
+ for name, value in content:
+ if isinstance(name, int) or model.element is None:
+ yield name, value
+ continue
- for name, value in content:
- if isinstance(name, int) or self.element is None:
+ while model.element is not None:
+ if model.element.is_matching(name, default_namespace, group=group):
yield name, value
- continue
+ prev_name = name
+ for _err in model.advance(True):
+ pass
+ break
- while self.element is not None:
- if self.element.is_matching(name, default_namespace,
group=self.group):
- yield name, value
- prev_name = name
- for _ in self.advance(True):
- pass
+ for key in unordered_content:
+ if model.element.is_matching(key, default_namespace,
group=group):
+ break
+ else:
+ if prev_name == name:
+ unordered_content[name].append(value)
break
- for key in unordered_content:
- if self.element.is_matching(key, default_namespace,
group=self.group):
- break
- else:
- if prev_name == name:
- unordered_content[name].append(value)
- break
-
- for _ in self.advance(False):
- pass
- continue
+ for _err in model.advance(False):
+ pass
+ continue
- try:
- yield key, unordered_content[key].popleft()
- except IndexError:
- del unordered_content[key]
- else:
- for _ in self.advance(True):
- pass
+ try:
+ yield key, unordered_content[key].popleft()
+ except IndexError:
+ del unordered_content[key]
else:
- yield name, value
- prev_name = name
+ for _err in model.advance(True):
+ pass
+ else:
+ yield name, value
+ prev_name = name
- # Add the remaining consumable content onto the end of the data.
- for name, values in unordered_content.items():
- for v in values:
- yield name, v
+ # Add the remaining consumable content onto the end of the data.
+ for name, values in unordered_content.items():
+ for v in values:
+ yield name, v
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/xmlschema.egg-info/PKG-INFO
new/xmlschema-2.2.3/xmlschema.egg-info/PKG-INFO
--- old/xmlschema-2.2.2/xmlschema.egg-info/PKG-INFO 2023-03-05
22:10:04.000000000 +0100
+++ new/xmlschema-2.2.3/xmlschema.egg-info/PKG-INFO 2023-04-14
15:52:42.000000000 +0200
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: xmlschema
-Version: 2.2.2
+Version: 2.2.3
Summary: An XML Schema validator and decoder
Home-page: https://github.com/sissaschool/xmlschema
Author: Davide Brunato
@@ -20,6 +20,7 @@
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Software Development :: Libraries
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/xmlschema-2.2.2/xmlschema.egg-info/SOURCES.txt
new/xmlschema-2.2.3/xmlschema.egg-info/SOURCES.txt
--- old/xmlschema-2.2.2/xmlschema.egg-info/SOURCES.txt 2023-03-05
22:10:04.000000000 +0100
+++ new/xmlschema-2.2.3/xmlschema.egg-info/SOURCES.txt 2023-04-14
15:52:42.000000000 +0200
@@ -274,6 +274,9 @@
tests/test_cases/issues/issue_334/issue_334.xml
tests/test_cases/issues/issue_334/issue_334.xsd
tests/test_cases/issues/issue_334/issue_334.zip
+tests/test_cases/issues/issue_341/issue_341-ext.xsd
+tests/test_cases/issues/issue_341/issue_341.xml
+tests/test_cases/issues/issue_341/issue_341.xsd
tests/test_cases/mypy/extra_validator.py
tests/test_cases/mypy/schema_source.py
tests/test_cases/mypy/simple_types.py