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 <dmuel...@suse.com>
+
+- 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__ = "brun...@sissa.it"
 __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 <brun...@sissa.it>
 #
 """
-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

Reply via email to