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-02-11 22:20:05
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-xmlschema (Old)
 and      /work/SRC/openSUSE:Factory/.python-xmlschema.new.1848 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-xmlschema"

Sat Feb 11 22:20:05 2023 rev:19 rq:1064241 version:2.2.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-xmlschema/python-xmlschema.changes        
2022-10-04 20:37:17.748879628 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-xmlschema.new.1848/python-xmlschema.changes  
    2023-02-11 22:20:07.168048762 +0100
@@ -1,0 +2,11 @@
+Fri Feb 10 12:46:13 UTC 2023 - Dirk Müller <dmuel...@suse.com>
+
+- update to 2.2.0:
+  * Refine string serialization of XML resources and data
+    elements
+  * Switch to use elementpath v4
+  * Fix sequence_type property for XSD types
+  * Remove *XsdElement.get_attribute()*: unused and doesn't work
+    as expected
+
+-------------------------------------------------------------------

Old:
----
  xmlschema-2.1.1.tar.gz

New:
----
  xmlschema-2.2.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-xmlschema.spec ++++++
--- /var/tmp/diff_new_pack.vp5zlv/_old  2023-02-11 22:20:07.740052262 +0100
+++ /var/tmp/diff_new_pack.vp5zlv/_new  2023-02-11 22:20:07.756052360 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package python-xmlschema
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -19,7 +19,7 @@
 %{?!python_module:%define python_module() python-%{**} python3-%{**}}
 %define skip_python2 1
 Name:           python-xmlschema
-Version:        2.1.1
+Version:        2.2.0
 Release:        0
 Summary:        An XML Schema validator and decoder
 License:        MIT

++++++ xmlschema-2.1.1.tar.gz -> xmlschema-2.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/CHANGELOG.rst 
new/xmlschema-2.2.0/CHANGELOG.rst
--- old/xmlschema-2.1.1/CHANGELOG.rst   2022-10-01 15:42:01.000000000 +0200
+++ new/xmlschema-2.2.0/CHANGELOG.rst   2023-02-06 07:20:42.000000000 +0100
@@ -2,6 +2,13 @@
 CHANGELOG
 *********
 
+`v2.2.0`_ (2023-02-06)
+======================
+* Refine string serialization of XML resources and data elements
+* Switch to use elementpath v4
+* Fix sequence_type property for XSD types
+* Remove *XsdElement.get_attribute()*: unused and doesn't work as expected
+
 `v2.1.1`_ (2022-10-01)
 ======================
 * Fix *schema_path* usage in `XMLSchemaBase.iter_errors()`
@@ -591,3 +598,4 @@
 .. _v2.0.4: https://github.com/brunato/xmlschema/compare/v2.0.3...v2.0.4
 .. _v2.1.0: https://github.com/brunato/xmlschema/compare/v2.0.4...v2.1.0
 .. _v2.1.1: https://github.com/brunato/xmlschema/compare/v2.1.0...v2.1.1
+.. _v2.2.0: https://github.com/brunato/xmlschema/compare/v2.1.1...v2.2.0
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/PKG-INFO new/xmlschema-2.2.0/PKG-INFO
--- old/xmlschema-2.1.1/PKG-INFO        2022-10-01 19:02:18.985521600 +0200
+++ new/xmlschema-2.2.0/PKG-INFO        2023-02-06 10:06:40.856029000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: xmlschema
-Version: 2.1.1
+Version: 2.2.0
 Summary: An XML Schema validator and decoder
 Home-page: https://github.com/sissaschool/xmlschema
 Author: Davide Brunato
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/doc/conf.py 
new/xmlschema-2.2.0/doc/conf.py
--- old/xmlschema-2.1.1/doc/conf.py     2022-10-01 15:42:01.000000000 +0200
+++ new/xmlschema-2.2.0/doc/conf.py     2023-02-06 07:20:42.000000000 +0100
@@ -79,9 +79,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '2.1'
+version = '2.2'
 # The full version, including alpha/beta/rc tags.
-release = '2.1.1'
+release = '2.2.0'
 
 # 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.1.1/requirements-dev.txt 
new/xmlschema-2.2.0/requirements-dev.txt
--- old/xmlschema-2.1.1/requirements-dev.txt    2022-07-18 16:19:15.000000000 
+0200
+++ new/xmlschema-2.2.0/requirements-dev.txt    2023-02-06 07:20:42.000000000 
+0100
@@ -1,8 +1,8 @@
 # Requirements for setup a development environment for the xmlschema package.
 setuptools
-tox
+tox>=4.0
 coverage
-elementpath>=3.0.0, <4.0.0
+elementpath>=4.0.0, <5.0.0
 lxml
 jinja2
 memory_profiler
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/setup.py new/xmlschema-2.2.0/setup.py
--- old/xmlschema-2.1.1/setup.py        2022-10-01 15:42:01.000000000 +0200
+++ new/xmlschema-2.2.0/setup.py        2023-02-06 07:20:42.000000000 +0100
@@ -18,7 +18,7 @@
 
 setup(
     name='xmlschema',
-    version='2.1.1',
+    version='2.2.0',
     packages=find_packages(include=['xmlschema*']),
     package_data={
         'xmlschema': ['py.typed', 'locale/**/*.mo', 'locale/**/*.po', 
'schemas/*/*.xsd'],
@@ -32,13 +32,13 @@
         ]
     },
     python_requires='>=3.7',
-    install_requires=['elementpath>=3.0.0, <4.0.0'],
+    install_requires=['elementpath>=4.0.0, <5.0.0'],
     extras_require={
-        'codegen': ['elementpath>=3.0.0, <4.0.0', 'jinja2'],
-        'dev': ['tox', 'coverage', 'lxml', 'elementpath>=3.0.0, <4.0.0',
+        'codegen': ['elementpath>=4.0.0, <5.0.0', 'jinja2'],
+        'dev': ['tox', 'coverage', 'lxml', 'elementpath>=4.0.0, <5.0.0',
                 'memory_profiler', 'Sphinx', 'sphinx_rtd_theme', 'jinja2',
                 'flake8', 'mypy', 'lxml-stubs'],
-        'docs': ['elementpath>=3.0.0, <4.0.0', 'Sphinx', 'sphinx_rtd_theme', 
'jinja2']
+        'docs': ['elementpath>=4.0.0, <5.0.0', 'Sphinx', 'sphinx_rtd_theme', 
'jinja2']
     },
     author='Davide Brunato',
     author_email='brun...@sissa.it',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/tests/test_dataobjects.py 
new/xmlschema-2.2.0/tests/test_dataobjects.py
--- old/xmlschema-2.1.1/tests/test_dataobjects.py       2022-07-21 
11:40:50.000000000 +0200
+++ new/xmlschema-2.2.0/tests/test_dataobjects.py       2023-02-06 
09:55:27.000000000 +0100
@@ -14,8 +14,10 @@
 from typing import Dict
 
 from xmlschema import XMLSchema10, XMLSchema11, fetch_namespaces, 
etree_tostring, \
-    XMLSchemaValidationError, DataElement, DataElementConverter, XMLResource
+    XMLSchemaValidationError, DataElement, DataElementConverter, XMLResource, \
+    XsdElement, XsdAttribute, XsdType
 
+from xmlschema.validators import XsdAttributeGroup
 from xmlschema.helpers import is_etree_element
 from xmlschema.names import XSI_TYPE
 from xmlschema.dataobjects import DataBindingMeta, DataBindingConverter
@@ -45,10 +47,11 @@
         self.assertIsNone(data_element.get('a'))
         self.assertEqual(data_element.get('b'), 9)
 
-    def test_namespaces(self):
+    def test_nsmap(self):
         self.assertEqual(DataElement('foo').nsmap, {})
         nsmap = {'tns': 'http://xmlschema.test/ns'}
         self.assertEqual(DataElement('foo', nsmap=nsmap).nsmap, nsmap)
+        self.assertIsNot(DataElement('foo', nsmap=nsmap).nsmap, nsmap)
 
     def test_attributes_with_namespaces(self):
         nsmap = {'tns': 'http://xmlschema.test/ns'}
@@ -265,12 +268,72 @@
         self.assertIsInstance(etree_tostring(obj), str)
         self.assertIsNone(etree_elements_assert_equal(obj, root, strict=False))
 
+    def test_collapsed_namespace_map(self):
+        col_data = self.col_schema.decode(self.col_xml_filename)
+
+        namespaces = col_data.get_namespaces()
+        self.assertDictEqual(
+            namespaces,
+            {'col': 'http://example.com/ns/collection',
+             'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
+        )
+
+        namespaces = col_data.get_namespaces({'': 'http://xmlschema.test/ns'})
+        self.assertDictEqual(
+            namespaces,
+            {'': 'http://xmlschema.test/ns',
+             'col': 'http://example.com/ns/collection',
+             'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
+        )
+
+        namespaces = col_data.get_namespaces({'tns': 
'http://xmlschema.test/ns'})
+        self.assertDictEqual(
+            namespaces,
+            {'tns': 'http://xmlschema.test/ns',
+             'col': 'http://example.com/ns/collection',
+             'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
+        )
+
+        namespaces = col_data.get_namespaces({'xsi': 
'http://xmlschema.test/ns'})
+        self.assertDictEqual(
+            namespaces,
+            {'xsi': 'http://xmlschema.test/ns',
+             'col': 'http://example.com/ns/collection',
+             'xsi0': 'http://www.w3.org/2001/XMLSchema-instance'}
+        )
+
+        xsd_filename = self.casepath('examples/collection/collection5.xsd')
+        col_schema = self.schema_class(xsd_filename, converter=self.converter)
+        xml_filename = 
self.casepath('examples/collection/collection-default.xml')
+        col_data = col_schema.decode(xml_filename)
+
+        namespaces = col_data.get_namespaces()
+        self.assertDictEqual(
+            namespaces,
+            {'': 'http://example.com/ns/collection',
+             'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
+        )
+
+        namespaces = col_data.get_namespaces({'': 'http://xmlschema.test/ns'})
+        self.assertDictEqual(
+            namespaces,
+            {'': 'http://xmlschema.test/ns',
+             'default': 'http://example.com/ns/collection',
+             'xsi': 'http://www.w3.org/2001/XMLSchema-instance'}
+        )
+
     def test_serialize_to_xml_source(self):
         col_data = self.col_schema.decode(self.col_xml_filename)
 
-        xml_source = col_data.tostring()
-        self.assertTrue(xml_source.startswith('<col:collection '))
-        self.assertTrue(xml_source.endswith('</col:collection>'))
+        with Path(self.col_xml_filename).open() as fp:
+            _ = fp.read()
+
+        result = col_data.tostring()
+        self.assertTrue(result.startswith('<col:collection '))
+        self.assertTrue(result.endswith('</col:collection>'))
+
+        result = col_data.tostring(xml_declaration=True)
+        self.assertTrue(self.col_schema.is_valid(result))
 
     def test_validation(self):
         with self.assertRaises(ValueError) as ec:
@@ -433,6 +496,21 @@
         self.assertEqual(data_element[0][0].get('b:type'), 
'p:ConcreteContainterItemInfo')
         self.assertIsNone(data_element[0][0].get('xsi:type'))
 
+    def test_xsd_attribute_access__issue_331(self):
+        col_data = self.col_schema.decode(self.col_xml_filename)
+        self.assertIsInstance(col_data[0].xsd_element, XsdElement)
+        self.assertEqual(col_data[0].xsd_element.name, 'object')
+        self.assertIsInstance(
+            col_data[0].xsd_element.attributes, XsdAttributeGroup
+        )
+        xsd_attribute = col_data[0].xsd_element.attributes.get('id')
+        self.assertIsInstance(xsd_attribute, XsdAttribute)
+
+        xsd_attribute = col_data[0].xsd_element.find('@id')
+        self.assertIsInstance(xsd_attribute, XsdAttribute)
+        self.assertIsInstance(xsd_attribute.type, XsdType)
+        self.assertIsNone(col_data[0].xsd_element.find('@unknown'))
+
 
 class TestDataBindings(TestDataObjects):
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/tests/test_documents.py 
new/xmlschema-2.2.0/tests/test_documents.py
--- old/xmlschema-2.1.1/tests/test_documents.py 2022-09-25 09:58:42.000000000 
+0200
+++ new/xmlschema-2.2.0/tests/test_documents.py 2023-02-06 07:20:42.000000000 
+0100
@@ -458,7 +458,7 @@
             xml_document = XmlDocument(self.col_xml_file, lazy=True)
             with self.assertRaises(XMLResourceError) as ctx:
                 xml_document.write(str(col_file_path))
-            self.assertEqual(str(ctx.exception), "cannot serialize a lazy XML 
document")
+            self.assertEqual(str(ctx.exception), "cannot serialize a lazy XML 
resource")
 
     def test_xml_document_etree_interface(self):
         xml_document = XmlDocument(self.vh_xml_file)
@@ -471,7 +471,8 @@
         xml_document = XmlDocument(self.vh_xml_file, lazy=1)
         with self.assertRaises(XMLResourceError) as ctx:
             xml_document.get_etree_document()
-        self.assertIn('cannot create an ElementTree from a lazy resource', 
str(ctx.exception))
+        self.assertIn('cannot create an ElementTree instance from a lazy XML 
resource',
+                      str(ctx.exception))
 
         vh_tree = ElementTree.parse(self.vh_xml_file)
         xml_document = XmlDocument(vh_tree, base_url=self.vh_dir)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/tests/test_resources.py 
new/xmlschema-2.2.0/tests/test_resources.py
--- old/xmlschema-2.1.1/tests/test_resources.py 2022-07-18 16:19:15.000000000 
+0200
+++ new/xmlschema-2.2.0/tests/test_resources.py 2023-02-06 07:20:42.000000000 
+0100
@@ -43,6 +43,10 @@
 
 DRIVE_REGEX = '/[a-zA-Z]:' if platform.system() == 'Windows' else ''
 
+XML_WITH_NAMESPACES = '<pfa:root xmlns:pfa="http://xmlschema.test/nsa";>\n' \
+                      '  <pfb:elem xmlns:pfb="http://xmlschema.test/nsb"/>\n' \
+                      '</pfa:root>'
+
 
 def casepath(relative_path):
     return str(pathlib.Path(TEST_CASES_DIR).joinpath(relative_path))
@@ -456,7 +460,7 @@
         self.assertIsNone(resource.text)
         with self.assertRaises(XMLResourceError) as ctx:
             resource.load()
-        self.assertIn('cannot load a lazy resource', str(ctx.exception))
+        self.assertIn('cannot load a lazy XML resource', str(ctx.exception))
         self.assertIsNone(resource.text)
 
         resource = XMLResource(self.vh_xml_file, lazy=False)
@@ -492,7 +496,7 @@
         self.assertIsNone(resource.text)
         with self.assertRaises(XMLResourceError) as ctx:
             resource.load()
-        self.assertIn('cannot load a lazy resource', str(ctx.exception))
+        self.assertIn('cannot load a lazy XML resource', str(ctx.exception))
         self.assertIsNone(resource.text)
 
         resource = XMLResource(path, lazy=False)
@@ -605,7 +609,7 @@
 
             with self.assertRaises(XMLResourceError) as ctx:
                 resource.load()
-            self.assertEqual("cannot load a lazy resource", str(ctx.exception))
+            self.assertEqual("cannot load a lazy XML resource", 
str(ctx.exception))
 
             self.assertFalse(schema_file.closed)
             for _ in resource.iter():
@@ -962,7 +966,24 @@
         resource = XMLResource(self.vh_xml_file, lazy=True)
         with self.assertRaises(XMLResourceError) as ctx:
             resource.tostring()
-        self.assertEqual("cannot serialize a lazy resource", 
str(ctx.exception))
+        self.assertEqual("cannot serialize a lazy XML resource", 
str(ctx.exception))
+
+        resource = XMLResource(XML_WITH_NAMESPACES)
+        result = resource.tostring()
+        self.assertNotEqual(result, XML_WITH_NAMESPACES)
+
+        # With xml.etree.ElementTree namespace declarations are serialized
+        # with a loss of information (all collapsed into the root element).
+        self.assertEqual(result, '<pfa:root 
xmlns:pfa="http://xmlschema.test/nsa"; '
+                                 'xmlns:pfb="http://xmlschema.test/nsb";>\n'
+                                 '  <pfb:elem />\n</pfa:root>')
+
+        if lxml_etree is not None:
+            root = lxml_etree.XML(XML_WITH_NAMESPACES)
+            resource = XMLResource(root)
+
+            # With lxml.etree there is no information loss.
+            self.assertEqual(resource.tostring(), XML_WITH_NAMESPACES)
 
     def test_xml_resource_open(self):
         resource = XMLResource(self.vh_xml_file)
@@ -1459,7 +1480,8 @@
         resource = XMLResource(StringIO('<a><b1><c1/><c2/></b1><b2/></a>'), 
lazy=True)
         with self.assertRaises(XMLResourceError) as ctx:
             _ = resource.parent_map
-        self.assertEqual("cannot create the parent map of a lazy resource", 
str(ctx.exception))
+        self.assertEqual("cannot create the parent map of a lazy XML resource",
+                         str(ctx.exception))
 
     def test_get_nsmap(self):
         source = '<a xmlns="uri1"><b1 xmlns:x="uri2"><c1/><c2/></b1><b2 
xmlns="uri3"/></a>'
@@ -1502,7 +1524,8 @@
         resource = XMLResource(self.vh_xml_file, lazy=True)
         with self.assertRaises(XMLResourceError) as ctx:
             resource.subresource(resource.root)
-        self.assertEqual("cannot create a subresource from a lazy resource", 
str(ctx.exception))
+        self.assertEqual("cannot create a subresource from a lazy XML 
resource",
+                         str(ctx.exception))
 
         resource = XMLResource(self.vh_xml_file)
         root = resource.root
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/tests/validation/test_decoding.py 
new/xmlschema-2.2.0/tests/validation/test_decoding.py
--- old/xmlschema-2.1.1/tests/validation/test_decoding.py       2022-08-26 
17:33:28.000000000 +0200
+++ new/xmlschema-2.2.0/tests/validation/test_decoding.py       2023-02-06 
07:20:42.000000000 +0100
@@ -866,8 +866,8 @@
         self.assertIsInstance(obj, str)
 
         obj = xs.decode('<hex> 9AFD </hex>', binary_types=True)
-        self.assertEqual(obj, '9AFD')
         self.assertIsInstance(obj, datatypes.HexBinary)
+        self.assertEqual(obj.value, b'9AFD')
 
         xs = self.get_schema('<xs:attribute name="hex" type="xs:hexBinary"/>')
 
@@ -876,8 +876,8 @@
         self.assertIsInstance(obj, str)
 
         obj = xs.attributes['hex'].decode(' 9AFD ', binary_types=True)
-        self.assertEqual(obj, '9AFD')
         self.assertIsInstance(obj, datatypes.HexBinary)
+        self.assertEqual(obj.value, b'9AFD')
 
     def test_base64_binary_type(self):
         base64_code_type = self.st_schema.types['base64Code']
@@ -891,7 +891,7 @@
         xs = self.get_schema('<xs:attribute name="b64" 
type="xs:base64Binary"/>')
 
         obj = xs.attributes['b64'].decode(base64_value.decode())
-        self.assertEqual(obj, expected_value)
+        self.assertEqual(obj, str(expected_value))
         self.assertIsInstance(obj, str)
 
         obj = xs.attributes['b64'].decode(base64_value.decode(), 
binary_types=True)
@@ -902,7 +902,7 @@
         xs = self.get_schema('<xs:element name="b64" type="xs:base64Binary"/>')
 
         obj = xs.decode('<b64>{}</b64>'.format(base64_value.decode()))
-        self.assertEqual(obj, expected_value)
+        self.assertEqual(obj, str(expected_value))
         self.assertIsInstance(obj, str)
 
         obj = xs.decode('<b64>{}</b64>'.format(base64_value.decode()), 
binary_types=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/tests/validators/test_identities.py 
new/xmlschema-2.2.0/tests/validators/test_identities.py
--- old/xmlschema-2.1.1/tests/validators/test_identities.py     2022-07-18 
16:19:15.000000000 +0200
+++ new/xmlschema-2.2.0/tests/validators/test_identities.py     2023-02-06 
07:20:42.000000000 +0100
@@ -122,7 +122,7 @@
                     </xs:key>
                 </xs:element>""")
 
-        self.assertIn("a QName cannot contains spaces", ctx.exception.message)
+        self.assertIn("XPST0003", ctx.exception.message)
 
     def test_selector_target_namespace(self):
         schema = self.check_schema("""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/tox.ini new/xmlschema-2.2.0/tox.ini
--- old/xmlschema-2.1.1/tox.ini 2022-10-01 15:42:01.000000000 +0200
+++ new/xmlschema-2.2.0/tox.ini 2023-02-06 07:20:42.000000000 +0100
@@ -1,28 +1,28 @@
 [tox]
-envlist = py{37,38,39,310,311}, pypy3, ep{300}, docs,
+envlist = py{37,38,39,310,311}, pypy3, ep{40}, docs,
     flake8, mypy-py{37,38,39,310,311}, coverage, pytest
 skip_missing_interpreters = true
-toxworkdir = {homedir}/.tox/xmlschema
+work_dir = {tox_root}/../.tox/xmlschema
 
 [testenv]
 deps =
-    elementpath>=3.0.0, <4.0.0
+    elementpath>=4.0.0, <5.0.0
     lxml
     jinja2
-    py{39,310}: memory_profiler
+    py{310,311}: memory_profiler
     docs: Sphinx
     docs: sphinx_rtd_theme
     coverage: coverage
 commands =
     python -m unittest
-whitelist_externals = make
+allowlist_externals = make
 
 [testenv:pypy3]
 commands = python -m unittest
 
-[testenv:ep300]
+[testenv:ep40]
 deps =
-    elementpath~=3.0.0
+    elementpath~=4.0.0
     lxml
 
 [testenv:docs]
@@ -44,8 +44,8 @@
 
 [testenv:mypy-py37]
 deps =
-    mypy==0.981
-    elementpath==3.0.1
+    mypy==0.991
+    elementpath==4.0.1
     lxml-stubs
     jinja2
 commands =
@@ -53,8 +53,8 @@
 
 [testenv:mypy-py{38,39,310,311}]
 deps =
-    mypy==0.981
-    elementpath==3.0.1
+    mypy==0.991
+    elementpath==4.0.1
     lxml-stubs
     jinja2
 commands =
@@ -71,10 +71,10 @@
 deps =
     pytest
     pytest-randomly
-    elementpath>=3.0.0, <4.0.0
+    elementpath>=4.0.0, <5.0.0
     lxml
     jinja2
-    mypy==0.981
+    mypy==0.991
     lxml-stubs
 commands =
     pytest tests -ra
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/__init__.py 
new/xmlschema-2.2.0/xmlschema/__init__.py
--- old/xmlschema-2.1.1/xmlschema/__init__.py   2022-10-01 15:42:01.000000000 
+0200
+++ new/xmlschema-2.2.0/xmlschema/__init__.py   2023-02-06 07:20:42.000000000 
+0100
@@ -31,10 +31,10 @@
     XsdComponent, XsdType, XsdElement, XsdAttribute
 )
 
-__version__ = '2.1.1'
+__version__ = '2.2.0'
 __author__ = "Davide Brunato"
 __contact__ = "brun...@sissa.it"
-__copyright__ = "Copyright 2016-2022, SISSA"
+__copyright__ = "Copyright 2016-2023, SISSA"
 __license__ = "MIT"
 __status__ = "Production/Stable"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/dataobjects.py 
new/xmlschema-2.2.0/xmlschema/dataobjects.py
--- old/xmlschema-2.1.1/xmlschema/dataobjects.py        2022-07-21 
11:40:50.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/dataobjects.py        2023-02-06 
09:54:57.000000000 +0100
@@ -7,7 +7,9 @@
 #
 # @author Davide Brunato <brun...@sissa.it>
 #
+import re
 from abc import ABCMeta
+from copy import copy
 from itertools import count
 from typing import TYPE_CHECKING, cast, overload, Any, Dict, List, Iterator, \
     Optional, Union, Tuple, Type, MutableMapping, MutableSequence
@@ -188,6 +190,71 @@
         """The local part of the tag."""
         return local_name(self.tag)
 
+    def iter(self, tag: Optional[str] = None) -> Iterator['DataElement']:
+        """
+        Creates an iterator for the data element and its subelements. If tag
+        is not `None` or '*', only data elements whose matches tag are returned
+        from the iterator.
+        """
+        if tag == '*':
+            tag = None
+        if tag is None or tag == self.tag:
+            yield self
+        for child in self._children:
+            yield from child.iter(tag)
+
+    def iterchildren(self, tag: Optional[str] = None) -> 
Iterator['DataElement']:
+        """
+        Creates an iterator for the child data elements. If *tag* is not 
`None` or '*',
+        only data elements whose name matches tag are returned from the 
iterator.
+        """
+        if tag == '*':
+            tag = None
+        for child in self:
+            if tag is None or tag == child.tag:
+                yield child
+
+    def get_namespaces(self, namespaces: Optional[NamespacesType] = None) -> 
NamespacesType:
+        """
+        Returns an overall namespace map for DetaElement and its descendants,
+        resolving prefix redefinitions.
+
+        :param namespaces: builds the namespace map starting over the 
dictionary provided.
+        """
+        namespaces = copy(namespaces) if namespaces is not None else {}
+        nsmap = None
+
+        for elem in self.iter():
+            if nsmap is elem.nsmap:
+                continue
+            else:
+                nsmap = elem.nsmap
+                for prefix, uri in nsmap.items():
+                    if not prefix:
+                        if not uri:
+                            continue
+                        elif '' not in namespaces:
+                            if self.namespace:
+                                namespaces[prefix] = uri
+                                continue
+                        elif namespaces[''] == uri:
+                            continue
+                        prefix = 'default'
+
+                    while prefix in namespaces:
+                        if namespaces[prefix] == uri:
+                            break
+                        match = re.search(r'(\d+)$', prefix)
+                        if match:
+                            index = int(match.group()) + 1
+                            prefix = prefix[:match.span()[0]] + str(index)
+                        else:
+                            prefix += '0'
+                    else:
+                        namespaces[prefix] = uri
+
+        return namespaces
+
     def validate(self, use_defaults: bool = True,
                  namespaces: Optional[NamespacesType] = None,
                  max_depth: Optional[int] = None) -> None:
@@ -228,11 +295,10 @@
             raise XMLSchemaValueError("%r has no schema bindings" % self)
 
         kwargs: Dict[str, Any] = {
+            'namespaces': self.get_namespaces(namespaces),
             'converter': DataElementConverter,
             'use_defaults': use_defaults,
         }
-        if namespaces:
-            kwargs['namespaces'] = namespaces
         if isinstance(max_depth, int) and max_depth >= 0:
             kwargs['max_depth'] = max_depth
 
@@ -256,6 +322,7 @@
         :raises: :exc:`XMLSchemaValidationError` if the object is invalid \
         and ``validation='strict'``.
         """
+        kwargs['namespaces'] = self.get_namespaces(kwargs.get('namespaces'))
         if 'converter' not in kwargs:
             kwargs['converter'] = DataElementConverter
 
@@ -271,11 +338,44 @@
 
     to_etree = encode
 
-    def tostring(self, indent: str = '', max_lines: Optional[int] = None,
-                 spaces_for_tab: int = 4) -> Any:
-        """Serializes the data element tree to an XML source string."""
-        root, errors = self.encode(validation='lax')
-        return etree_tostring(root, self.nsmap, indent, max_lines, 
spaces_for_tab)
+    def tostring(self, namespaces: Optional[MutableMapping[str, str]] = None,
+                 indent: str = '', max_lines: Optional[int] = None,
+                 spaces_for_tab: int = 4, xml_declaration: bool = False,
+                 encoding: str = 'unicode', method: str = 'xml') -> str:
+        """
+        Serializes the data element tree to an XML source string.
+
+        :param namespaces: is an optional mapping from namespace prefix to 
URI. \
+        Provided namespaces are registered before serialization. Ignored if 
the \
+        provided *elem* argument is a lxml Element instance.
+        :param indent: the base line indentation.
+        :param max_lines: if truncate serialization after a number of lines \
+        (default: do not truncate).
+        :param spaces_for_tab: number of spaces for replacing tab characters. 
For \
+        default tabs are replaced with 4 spaces, provide `None` to keep tab 
characters.
+        :param xml_declaration: if set to `True` inserts the XML declaration 
at the head.
+        :param encoding: if "unicode" (the default) the output is a string, \
+        otherwise it’s binary.
+        :param method: is either "xml" (the default), "html" or "text".
+        :return: a Unicode string.
+        """
+        root, _ = self.encode(validation='lax')
+        if not hasattr(root, 'nsmap'):
+            namespaces = self.get_namespaces(namespaces)
+
+        _string = etree_tostring(
+            elem=root,
+            namespaces=namespaces,
+            indent=indent,
+            max_lines=max_lines,
+            spaces_for_tab=spaces_for_tab,
+            xml_declaration=xml_declaration,
+            encoding=encoding,
+            method=method
+        )
+        if isinstance(_string, bytes):
+            return _string.decode('utf-8')
+        return _string
 
     def _get_xpath_context(self) -> XPathContext:
         xpath_root = build_node_tree(cast(protocols.ElementProtocol, self))
@@ -326,30 +426,6 @@
         results = parser.parse(path).select_results(context)
         yield from filter(lambda x: isinstance(x, DataElement), results)
 
-    def iter(self, tag: Optional[str] = None) -> Iterator['DataElement']:
-        """
-        Creates an iterator for the data element and its subelements. If tag
-        is not `None` or '*', only data elements whose matches tag are returned
-        from the iterator.
-        """
-        if tag == '*':
-            tag = None
-        if tag is None or tag == self.tag:
-            yield self
-        for child in self._children:
-            yield from child.iter(tag)
-
-    def iterchildren(self, tag: Optional[str] = None) -> 
Iterator['DataElement']:
-        """
-        Creates an iterator for the child data elements. If *tag* is not 
`None` or '*',
-        only data elements whose name matches tag are returned from the 
iterator.
-        """
-        if tag == '*':
-            tag = None
-        for child in self:
-            if tag is None or tag == child.tag:
-                yield child
-
 
 class DataBindingMeta(ABCMeta):
     """Metaclass for creating classes with bindings to XSD elements."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/documents.py 
new/xmlschema-2.2.0/xmlschema/documents.py
--- old/xmlschema-2.1.1/xmlschema/documents.py  2022-09-25 09:58:42.000000000 
+0200
+++ new/xmlschema-2.2.0/xmlschema/documents.py  2023-02-06 07:20:42.000000000 
+0100
@@ -8,6 +8,7 @@
 # @author Davide Brunato <brun...@sissa.it>
 #
 import json
+import copy
 from io import IOBase, TextIOBase
 from typing import Any, Dict, List, Optional, Type, Union, Tuple, \
     IO, BinaryIO, TextIO, Iterator
@@ -616,7 +617,7 @@
 
     def parse(self, source: XMLSourceType, lazy: LazyType = False) -> None:
         super(XmlDocument, self).parse(source, lazy)
-        self.namespaces = self.get_namespaces(self._namespaces)
+        self.namespaces = self.get_namespaces()
 
         if self.schema is None:
             pass
@@ -625,40 +626,38 @@
         elif self.validation == 'lax':
             self.errors = [e for e in self.schema.iter_errors(self, 
namespaces=self.namespaces)]
 
+    def get_namespaces(self, namespaces: Optional[NamespacesType] = None,
+                       root_only: Optional[bool] = None) -> NamespacesType:
+        if not self._namespaces:
+            _namespaces = namespaces
+        elif not namespaces:
+            _namespaces = self._namespaces
+        else:
+            _namespaces = copy.copy(self._namespaces)
+            _namespaces.update(namespaces)
+
+        return super().get_namespaces(_namespaces, root_only)
+
     def getroot(self) -> ElementType:
         """Get the root element of the XML document."""
         return self._root
 
     def get_etree_document(self) -> Any:
         """
-        The resource as ElementTree XML document. If the resource is lazy 
raises a resource error.
+        The resource as ElementTree XML document. If the resource is lazy
+        raises a resource error.
         """
         if is_etree_document(self._source):
             return self._source
         elif self._lazy:
-            raise XMLResourceError("cannot create an ElementTree from a lazy 
resource")
+            raise XMLResourceError(
+                "cannot create an ElementTree instance from a lazy XML 
resource"
+            )
         elif hasattr(self._root, 'nsmap'):
             return self._root.getroottree()  # type: ignore[attr-defined]
         else:
             return ElementTree.ElementTree(self._root)
 
-    def tostring(self, indent: str = '', max_lines: Optional[int] = None,
-                 spaces_for_tab: int = 4, xml_declaration: bool = False,
-                 encoding: str = 'unicode', method: str = 'xml') -> str:
-        if self._lazy:
-            raise XMLResourceError("cannot serialize a lazy XML document")
-
-        _string = etree_tostring(
-            elem=self._root,
-            namespaces=self.namespaces,
-            xml_declaration=xml_declaration,
-            encoding=encoding,
-            method=method
-        )
-        if isinstance(_string, bytes):
-            return _string.decode('utf-8')
-        return _string
-
     def decode(self, **kwargs: Any) -> DecodeType[Any]:
         """
         Decode the XML document to a nested Python dictionary.
@@ -731,7 +730,7 @@
               default_namespace: Optional[str] = None, method: str = "xml") -> 
None:
         """Serialize an XML resource to a file. Cannot be used with lazy 
resources."""
         if self._lazy:
-            raise XMLResourceError("cannot serialize a lazy XML document")
+            raise XMLResourceError("cannot serialize a lazy XML resource")
 
         kwargs: Dict[str, Any] = {
             'xml_declaration': xml_declaration,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/resources.py 
new/xmlschema-2.2.0/xmlschema/resources.py
--- old/xmlschema-2.1.1/xmlschema/resources.py  2022-09-08 12:16:11.000000000 
+0200
+++ new/xmlschema-2.2.0/xmlschema/resources.py  2023-02-06 07:20:42.000000000 
+0100
@@ -27,7 +27,7 @@
 
 from .exceptions import XMLSchemaTypeError, XMLSchemaValueError, 
XMLResourceError
 from .names import XML_NAMESPACE
-from .aliases import ElementType, ElementTreeType, NamespacesType, 
XMLSourceType, \
+from .aliases import ElementType, NamespacesType, XMLSourceType, \
     NormalizedLocationsType, LocationsType, NsmapType, ParentMapType
 from .helpers import get_namespace, is_etree_document, 
etree_iter_location_hints
 
@@ -536,15 +536,15 @@
         if self._allow == 'all':
             return
         elif self._allow == 'none':
-            raise XMLResourceError("block access to resource {}".format(url))
+            raise XMLResourceError(f"block access to resource {url}")
         elif self._allow == 'remote':
             if is_local_url(url):
-                raise XMLResourceError("block access to local resource 
{}".format(url))
+                raise XMLResourceError(f"block access to local resource {url}")
         elif is_remote_url(url):
-            raise XMLResourceError("block access to remote resource 
{}".format(url))
+            raise XMLResourceError(f"block access to remote resource {url}")
         elif self._allow == 'sandbox' and self._base_url is not None:
             if not url.startswith(normalize_url(self._base_url)):
-                raise XMLResourceError("block access to out of sandbox file 
{}".format(url))
+                raise XMLResourceError(f"block access to out of sandbox file 
{url}")
 
     def _track_nsmap(self, elements: Iterator[ElementType],
                      nsmap: NsmapType) -> Iterator[ElementType]:
@@ -793,7 +793,7 @@
                 self._root = cast(ElementType, source)
             elif is_etree_document(source):
                 # Could be only an ElementTree object at last
-                self._root = cast(ElementTreeType, source).getroot()
+                self._root = source.getroot()
             else:
                 raise XMLSchemaTypeError(
                     "wrong type %r for 'source' attribute: an ElementTree 
object or "
@@ -827,7 +827,7 @@
     @property
     def parent_map(self) -> Dict[ElementType, Optional[ElementType]]:
         if self._lazy:
-            raise XMLResourceError("cannot create the parent map of a lazy 
resource")
+            raise XMLResourceError("cannot create the parent map of a lazy XML 
resource")
         if self._parent_map is None:
             assert self._root is not None
             self._parent_map = {child: elem for elem in self._root.iter() for 
child in elem}
@@ -917,23 +917,51 @@
 
         return self.tostring(xml_declaration=True)
 
-    def tostring(self, indent: str = '', max_lines: Optional[int] = None,
-                 spaces_for_tab: int = 4, xml_declaration: bool = False) -> 
str:
-        """Generates a string representation of the XML resource."""
+    def tostring(self, namespaces: Optional[MutableMapping[str, str]] = None,
+                 indent: str = '', max_lines: Optional[int] = None,
+                 spaces_for_tab: int = 4, xml_declaration: bool = False,
+                 encoding: str = 'unicode', method: str = 'xml') -> str:
+        """
+        Serialize an XML resource to a string.
+
+        :param namespaces: is an optional mapping from namespace prefix to 
URI. \
+        Provided namespaces are registered before serialization. Ignored if 
the \
+        provided *elem* argument is a lxml Element instance.
+        :param indent: the base line indentation.
+        :param max_lines: if truncate serialization after a number of lines \
+        (default: do not truncate).
+        :param spaces_for_tab: number of spaces for replacing tab characters. 
For \
+        default tabs are replaced with 4 spaces, provide `None` to keep tab 
characters.
+        :param xml_declaration: if set to `True` inserts the XML declaration 
at the head.
+        :param encoding: if "unicode" (the default) the output is a string, \
+        otherwise it’s binary.
+        :param method: is either "xml" (the default), "html" or "text".
+        :return: a Unicode string.
+        """
         if self._lazy:
-            raise XMLResourceError("cannot serialize a lazy resource")
+            raise XMLResourceError("cannot serialize a lazy XML resource")
 
-        elem = self._root
-        namespaces = self.get_namespaces(root_only=False)
-        _string = etree_tostring(elem, namespaces, indent, max_lines,
-                                 spaces_for_tab, xml_declaration)
+        if not hasattr(self._root, 'nsmap'):
+            namespaces = self.get_namespaces(namespaces)
 
-        return _string.decode('utf-8') if isinstance(_string, bytes) else 
_string
+        _string = etree_tostring(
+            elem=self._root,
+            namespaces=namespaces,
+            indent=indent,
+            max_lines=max_lines,
+            spaces_for_tab=spaces_for_tab,
+            xml_declaration=xml_declaration,
+            encoding=encoding,
+            method=method
+        )
+        if isinstance(_string, bytes):
+            return _string.decode('utf-8')
+        return _string
 
     def subresource(self, elem: ElementType) -> 'XMLResource':
         """Create an XMLResource instance from a subelement of a non-lazy XML 
tree."""
         if self._lazy:
-            raise XMLResourceError("cannot create a subresource from a lazy 
resource")
+            raise XMLResourceError("cannot create a subresource from a lazy 
XML resource")
 
         for e in self._root.iter():  # pragma: no cover
             if e is elem:
@@ -969,7 +997,7 @@
         if self.seek(0) == 0:
             return cast(IO[AnyStr], self._source)
         elif self._url is None:
-            raise XMLResourceError("can't open, the resource has no URL 
associated.")
+            raise XMLResourceError(f"can't open, {self!r} has no URL 
associated")
 
         try:
             return cast(IO[AnyStr], urlopen(self._url, timeout=self._timeout))
@@ -1013,7 +1041,7 @@
         if self._url is None and not hasattr(self._source, 'read'):
             return  # Created from Element or text source --> already loaded
         elif self._lazy:
-            raise XMLResourceError("cannot load a lazy resource")
+            raise XMLResourceError("cannot load a lazy XML resource")
 
         resource = self.open()
         try:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/xmlschema-2.1.1/xmlschema/validators/complex_types.py 
new/xmlschema-2.2.0/xmlschema/validators/complex_types.py
--- old/xmlschema-2.1.1/xmlschema/validators/complex_types.py   2022-09-25 
09:58:42.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/validators/complex_types.py   2023-02-06 
07:20:42.000000000 +0100
@@ -15,12 +15,12 @@
 from ..names import XSD_GROUP, XSD_ATTRIBUTE_GROUP, XSD_SEQUENCE, 
XSD_OVERRIDE, \
     XSD_ALL, XSD_CHOICE, XSD_ANY_ATTRIBUTE, XSD_ATTRIBUTE, 
XSD_COMPLEX_CONTENT, \
     XSD_RESTRICTION, XSD_COMPLEX_TYPE, XSD_EXTENSION, XSD_ANY_TYPE, 
XSD_ASSERT, \
-    XSD_UNTYPED_ATOMIC, XSD_SIMPLE_CONTENT, XSD_OPEN_CONTENT, XSD_ANNOTATION
+    XSD_SIMPLE_CONTENT, XSD_OPEN_CONTENT, XSD_ANNOTATION
 from ..aliases import ElementType, NamespacesType, SchemaType, 
ComponentClassType, \
     DecodeType, IterDecodeType, IterEncodeType, BaseXsdType, AtomicValueType, \
     ExtraValidatorType
 from ..translation import gettext as _
-from ..helpers import get_prefixed_qname, get_qname, local_name
+from ..helpers import get_qname, local_name
 
 from .exceptions import XMLSchemaDecodeError
 from .helpers import get_xsd_derivation_attribute
@@ -537,15 +537,14 @@
         if self.is_empty():
             return 'empty-sequence()'
         elif not self.has_simple_content():
-            st = get_prefixed_qname(XSD_UNTYPED_ATOMIC, self.namespaces)
+            st = 'xs:untypedAtomic'
         else:
             try:
-                st = self.content.primitive_type.prefixed_name  # type: 
ignore[union-attr]
+                name = self.content.primitive_type.local_name  # type: 
ignore[union-attr]
             except AttributeError:
-                st = get_prefixed_qname(XSD_UNTYPED_ATOMIC, self.namespaces)
+                st = 'xs:untypedAtomic'
             else:
-                if st is None:
-                    st = 'item()'
+                st = 'item()' if name is None else f'xs:{name}'
 
         return f"{st}{'*' if self.is_emptiable() else '+'}"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/validators/elements.py 
new/xmlschema-2.2.0/xmlschema/validators/elements.py
--- old/xmlschema-2.1.1/xmlschema/validators/elements.py        2022-10-01 
12:20:15.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/validators/elements.py        2023-02-06 
07:20:42.000000000 +0100
@@ -37,7 +37,8 @@
     ElementPathMixin, XPathElement
 from ..resources import XMLResource
 
-from .exceptions import XMLSchemaValidationError, XMLSchemaTypeTableWarning
+from .exceptions import XMLSchemaNotBuiltError, XMLSchemaValidationError, \
+    XMLSchemaTypeTableWarning
 from .helpers import get_xsd_derivation_attribute
 from .xsdbase import XSD_TYPE_DERIVATIONS, XSD_ELEMENT_DERIVATIONS, \
     XsdComponent, ValidationMixin
@@ -472,15 +473,6 @@
                                 dataobjects.DataBindingMeta(class_name, bases, 
attrs))
         return self.binding
 
-    def get_attribute(self, name: str) -> Optional[XsdAttribute]:
-        if name[0] != '{':
-            name = get_qname(self.type.target_namespace, name)
-        if not isinstance(self.type, XsdSimpleType):
-            xsd_attribute = self.type.attributes[name]
-            assert isinstance(xsd_attribute, XsdAttribute)
-            return xsd_attribute
-        return None
-
     def get_type(self, elem: Union[ElementType, ElementData],
                  inherited: Optional[Dict[str, Any]] = None) -> BaseXsdType:
         return self._head_type or self.type
@@ -675,8 +667,13 @@
                 if self.identities:
                     xpath_element = XPathElement(self.name, xsd_type)
                     for identity in self.identities.values():
-                        if isinstance(identity.elements, tuple):
-                            continue  # Skip unbuilt identities
+                        if isinstance(identity.elements, tuple) \
+                                or identity.selector is None:
+                            continue  # Skip unbuilt or incomplete identities
+                        elif identity.selector.token is None:
+                            raise XMLSchemaNotBuiltError(
+                                identity, "identity selector is not built"
+                            )
 
                         context = XPathContext(
                             root=self.schema.xpath_node,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/validators/exceptions.py 
new/xmlschema-2.2.0/xmlschema/validators/exceptions.py
--- old/xmlschema-2.1.1/xmlschema/validators/exceptions.py      2022-07-18 
16:19:15.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/validators/exceptions.py      2023-02-06 
07:20:42.000000000 +0100
@@ -240,7 +240,7 @@
             msg.append('Reason: %s\n' % self.reason)
 
         if hasattr(self.validator, 'tostring'):
-            chunk = self.validator.tostring('  ', 20)  # type: 
ignore[union-attr]
+            chunk = self.validator.tostring('  ', 20)
             msg.append("Schema:\n\n%s\n" % chunk)
 
         if self.elem is not None and is_etree_element(self.elem):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/validators/identities.py 
new/xmlschema-2.2.0/xmlschema/validators/identities.py
--- old/xmlschema-2.1.1/xmlschema/validators/identities.py      2022-07-18 
16:19:15.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/validators/identities.py      2023-02-06 
07:20:42.000000000 +0100
@@ -22,6 +22,7 @@
 from ..translation import gettext as _
 from ..helpers import get_qname, get_extended_qname
 from ..aliases import ElementType, SchemaType, NamespacesType, AtomicValueType
+from .exceptions import XMLSchemaNotBuiltError
 from .xsdbase import XsdComponent
 from .attributes import XsdAttribute
 
@@ -68,8 +69,8 @@
         lazy_quantifiers=False,
         anchors=False
     )
-    token = None   # type: XPathToken
-    parser = None  # type: IdentityXPathParser
+    token: Optional[XPathToken] = None
+    parser: Optional[IdentityXPathParser] = None
 
     def __init__(self, elem: ElementType, schema: SchemaType,
                  parent: Optional['XsdIdentity']) -> None:
@@ -161,8 +162,8 @@
     parent: 'XsdElement'
     ref: Optional['XsdIdentity']
 
-    selector = None  # type: XsdSelector
-    fields = ()      # type: Union[Tuple[()], List[XsdFieldSelector]]
+    selector: Optional[XsdSelector] = None
+    fields: Union[Tuple[()], List[XsdFieldSelector]] = ()
 
     # XSD elements bound by selector (for speed-up and for lazy mode)
     elements: Union[Tuple[()], Dict['XsdElement', 
Optional[IdentityCounterType]]] = ()
@@ -206,34 +207,37 @@
                 self.fields = ref.fields
                 self.ref = ref
 
+        if self.selector is None:
+            return  # Do not raise, already found by meta-schema validation.
+        elif self.selector.token is None:
+            raise XMLSchemaNotBuiltError(self, "identity selector is not 
built")
+
         context = XPathContext(self.schema.xpath_node, 
item=self.parent.xpath_node)
         self.elements = {}
-        try:
-            for e in self.selector.token.select_results(context):
-                if not isinstance(e, XsdComponent) or isinstance(e, 
XsdAttribute):
-                    msg = _("selector xpath expression can only select 
elements")
-                    self.parse_error(msg)
-                elif e.name is not None:
-                    if TYPE_CHECKING:
-                        assert isinstance(e, XsdElement)  # for mypy checks 
with Python 3.7
-                    self.elements[e] = None
-        except AttributeError:
-            pass
-        else:
-            if not self.elements:
-                # Try to detect target XSD elements extracting QNames
-                # of the leaf elements from the XPath expression and
-                # use them to match global elements.
-
-                qname: Any
-                for qname in self.selector.token.iter_leaf_elements():
-                    xsd_element = self.maps.elements.get(
-                        get_extended_qname(qname, self.namespaces)
-                    )
-                    if xsd_element is not None and \
-                            not isinstance(xsd_element, tuple) and \
-                            xsd_element not in self.elements:
-                        self.elements[xsd_element] = None
+
+        for e in self.selector.token.select_results(context):
+            if not isinstance(e, XsdComponent) or isinstance(e, XsdAttribute):
+                msg = _("selector xpath expression can only select elements")
+                self.parse_error(msg)
+            elif e.name is not None:
+                if TYPE_CHECKING:
+                    assert isinstance(e, XsdElement)  # for mypy checks with 
Python 3.7
+                self.elements[e] = None
+
+        if not self.elements:
+            # Try to detect target XSD elements extracting QNames
+            # of the leaf elements from the XPath expression and
+            # use them to match global elements.
+
+            qname: Any
+            for qname in self.selector.token.iter_leaf_elements():
+                xsd_element = self.maps.elements.get(
+                    get_extended_qname(qname, self.namespaces)
+                )
+                if xsd_element is not None and \
+                        not isinstance(xsd_element, tuple) and \
+                        xsd_element not in self.elements:
+                    self.elements[xsd_element] = None
 
     @property
     def built(self) -> bool:
@@ -268,6 +272,10 @@
         value: Union[AtomicValueType, None]
 
         for k, field in enumerate(self.fields):
+            if field.token is None:
+                msg = f"identity field {field} is not built"
+                raise XMLSchemaNotBuiltError(self, msg)
+
             context = XPathContext(element_node)
             result = field.token.get_results(context)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/validators/schemas.py 
new/xmlschema-2.2.0/xmlschema/validators/schemas.py
--- old/xmlschema-2.1.1/xmlschema/validators/schemas.py 2022-10-01 
15:42:01.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/validators/schemas.py 2023-02-06 
07:20:42.000000000 +0100
@@ -208,7 +208,6 @@
     belong the declarations/definitions of the schema. If it's empty no 
namespace is associated \
     with the schema. In this case the schema declarations can be reused from 
other namespaces as \
     *chameleon* definitions.
-    :ivar validation: validation mode, can be 'strict', 'lax' or 'skip'.
     :ivar maps: XSD global declarations/definitions maps. This is an instance 
of \
     :class:`XsdGlobals`, that stores the *global_maps* argument or a new 
object \
     when this argument is not provided.
@@ -350,7 +349,7 @@
         self.imports = {}
         self.includes = {}
         self.warnings = []
-        self._root_elements = None  # type: Optional[Set[str]]
+        self._root_elements: Optional[Set[str]] = None
 
         self.name = self.source.name
         root = self.source.root
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/validators/simple_types.py 
new/xmlschema-2.2.0/xmlschema/validators/simple_types.py
--- old/xmlschema-2.1.1/xmlschema/validators/simple_types.py    2022-09-25 
09:58:42.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/validators/simple_types.py    2023-02-06 
07:20:42.000000000 +0100
@@ -12,7 +12,7 @@
 """
 from decimal import DecimalException
 from typing import cast, Any, Callable, Dict, Iterator, List, \
-    Optional, Pattern, Set, Union, Tuple, Type
+    Optional, Set, Union, Tuple, Type
 from xml.etree import ElementTree
 
 from ..aliases import ElementType, AtomicValueType, ComponentClassType, \
@@ -25,9 +25,9 @@
     XSD_LENGTH, XSD_MIN_LENGTH, XSD_MAX_LENGTH, XSD_WHITE_SPACE, 
XSD_ENUMERATION,\
     XSD_LIST, XSD_ANY_SIMPLE_TYPE, XSD_UNION, XSD_RESTRICTION, XSD_ANNOTATION, 
\
     XSD_ASSERTION, XSD_ID, XSD_IDREF, XSD_FRACTION_DIGITS, XSD_TOTAL_DIGITS, \
-    XSD_EXPLICIT_TIMEZONE, XSD_ERROR, XSD_ASSERT, XSD_QNAME, XSD_UNTYPED_ATOMIC
+    XSD_EXPLICIT_TIMEZONE, XSD_ERROR, XSD_ASSERT, XSD_QNAME
 from ..translation import gettext as _
-from ..helpers import get_prefixed_qname, local_name
+from ..helpers import local_name
 
 from .exceptions import XMLSchemaValidationError, XMLSchemaEncodeError, \
     XMLSchemaDecodeError, XMLSchemaParseError
@@ -96,7 +96,6 @@
             if white_space is not None:
                 self.white_space = white_space
 
-            p: Pattern[str]
             patterns = self.get_facet(XSD_PATTERN)
             if isinstance(patterns, XsdPatternFacets):
                 self.patterns = patterns
@@ -353,16 +352,16 @@
 
         root_type = self.root_type
         if root_type.name is not None:
-            sequence_type = cast(str, root_type.prefixed_name)
+            sequence_type = f'xs:{root_type.local_name}'
         else:
-            sequence_type = get_prefixed_qname(XSD_UNTYPED_ATOMIC, 
self.namespaces)
+            sequence_type = 'xs:untypedAtomic'
 
         if not self.is_list():
             return sequence_type
         elif self.is_emptiable():
-            return '{}*'.format(sequence_type)
+            return f'{sequence_type}*'
         else:
-            return '{}+'.format(sequence_type)
+            return f'{sequence_type}+'
 
     def is_empty(self) -> bool:
         return self.max_length == 0 or \
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema/validators/xsdbase.py 
new/xmlschema-2.2.0/xmlschema/validators/xsdbase.py
--- old/xmlschema-2.1.1/xmlschema/validators/xsdbase.py 2022-09-08 
12:16:11.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema/validators/xsdbase.py 2023-02-06 
07:20:42.000000000 +0100
@@ -768,6 +768,7 @@
     @staticmethod
     def is_complex() -> bool:
         """Returns `True` if the instance is a complexType, `False` 
otherwise."""
+        return False
 
     def is_atomic(self) -> bool:
         """Returns `True` if the instance is an atomic simpleType, `False` 
otherwise."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema.egg-info/PKG-INFO 
new/xmlschema-2.2.0/xmlschema.egg-info/PKG-INFO
--- old/xmlschema-2.1.1/xmlschema.egg-info/PKG-INFO     2022-10-01 
19:02:18.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema.egg-info/PKG-INFO     2023-02-06 
10:06:40.000000000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: xmlschema
-Version: 2.1.1
+Version: 2.2.0
 Summary: An XML Schema validator and decoder
 Home-page: https://github.com/sissaschool/xmlschema
 Author: Davide Brunato
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/xmlschema-2.1.1/xmlschema.egg-info/requires.txt 
new/xmlschema-2.2.0/xmlschema.egg-info/requires.txt
--- old/xmlschema-2.1.1/xmlschema.egg-info/requires.txt 2022-10-01 
19:02:18.000000000 +0200
+++ new/xmlschema-2.2.0/xmlschema.egg-info/requires.txt 2023-02-06 
10:06:40.000000000 +0100
@@ -1,14 +1,14 @@
-elementpath<4.0.0,>=3.0.0
+elementpath<5.0.0,>=4.0.0
 
 [codegen]
-elementpath<4.0.0,>=3.0.0
+elementpath<5.0.0,>=4.0.0
 jinja2
 
 [dev]
 tox
 coverage
 lxml
-elementpath<4.0.0,>=3.0.0
+elementpath<5.0.0,>=4.0.0
 memory_profiler
 Sphinx
 sphinx_rtd_theme
@@ -18,7 +18,7 @@
 lxml-stubs
 
 [docs]
-elementpath<4.0.0,>=3.0.0
+elementpath<5.0.0,>=4.0.0
 Sphinx
 sphinx_rtd_theme
 jinja2

Reply via email to