elementpath 4.7.0__tar.gz → 5.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {elementpath-4.7.0 → elementpath-5.0.0}/CHANGELOG.rst +18 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/MANIFEST.in +1 -4
- {elementpath-4.7.0 → elementpath-5.0.0}/PKG-INFO +18 -17
- {elementpath-4.7.0 → elementpath-5.0.0}/README.rst +1 -1
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/advanced.rst +13 -8
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/conf.py +3 -3
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/__init__.py +11 -12
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/aliases.py +9 -10
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/collations.py +4 -4
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/compare.py +34 -34
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/__init__.py +6 -15
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/atomic_types.py +32 -33
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/binary.py +1 -2
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/datetime.py +17 -10
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/numeric.py +3 -3
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/proxies.py +2 -2
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/untyped.py +2 -2
- elementpath-5.0.0/elementpath/decoder.py +167 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/etree.py +46 -81
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/exceptions.py +10 -2
- elementpath-5.0.0/elementpath/extras/pathnodes.py +262 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/helpers.py +20 -12
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/namespaces.py +58 -2
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/protocols.py +49 -20
- elementpath-5.0.0/elementpath/regex/categories_fallback.py +77 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/regex/character_classes.py +3 -3
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/regex/codepoints.py +4 -5
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/regex/unicode_subsets.py +22 -13
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/schema_proxy.py +67 -11
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/sequence_types.py +12 -13
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/serialization.py +13 -13
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/tdop.py +33 -35
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/tree_builders.py +75 -102
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath1/_xpath1_axes.py +7 -5
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath1/_xpath1_functions.py +15 -13
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath1/_xpath1_operators.py +49 -137
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath1/xpath1_parser.py +41 -28
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath2/_xpath2_constructors.py +5 -8
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath2/_xpath2_functions.py +60 -87
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath2/_xpath2_operators.py +36 -47
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath2/xpath2_parser.py +18 -39
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath30/_xpath30_functions.py +47 -102
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath30/_xpath30_operators.py +6 -10
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath30/xpath30_helpers.py +6 -6
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath30/xpath30_parser.py +4 -4
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath31/_xpath31_functions.py +18 -18
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath31/_xpath31_operators.py +16 -9
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath31/xpath31_parser.py +3 -3
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath_context.py +134 -45
- elementpath-5.0.0/elementpath/xpath_nodes.py +1766 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath_selectors.py +18 -11
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath_tokens.py +212 -291
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath.egg-info/PKG-INFO +18 -17
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath.egg-info/SOURCES.txt +9 -7
- elementpath-5.0.0/elementpath.egg-info/requires.txt +16 -0
- elementpath-5.0.0/pyproject.toml +87 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/requirements-dev.txt +4 -4
- elementpath-5.0.0/tests/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/memory_profiling.py +27 -3
- elementpath-4.7.0/tests/test_typing.py → elementpath-5.0.0/tests/run_typing_tests.py +1 -1
- elementpath-4.7.0/tests/execute_w3c_tests.py → elementpath-5.0.0/tests/run_w3c_tests.py +8 -3
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_compare.py +5 -5
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_datatypes.py +42 -16
- elementpath-5.0.0/tests/test_decoder.py +61 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_etree.py +77 -154
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_helpers.py +177 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_namespaces.py +44 -3
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_package.py +6 -6
- elementpath-5.0.0/tests/test_pathnodes.py +63 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_schema_context.py +0 -79
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_schema_proxy.py +39 -57
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_sequence_types.py +1 -1
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_serialization.py +2 -2
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_tree_builders.py +3 -3
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_validators.py +1 -1
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath1_parser.py +8 -8
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath2_constructors.py +12 -7
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath2_functions.py +20 -24
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath2_parser.py +60 -41
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath30.py +10 -5
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath_context.py +23 -18
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath_nodes.py +86 -69
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath_tokens.py +39 -285
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/xpath_test_class.py +5 -4
- {elementpath-4.7.0 → elementpath-5.0.0}/tox.ini +46 -33
- elementpath-4.7.0/.coveragerc +0 -12
- elementpath-4.7.0/elementpath/_typing.py +0 -27
- elementpath-4.7.0/elementpath/decoder.py +0 -176
- elementpath-4.7.0/elementpath/xpath_nodes.py +0 -1055
- elementpath-4.7.0/elementpath.egg-info/requires.txt +0 -12
- elementpath-4.7.0/mypy.ini +0 -2
- elementpath-4.7.0/setup.py +0 -59
- {elementpath-4.7.0 → elementpath-5.0.0}/LICENSE +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/Makefile +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/index.rst +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/introduction.rst +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/make.bat +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/pratt_api.rst +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/requirements.txt +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/doc/xpath_api.rst +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/qname.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/string.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/datatypes/uri.py +0 -0
- {elementpath-4.7.0/tests → elementpath-5.0.0/elementpath/extras}/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/py.typed +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/regex/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/regex/patterns.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/regex/unicode_blocks.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/regex/unicode_categories.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/validators/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/validators/analyze-string.xsd +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/validators/schema-for-json.xsd +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath1/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath2/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath3.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath30/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath30/_translation_maps.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath/xpath31/__init__.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath.egg-info/dependency_links.txt +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/elementpath.egg-info/top_level.txt +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/scripts/generate_codepoints.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/setup.cfg +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/mypy_tests/advanced.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/mypy_tests/protocols.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/mypy_tests/selectors.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/analyze-string.xsd +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/external_entity.xml +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/sample.xml +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/schema-for-json.xsd +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/unparsed_entity.xml +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/unused_external_entity.xml +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/unused_unparsed_entity.xml +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/resources/with_entity.xml +0 -0
- /elementpath-4.7.0/tests/test_elementpath.py → /elementpath-5.0.0/tests/run_all_tests.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_collations.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_exceptions.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_regex.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_selectors.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_tdop_parser.py +0 -0
- {elementpath-4.7.0 → elementpath-5.0.0}/tests/test_xpath31.py +0 -0
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
CHANGELOG
|
|
3
3
|
*********
|
|
4
4
|
|
|
5
|
+
`v5.0.0`_ (2025-04-27)
|
|
6
|
+
======================
|
|
7
|
+
* Replace SafeXMLParser with SafeExpatParser
|
|
8
|
+
* Drop compatibility with Python 3.8
|
|
9
|
+
* Restruct node trees with XPathNodeTree backbone
|
|
10
|
+
* Unicode categories fallback with a warning (issue #85)
|
|
11
|
+
* Rewriting decoder module (issue #87)
|
|
12
|
+
* Add extras subpackage for experiment XDM and XPath on Path objects
|
|
13
|
+
|
|
14
|
+
`v4.8.0`_ (2025-03-03)
|
|
15
|
+
======================
|
|
16
|
+
* Add full PSVI type labeling in XDM to solve type errors with XSD 1.1 assertions
|
|
17
|
+
* Add *schema* optional argument to dynamic context
|
|
18
|
+
* Add a RootToken as a proxy of the parsed token tree for compatibility with xmlschema<=3.4.3
|
|
19
|
+
* Extend XDM to split ElementTree/lxml processing from schema nodes and to allow future extensions
|
|
20
|
+
|
|
5
21
|
`v4.7.0`_ (2024-12-20)
|
|
6
22
|
======================
|
|
7
23
|
* Fix *fragment* argument usage (issue #81)
|
|
@@ -484,3 +500,5 @@ CHANGELOG
|
|
|
484
500
|
.. _v4.5.0: https://github.com/sissaschool/elementpath/compare/v4.4.0...v4.5.0
|
|
485
501
|
.. _v4.6.0: https://github.com/sissaschool/elementpath/compare/v4.5.0...v4.6.0
|
|
486
502
|
.. _v4.7.0: https://github.com/sissaschool/elementpath/compare/v4.6.0...v4.7.0
|
|
503
|
+
.. _v4.8.0: https://github.com/sissaschool/elementpath/compare/v4.7.0...v4.8.0
|
|
504
|
+
.. _v5.0.0: https://github.com/sissaschool/elementpath/compare/v4.8.0...v5.0.0
|
|
@@ -2,12 +2,9 @@ include LICENSE
|
|
|
2
2
|
include MANIFEST.in
|
|
3
3
|
include README.rst
|
|
4
4
|
include CHANGELOG.rst
|
|
5
|
-
include
|
|
6
|
-
include setup.cfg
|
|
5
|
+
include pyproject.toml
|
|
7
6
|
include requirements-dev.txt
|
|
8
7
|
include tox.ini
|
|
9
|
-
include .coveragerc
|
|
10
|
-
include mypy.ini
|
|
11
8
|
include doc/*
|
|
12
9
|
|
|
13
10
|
recursive-include elementpath *
|
|
@@ -1,22 +1,18 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: elementpath
|
|
3
|
-
Version:
|
|
3
|
+
Version: 5.0.0
|
|
4
4
|
Summary: XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
License: MIT
|
|
9
|
-
Keywords: XPath,XPath2,XPath3,XPath31,Pratt-parser,ElementTree,lxml
|
|
5
|
+
Author-email: Davide Brunato <brunato@sissa.it>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sissaschool/elementpath
|
|
10
8
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
9
|
Classifier: Intended Audience :: Developers
|
|
12
10
|
Classifier: Intended Audience :: Information Technology
|
|
13
11
|
Classifier: Intended Audience :: Science/Research
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
12
|
Classifier: Operating System :: OS Independent
|
|
16
13
|
Classifier: Programming Language :: Python
|
|
17
14
|
Classifier: Programming Language :: Python :: 3
|
|
18
15
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
20
16
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
17
|
Classifier: Programming Language :: Python :: 3.10
|
|
22
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -27,19 +23,24 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
|
27
23
|
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
28
24
|
Classifier: Topic :: Software Development :: Libraries
|
|
29
25
|
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
30
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Description-Content-Type: text/x-rst
|
|
31
28
|
License-File: LICENSE
|
|
32
29
|
Provides-Extra: dev
|
|
33
|
-
Requires-Dist: tox; extra == "dev"
|
|
34
30
|
Requires-Dist: coverage; extra == "dev"
|
|
31
|
+
Requires-Dist: flake8; extra == "dev"
|
|
35
32
|
Requires-Dist: lxml; extra == "dev"
|
|
36
|
-
Requires-Dist:
|
|
37
|
-
Requires-Dist: Sphinx; extra == "dev"
|
|
38
|
-
Requires-Dist: memory-profiler; extra == "dev"
|
|
33
|
+
Requires-Dist: lxml-stubs; extra == "dev"
|
|
39
34
|
Requires-Dist: memray; extra == "dev"
|
|
40
|
-
Requires-Dist: flake8; extra == "dev"
|
|
41
35
|
Requires-Dist: mypy; extra == "dev"
|
|
42
|
-
Requires-Dist:
|
|
36
|
+
Requires-Dist: psutil; extra == "dev"
|
|
37
|
+
Requires-Dist: sphinx; extra == "dev"
|
|
38
|
+
Requires-Dist: tox; extra == "dev"
|
|
39
|
+
Requires-Dist: xmlschema>=4.0.1; extra == "dev"
|
|
40
|
+
Provides-Extra: docs
|
|
41
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
42
|
+
Requires-Dist: readthedocs-sphinx-search; extra == "docs"
|
|
43
|
+
Dynamic: license-file
|
|
43
44
|
|
|
44
45
|
***********
|
|
45
46
|
elementpath
|
|
@@ -71,7 +72,7 @@ has it's own implementation of XPath 1.0.
|
|
|
71
72
|
Installation and usage
|
|
72
73
|
======================
|
|
73
74
|
|
|
74
|
-
You can install the package with *pip* in a Python 3.
|
|
75
|
+
You can install the package with *pip* in a Python 3.9+ environment::
|
|
75
76
|
|
|
76
77
|
pip install elementpath
|
|
77
78
|
|
|
@@ -28,7 +28,7 @@ has it's own implementation of XPath 1.0.
|
|
|
28
28
|
Installation and usage
|
|
29
29
|
======================
|
|
30
30
|
|
|
31
|
-
You can install the package with *pip* in a Python 3.
|
|
31
|
+
You can install the package with *pip* in a Python 3.9+ environment::
|
|
32
32
|
|
|
33
33
|
pip install elementpath
|
|
34
34
|
|
|
@@ -22,6 +22,11 @@ having as result a tree of tokens:
|
|
|
22
22
|
>>> token = parser.parse('/root/(: comment :) child[@attr]')
|
|
23
23
|
>>> isinstance(token, XPathToken)
|
|
24
24
|
True
|
|
25
|
+
|
|
26
|
+
That token is a proxy token for the tree produced by TDOP parsing:
|
|
27
|
+
|
|
28
|
+
.. doctest::
|
|
29
|
+
|
|
25
30
|
>>> token
|
|
26
31
|
<_SolidusOperator object at 0x...
|
|
27
32
|
>>> str(token)
|
|
@@ -73,7 +78,7 @@ represented by *XPathContext* objects.
|
|
|
73
78
|
>>> root = ElementTree.XML('<root><child/><child attr="10"/></root>')
|
|
74
79
|
>>> context = XPathContext(root)
|
|
75
80
|
>>> token.evaluate(context)
|
|
76
|
-
[
|
|
81
|
+
[EtreeElementNode(elem=<Element 'child' at ...)]
|
|
77
82
|
|
|
78
83
|
In this case an error is raised if you don't provide a context:
|
|
79
84
|
|
|
@@ -124,9 +129,9 @@ Node trees are automatically created at dynamic context initialization:
|
|
|
124
129
|
>>> root = ElementTree.XML('<root><child/><child attr="10"/></root>')
|
|
125
130
|
>>> context = XPathContext(root)
|
|
126
131
|
>>> context.root
|
|
127
|
-
|
|
132
|
+
EtreeElementNode(elem=<Element 'root' at ...>)
|
|
128
133
|
>>> context.root.children
|
|
129
|
-
[
|
|
134
|
+
[EtreeElementNode(elem=<Element 'child' at ...>), EtreeElementNode(elem=<Element 'child' at ...>)]
|
|
130
135
|
|
|
131
136
|
If the same XML data is applied several times for dynamic evaluation it maybe
|
|
132
137
|
convenient to build the node tree before, in the way to create it only once:
|
|
@@ -232,7 +237,7 @@ context item is set to context root:
|
|
|
232
237
|
|
|
233
238
|
>>> context = XPathContext(doc)
|
|
234
239
|
>>> context.root
|
|
235
|
-
|
|
240
|
+
EtreeDocumentNode(document=<xml.etree.ElementTree.ElementTree object at ...>)
|
|
236
241
|
>>> context.item is context.root
|
|
237
242
|
True
|
|
238
243
|
>>> context.document is context.root
|
|
@@ -246,11 +251,11 @@ set to root element node. In this case the context document is a dummy document:
|
|
|
246
251
|
>>> root = ElementTree.XML('<root><child1/><child2/><child3/></root>')
|
|
247
252
|
>>> context = XPathContext(root)
|
|
248
253
|
>>> context.root
|
|
249
|
-
|
|
254
|
+
EtreeElementNode(elem=<Element 'root' at ...>)
|
|
250
255
|
>>> context.item is context.root
|
|
251
256
|
True
|
|
252
257
|
>>> context.document
|
|
253
|
-
|
|
258
|
+
EtreeDocumentNode(document=<xml.etree.ElementTree.ElementTree object at ...>)
|
|
254
259
|
>>> context.root.parent is None
|
|
255
260
|
True
|
|
256
261
|
|
|
@@ -263,7 +268,7 @@ the data with lxml:
|
|
|
263
268
|
>>> root = etree.XML('<!-- comment --><root><child/></root>')
|
|
264
269
|
>>> context = XPathContext(root)
|
|
265
270
|
>>> context.root
|
|
266
|
-
|
|
271
|
+
EtreeDocumentNode(document=<lxml.etree._ElementTree object at ...>)
|
|
267
272
|
>>> context.item is context.root
|
|
268
273
|
True
|
|
269
274
|
>>> context.document is context.root
|
|
@@ -278,7 +283,7 @@ is set to `None`:
|
|
|
278
283
|
>>> root = ElementTree.XML('<root><child1/><child2/><child3/></root>')
|
|
279
284
|
>>> context = XPathContext(root, fragment=True)
|
|
280
285
|
>>> context.root
|
|
281
|
-
|
|
286
|
+
EtreeElementNode(elem=<Element 'root' at ...>)
|
|
282
287
|
>>> context.item is context.root
|
|
283
288
|
True
|
|
284
289
|
>>> context.document is None
|
|
@@ -25,13 +25,13 @@ sys.path.insert(0, os.path.abspath('..'))
|
|
|
25
25
|
# -- Project information -----------------------------------------------------
|
|
26
26
|
|
|
27
27
|
project = 'elementpath'
|
|
28
|
-
copyright = '2018-
|
|
28
|
+
copyright = '2018-2025, SISSA (International School for Advanced Studies)'
|
|
29
29
|
author = 'Davide Brunato'
|
|
30
30
|
|
|
31
31
|
# The short X.Y version
|
|
32
|
-
version = '
|
|
32
|
+
version = '5.0'
|
|
33
33
|
# The full version, including alpha/beta/rc tags
|
|
34
|
-
release = '
|
|
34
|
+
release = '5.0.0'
|
|
35
35
|
|
|
36
36
|
# -- General configuration ---------------------------------------------------
|
|
37
37
|
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
#
|
|
8
8
|
# @author Davide Brunato <brunato@sissa.it>
|
|
9
9
|
#
|
|
10
|
-
__version__ = '
|
|
10
|
+
__version__ = '5.0.0'
|
|
11
11
|
__author__ = "Davide Brunato"
|
|
12
12
|
__contact__ = "brunato@sissa.it"
|
|
13
|
-
__copyright__ = "Copyright 2018-
|
|
13
|
+
__copyright__ = "Copyright 2018-2025, SISSA"
|
|
14
14
|
__license__ = "MIT"
|
|
15
15
|
__status__ = "Production/Stable"
|
|
16
16
|
|
|
@@ -26,9 +26,9 @@ from .exceptions import ElementPathError, MissingContextError, ElementPathKeyErr
|
|
|
26
26
|
ElementPathValueError, ElementPathLocaleError, UnsupportedFeatureError
|
|
27
27
|
|
|
28
28
|
from .xpath_context import XPathContext, XPathSchemaContext
|
|
29
|
-
from .xpath_nodes import XPathNode,
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
from .xpath_nodes import XPathNode, AttributeNode, NamespaceNode, CommentNode, \
|
|
30
|
+
ProcessingInstructionNode, TextNode, ElementNode, LazyElementNode, \
|
|
31
|
+
SchemaElementNode, DocumentNode
|
|
32
32
|
from .tree_builders import get_node_tree, build_node_tree, build_lxml_node_tree, \
|
|
33
33
|
build_schema_node_tree
|
|
34
34
|
from .xpath_tokens import XPathToken, XPathFunction
|
|
@@ -39,15 +39,14 @@ from .schema_proxy import AbstractSchemaProxy
|
|
|
39
39
|
from .regex import RegexError, translate_pattern, install_unicode_data, unicode_version
|
|
40
40
|
|
|
41
41
|
__all__ = ['datatypes', 'protocols', 'etree', 'ElementPathError', 'MissingContextError',
|
|
42
|
-
'UnsupportedFeatureError', 'ElementPathKeyError',
|
|
42
|
+
'UnsupportedFeatureError', 'ElementPathKeyError', 'ElementPathLocaleError',
|
|
43
43
|
'ElementPathZeroDivisionError', 'ElementPathNameError',
|
|
44
44
|
'ElementPathOverflowError', 'ElementPathRuntimeError', 'ElementPathSyntaxError',
|
|
45
|
-
'ElementPathTypeError', 'ElementPathValueError', '
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'
|
|
50
|
-
'build_lxml_node_tree', 'build_schema_node_tree', 'XPathToken',
|
|
45
|
+
'ElementPathTypeError', 'ElementPathValueError', 'XPathContext',
|
|
46
|
+
'XPathSchemaContext', 'XPathNode', 'AttributeNode', 'NamespaceNode',
|
|
47
|
+
'CommentNode', 'ProcessingInstructionNode', 'TextNode', 'ElementNode',
|
|
48
|
+
'LazyElementNode', 'SchemaElementNode', 'DocumentNode', 'get_node_tree',
|
|
49
|
+
'build_node_tree', 'build_lxml_node_tree', 'build_schema_node_tree', 'XPathToken',
|
|
51
50
|
'XPathFunction', 'XPath1Parser', 'XPath2Parser', 'select', 'iter_select',
|
|
52
51
|
'Selector', 'AbstractSchemaProxy', 'RegexError', 'translate_pattern',
|
|
53
52
|
'install_unicode_data', 'unicode_version']
|
|
@@ -10,28 +10,27 @@
|
|
|
10
10
|
"""
|
|
11
11
|
Common type hints aliases for elementpath.
|
|
12
12
|
"""
|
|
13
|
-
from typing import Any,
|
|
13
|
+
from typing import Any, Optional, NoReturn, TYPE_CHECKING, TypeVar, Union
|
|
14
14
|
|
|
15
|
-
from
|
|
15
|
+
from collections.abc import MutableMapping
|
|
16
16
|
|
|
17
17
|
##
|
|
18
|
-
#
|
|
18
|
+
# type aliases
|
|
19
19
|
NamespacesType = MutableMapping[str, str]
|
|
20
20
|
NsmapType = MutableMapping[Optional[str], str] # compatible with the nsmap of lxml Element
|
|
21
21
|
AnyNsmapType = Union[NamespacesType, NsmapType, None] # for composition and function arguments
|
|
22
22
|
|
|
23
|
-
NargsType = Optional[Union[int,
|
|
24
|
-
ClassCheckType = Union[
|
|
23
|
+
NargsType = Optional[Union[int, tuple[int, Optional[int]]]]
|
|
24
|
+
ClassCheckType = Union[type[Any], tuple[type[Any], ...]]
|
|
25
25
|
|
|
26
26
|
T = TypeVar('T')
|
|
27
|
-
Emptiable = Union[T,
|
|
28
|
-
SequenceType = Union[T,
|
|
29
|
-
InputType = Union[None, T,
|
|
27
|
+
Emptiable = Union[T, list[NoReturn]]
|
|
28
|
+
SequenceType = Union[T, list[T]]
|
|
29
|
+
InputType = Union[None, T, list[T], tuple[T, ...]]
|
|
30
30
|
|
|
31
31
|
if TYPE_CHECKING:
|
|
32
32
|
from elementpath.datatypes import AtomicType, ArithmeticType, NumericType
|
|
33
|
-
from elementpath.xpath_nodes import ChildNodeType, ParentNodeType
|
|
34
|
-
from elementpath.tree_builders import RootArgType
|
|
33
|
+
from elementpath.xpath_nodes import ChildNodeType, ParentNodeType, RootArgType
|
|
35
34
|
from elementpath.xpath_context import ContextType, FunctionArgType, ItemType, \
|
|
36
35
|
ItemArgType, ValueType
|
|
37
36
|
from elementpath.xpath_tokens import XPathParserType, XPathTokenType
|
|
@@ -11,7 +11,7 @@ import locale
|
|
|
11
11
|
import threading
|
|
12
12
|
from contextlib import AbstractContextManager
|
|
13
13
|
from types import TracebackType
|
|
14
|
-
from typing import TYPE_CHECKING, Any, Optional,
|
|
14
|
+
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
15
15
|
from urllib.parse import urljoin, urlsplit
|
|
16
16
|
|
|
17
17
|
from elementpath.exceptions import xpath_error
|
|
@@ -76,9 +76,9 @@ class CollationManager(context_class_base):
|
|
|
76
76
|
"""
|
|
77
77
|
Context Manager for collations. Provide helper operators as methods.
|
|
78
78
|
"""
|
|
79
|
-
lc_collate: Union[None, str,
|
|
79
|
+
lc_collate: Union[None, str, tuple[Optional[str], Optional[str]]]
|
|
80
80
|
fallback: bool = False
|
|
81
|
-
_current_lc_collate: Optional[
|
|
81
|
+
_current_lc_collate: Optional[tuple[Optional[str], Optional[str]]] = None
|
|
82
82
|
|
|
83
83
|
def __init__(self,
|
|
84
84
|
collation: Optional[str],
|
|
@@ -149,7 +149,7 @@ class CollationManager(context_class_base):
|
|
|
149
149
|
|
|
150
150
|
return self
|
|
151
151
|
|
|
152
|
-
def __exit__(self, exc_type: Optional[
|
|
152
|
+
def __exit__(self, exc_type: Optional[type[BaseException]],
|
|
153
153
|
exc_val: Optional[BaseException],
|
|
154
154
|
exc_tb: Optional[TracebackType]) -> None:
|
|
155
155
|
if self._current_lc_collate is not None:
|
|
@@ -13,13 +13,13 @@ from functools import cmp_to_key
|
|
|
13
13
|
from itertools import zip_longest
|
|
14
14
|
from typing import Any, Optional
|
|
15
15
|
|
|
16
|
-
from
|
|
16
|
+
from collections.abc import Callable, Iterable, Iterator
|
|
17
17
|
from elementpath.protocols import ElementProtocol
|
|
18
18
|
from elementpath.exceptions import xpath_error
|
|
19
19
|
from elementpath.datatypes import UntypedAtomic, AnyURI, AbstractQName
|
|
20
20
|
from elementpath.collations import UNICODE_CODEPOINT_COLLATION, CollationManager
|
|
21
|
-
from elementpath.xpath_nodes import XPathNode,
|
|
22
|
-
NamespaceNode, TextNode, CommentNode, ProcessingInstructionNode,
|
|
21
|
+
from elementpath.xpath_nodes import XPathNode, EtreeElementNode, TextAttributeNode, \
|
|
22
|
+
NamespaceNode, TextNode, CommentNode, ProcessingInstructionNode, EtreeDocumentNode
|
|
23
23
|
from elementpath.xpath_tokens import XPathToken, XPathFunction, XPathMap, XPathArray
|
|
24
24
|
|
|
25
25
|
|
|
@@ -28,7 +28,7 @@ def deep_equal(seq1: Iterable[Any],
|
|
|
28
28
|
collation: Optional[str] = None,
|
|
29
29
|
token: Optional[XPathToken] = None) -> bool:
|
|
30
30
|
|
|
31
|
-
etree_node_types = (
|
|
31
|
+
etree_node_types = (EtreeElementNode, CommentNode, ProcessingInstructionNode)
|
|
32
32
|
|
|
33
33
|
def etree_deep_equal(e1: ElementProtocol, e2: ElementProtocol) -> bool:
|
|
34
34
|
if cm.ne(e1.tag, e2.tag):
|
|
@@ -72,31 +72,31 @@ def deep_equal(seq1: Iterable[Any],
|
|
|
72
72
|
return False
|
|
73
73
|
elif isinstance(value1, XPathNode):
|
|
74
74
|
assert isinstance(value2, XPathNode)
|
|
75
|
-
if value1.
|
|
75
|
+
if value1.__class__ != value2.__class__:
|
|
76
76
|
return False
|
|
77
77
|
elif isinstance(value1, etree_node_types):
|
|
78
78
|
assert isinstance(value2, etree_node_types)
|
|
79
|
-
if not etree_deep_equal(value1.
|
|
79
|
+
if not etree_deep_equal(value1.obj, value2.obj):
|
|
80
80
|
return False
|
|
81
|
-
elif isinstance(value1,
|
|
82
|
-
assert isinstance(value2,
|
|
81
|
+
elif isinstance(value1, EtreeDocumentNode):
|
|
82
|
+
assert isinstance(value2, EtreeDocumentNode)
|
|
83
83
|
for child1, child2 in zip_longest(value1, value2):
|
|
84
84
|
if child1 is None or child2 is None:
|
|
85
85
|
return False
|
|
86
|
-
elif child1.
|
|
86
|
+
elif child1.__class__ != child2.__class__:
|
|
87
87
|
return False
|
|
88
88
|
elif isinstance(child1, etree_node_types):
|
|
89
89
|
assert isinstance(child2, etree_node_types)
|
|
90
|
-
if not etree_deep_equal(child1.
|
|
90
|
+
if not etree_deep_equal(child1.obj, child2.obj):
|
|
91
91
|
return False
|
|
92
92
|
elif isinstance(child1, TextNode):
|
|
93
93
|
assert isinstance(child2, TextNode)
|
|
94
|
-
if cm.ne(child1.
|
|
94
|
+
if cm.ne(child1.obj, child2.obj):
|
|
95
95
|
return False
|
|
96
96
|
|
|
97
|
-
elif cm.ne(value1.
|
|
97
|
+
elif cm.ne(value1.obj, value2.obj):
|
|
98
98
|
return False
|
|
99
|
-
elif isinstance(value1,
|
|
99
|
+
elif isinstance(value1, TextAttributeNode):
|
|
100
100
|
if cm.ne(value1.name, value2.name):
|
|
101
101
|
return False
|
|
102
102
|
elif isinstance(value1, NamespaceNode):
|
|
@@ -174,7 +174,7 @@ def deep_compare(obj1: Any,
|
|
|
174
174
|
token: Optional[XPathToken] = None) -> int:
|
|
175
175
|
|
|
176
176
|
msg_tmpl = "Sorting failed, cannot compare {!r} with {!r}"
|
|
177
|
-
etree_node_types = (
|
|
177
|
+
etree_node_types = (EtreeElementNode, CommentNode, ProcessingInstructionNode)
|
|
178
178
|
result: int = 0
|
|
179
179
|
|
|
180
180
|
def iter_object(obj: Any) -> Iterator[Any]:
|
|
@@ -244,51 +244,51 @@ def deep_compare(obj1: Any,
|
|
|
244
244
|
raise xpath_error('XPTY0004', msg, token=token)
|
|
245
245
|
elif isinstance(value1, XPathNode):
|
|
246
246
|
assert isinstance(value2, XPathNode)
|
|
247
|
-
if value1.
|
|
247
|
+
if value1.__class__ != value2.__class__:
|
|
248
248
|
msg = f"cannot compare {type(value1)} with {type(value2)}"
|
|
249
249
|
raise xpath_error('XPTY0004', msg, token=token)
|
|
250
250
|
elif isinstance(value1, etree_node_types):
|
|
251
251
|
assert isinstance(value2, etree_node_types)
|
|
252
|
-
result = etree_deep_compare(value1.
|
|
252
|
+
result = etree_deep_compare(value1.obj, value2.obj)
|
|
253
253
|
if result:
|
|
254
254
|
return result
|
|
255
|
-
elif isinstance(value1,
|
|
256
|
-
assert isinstance(value2,
|
|
255
|
+
elif isinstance(value1, EtreeDocumentNode):
|
|
256
|
+
assert isinstance(value2, EtreeDocumentNode)
|
|
257
257
|
for child1, child2 in zip_longest(value1, value2):
|
|
258
258
|
if child1 is None:
|
|
259
259
|
return -1
|
|
260
260
|
elif child2 is None:
|
|
261
261
|
return 1
|
|
262
|
-
elif child1.
|
|
262
|
+
elif child1.__class__ != child2.__class__:
|
|
263
263
|
msg = f"cannot compare {type(child1)} with {type(child2)}"
|
|
264
264
|
raise xpath_error('XPTY0004', msg, token=token)
|
|
265
265
|
elif isinstance(child1, etree_node_types):
|
|
266
266
|
assert isinstance(child2, etree_node_types)
|
|
267
|
-
result = etree_deep_compare(child1.
|
|
267
|
+
result = etree_deep_compare(child1.obj, child2.obj)
|
|
268
268
|
if result:
|
|
269
269
|
return result
|
|
270
270
|
elif isinstance(child1, TextNode):
|
|
271
271
|
assert isinstance(child2, TextNode)
|
|
272
272
|
result = cm.strcoll(
|
|
273
|
-
child1.
|
|
273
|
+
child1.obj.strip(), child2.obj.strip()
|
|
274
274
|
)
|
|
275
275
|
if result:
|
|
276
276
|
return result
|
|
277
|
-
|
|
278
|
-
|
|
277
|
+
elif isinstance(value1, TextNode):
|
|
278
|
+
assert isinstance(value2, TextNode)
|
|
279
|
+
result = cm.strcoll(value1.obj, value2.obj)
|
|
280
|
+
if result:
|
|
281
|
+
return result
|
|
282
|
+
elif isinstance(value1, TextAttributeNode):
|
|
283
|
+
assert isinstance(value2, TextAttributeNode)
|
|
284
|
+
result = cm.strcoll(value1.name or '', value2.name or '')
|
|
285
|
+
if result:
|
|
286
|
+
return result
|
|
287
|
+
elif isinstance(value1, NamespaceNode):
|
|
288
|
+
assert isinstance(value2, NamespaceNode)
|
|
289
|
+
result = cm.strcoll(value1.prefix or '', value2.prefix or '')
|
|
279
290
|
if result:
|
|
280
291
|
return result
|
|
281
|
-
|
|
282
|
-
if isinstance(value1, AttributeNode):
|
|
283
|
-
assert isinstance(value2, AttributeNode)
|
|
284
|
-
result = cm.strcoll(value1.name or '', value2.name or '')
|
|
285
|
-
if result:
|
|
286
|
-
return result
|
|
287
|
-
elif isinstance(value1, NamespaceNode):
|
|
288
|
-
assert isinstance(value2, NamespaceNode)
|
|
289
|
-
result = cm.strcoll(value1.prefix or '', value2.prefix or '')
|
|
290
|
-
if result:
|
|
291
|
-
return result
|
|
292
292
|
else:
|
|
293
293
|
try:
|
|
294
294
|
if isinstance(value1, bool):
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#
|
|
2
|
-
# Copyright (c), 2018-
|
|
2
|
+
# Copyright (c), 2018-2025, SISSA (International School for Advanced Studies).
|
|
3
3
|
# All rights reserved.
|
|
4
4
|
# This file is distributed under the terms of the MIT License.
|
|
5
5
|
# See the file 'LICENSE' in the root directory of the present
|
|
@@ -15,8 +15,7 @@ exceptions in order to be reusable in other packages.
|
|
|
15
15
|
from decimal import Decimal
|
|
16
16
|
from typing import Union
|
|
17
17
|
|
|
18
|
-
from .atomic_types import
|
|
19
|
-
AtomicTypeMeta, AnyAtomicType
|
|
18
|
+
from .atomic_types import xsd_atomic_types, AtomicTypeMeta, AnyAtomicType
|
|
20
19
|
from .untyped import UntypedAtomic
|
|
21
20
|
from .qname import AbstractQName, QName, Notation
|
|
22
21
|
from .numeric import Float10, Float, Integer, Int, NegativeInteger, \
|
|
@@ -33,22 +32,15 @@ from .datetime import AbstractDateTime, DateTime10, DateTime, DateTimeStamp, \
|
|
|
33
32
|
from .proxies import BooleanProxy, DecimalProxy, DoubleProxy10, DoubleProxy, \
|
|
34
33
|
StringProxy, NumericProxy, ArithmeticProxy
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
xsd11_atomic_types.update(
|
|
38
|
-
(k, v) for k, v in xsd10_atomic_types.items() if k not in xsd11_atomic_types
|
|
39
|
-
)
|
|
40
|
-
|
|
41
35
|
###
|
|
42
36
|
# Aliases for type annotations
|
|
43
37
|
AtomicType = Union[str, int, float, Decimal, bool, AnyAtomicType]
|
|
44
38
|
NumericType = Union[int, float, Decimal]
|
|
45
39
|
ArithmeticType = Union[NumericType, AbstractDateTime, Duration, UntypedAtomic]
|
|
46
|
-
DatetimeValueType = AbstractDateTime # keep until v5.0 for backward compatibility
|
|
47
40
|
|
|
48
|
-
__all__ = ['
|
|
49
|
-
'
|
|
50
|
-
'
|
|
51
|
-
'Date', 'Time', 'GregorianDay', 'GregorianMonth', 'GregorianMonthDay',
|
|
41
|
+
__all__ = ['xsd_atomic_types', 'AtomicTypeMeta', 'AnyAtomicType', 'NumericProxy',
|
|
42
|
+
'ArithmeticProxy', 'AbstractDateTime', 'DateTime10', 'DateTime', 'DateTimeStamp',
|
|
43
|
+
'Date10', 'Date', 'Time', 'GregorianDay', 'GregorianMonth', 'GregorianMonthDay',
|
|
52
44
|
'GregorianYear10', 'GregorianYear', 'GregorianYearMonth10', 'GregorianYearMonth',
|
|
53
45
|
'Timezone', 'Duration', 'YearMonthDuration', 'DayTimeDuration', 'StringProxy',
|
|
54
46
|
'NormalizedString', 'XsdToken', 'Language', 'Name', 'NCName', 'Id', 'Idref',
|
|
@@ -57,5 +49,4 @@ __all__ = ['xsd10_atomic_types', 'xsd11_atomic_types',
|
|
|
57
49
|
'Byte', 'NonNegativeInteger', 'PositiveInteger', 'UnsignedLong', 'UnsignedInt',
|
|
58
50
|
'UnsignedShort', 'UnsignedByte', 'AnyURI', 'Notation', 'QName', 'BooleanProxy',
|
|
59
51
|
'DecimalProxy', 'DoubleProxy10', 'DoubleProxy', 'UntypedAtomic', 'AbstractBinary',
|
|
60
|
-
'AtomicType', '
|
|
61
|
-
'NumericType', 'ArithmeticType']
|
|
52
|
+
'AtomicType', 'OrderedDateTime', 'AbstractQName', 'NumericType', 'ArithmeticType']
|
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
# @author Davide Brunato <brunato@sissa.it>
|
|
9
9
|
#
|
|
10
10
|
from abc import ABCMeta, abstractmethod
|
|
11
|
-
from
|
|
11
|
+
from types import MappingProxyType
|
|
12
|
+
from typing import Any, Optional
|
|
12
13
|
import re
|
|
13
14
|
|
|
14
|
-
from elementpath._typing import Pattern
|
|
15
|
-
|
|
16
15
|
XSD_NAMESPACE = "http://www.w3.org/2001/XMLSchema"
|
|
17
16
|
|
|
18
17
|
###
|
|
@@ -21,11 +20,16 @@ XSD_NAMESPACE = "http://www.w3.org/2001/XMLSchema"
|
|
|
21
20
|
# into a dictionary. Some classes of XSD primitive types are defined
|
|
22
21
|
# as proxies of basic Python datatypes.
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
_xsd_atomic_types: dict[str, dict[Optional[str], 'AtomicTypeMeta']] = {
|
|
24
|
+
'1.0': {},
|
|
25
|
+
'1.1': {}
|
|
26
|
+
}
|
|
27
|
+
"""Registry of builtin XSD 1.0/1.1 atomic types."""
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
xsd_atomic_types = MappingProxyType({
|
|
30
|
+
'1.0': MappingProxyType(_xsd_atomic_types['1.0']),
|
|
31
|
+
'1.1': MappingProxyType(_xsd_atomic_types['1.1']),
|
|
32
|
+
})
|
|
29
33
|
|
|
30
34
|
|
|
31
35
|
class AtomicTypeMeta(ABCMeta):
|
|
@@ -36,10 +40,8 @@ class AtomicTypeMeta(ABCMeta):
|
|
|
36
40
|
of XSD atomic types and also the expanded name is added.
|
|
37
41
|
"""
|
|
38
42
|
xsd_version: str
|
|
39
|
-
pattern: Pattern[str]
|
|
40
|
-
name: Optional[str] = None
|
|
41
43
|
|
|
42
|
-
def __new__(mcs, class_name: str, bases:
|
|
44
|
+
def __new__(mcs, class_name: str, bases: tuple[type[Any], ...], dict_: dict[str, Any]) \
|
|
43
45
|
-> 'AtomicTypeMeta':
|
|
44
46
|
try:
|
|
45
47
|
name = dict_['name']
|
|
@@ -49,28 +51,26 @@ class AtomicTypeMeta(ABCMeta):
|
|
|
49
51
|
if name is not None and not isinstance(name, str):
|
|
50
52
|
raise TypeError("attribute 'name' must be a string or None")
|
|
51
53
|
|
|
52
|
-
dict_['is_valid'] = classmethod(mcs.is_valid)
|
|
53
|
-
dict_['invalid_type'] = classmethod(mcs.invalid_type)
|
|
54
|
-
dict_['invalid_value'] = classmethod(mcs.invalid_value)
|
|
55
54
|
cls = super(AtomicTypeMeta, mcs).__new__(mcs, class_name, bases, dict_)
|
|
56
55
|
|
|
57
|
-
#
|
|
58
|
-
if not hasattr(cls, 'xsd_version'):
|
|
59
|
-
cls.xsd_version = '1.0'
|
|
60
|
-
if not hasattr(cls, 'pattern'):
|
|
61
|
-
cls.pattern = re.compile(r'^$')
|
|
62
|
-
|
|
63
|
-
# Register class with a name
|
|
56
|
+
# Register ony derived classes with a name
|
|
64
57
|
if name:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
58
|
+
for xsd_version, atomic_types in _xsd_atomic_types.items():
|
|
59
|
+
if cls.xsd_version <= xsd_version and (
|
|
60
|
+
name not in atomic_types or atomic_types[name].xsd_version < cls.xsd_version
|
|
61
|
+
):
|
|
62
|
+
atomic_types[name] = cls
|
|
70
63
|
|
|
71
64
|
return cls
|
|
72
65
|
|
|
73
|
-
|
|
66
|
+
|
|
67
|
+
class AnyAtomicType(metaclass=AtomicTypeMeta):
|
|
68
|
+
name: Optional[str] = 'anyAtomicType'
|
|
69
|
+
xsd_version: str = '1.0'
|
|
70
|
+
pattern: re.Pattern[str] = re.compile(r'^$')
|
|
71
|
+
|
|
72
|
+
@classmethod
|
|
73
|
+
def validate(cls, value: object) -> None:
|
|
74
74
|
if isinstance(value, cls):
|
|
75
75
|
return
|
|
76
76
|
elif isinstance(value, str):
|
|
@@ -79,7 +79,8 @@ class AtomicTypeMeta(ABCMeta):
|
|
|
79
79
|
else:
|
|
80
80
|
raise cls.invalid_type(value)
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
@classmethod
|
|
83
|
+
def is_valid(cls, value: object) -> bool:
|
|
83
84
|
try:
|
|
84
85
|
cls.validate(value)
|
|
85
86
|
except (TypeError, ValueError):
|
|
@@ -87,20 +88,18 @@ class AtomicTypeMeta(ABCMeta):
|
|
|
87
88
|
else:
|
|
88
89
|
return True
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
@classmethod
|
|
92
|
+
def invalid_type(cls, value: object) -> TypeError:
|
|
91
93
|
if cls.name:
|
|
92
94
|
return TypeError('invalid type {!r} for xs:{}'.format(type(value), cls.name))
|
|
93
95
|
return TypeError('invalid type {!r} for {!r}'.format(type(value), cls))
|
|
94
96
|
|
|
95
|
-
|
|
97
|
+
@classmethod
|
|
98
|
+
def invalid_value(cls, value: object) -> ValueError:
|
|
96
99
|
if cls.name:
|
|
97
100
|
return ValueError('invalid value {!r} for xs:{}'.format(value, cls.name))
|
|
98
101
|
return ValueError('invalid value {!r} for {!r}'.format(value, cls))
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
class AnyAtomicType(metaclass=AtomicTypeMeta):
|
|
102
|
-
name = 'anyAtomicType'
|
|
103
|
-
|
|
104
103
|
@abstractmethod
|
|
105
104
|
def __init__(self, value: Any) -> None:
|
|
106
105
|
raise NotImplementedError()
|