elementpath 4.6.0__tar.gz → 4.8.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.
Files changed (133) hide show
  1. {elementpath-4.6.0 → elementpath-4.8.0}/CHANGELOG.rst +14 -0
  2. {elementpath-4.6.0 → elementpath-4.8.0}/PKG-INFO +12 -2
  3. {elementpath-4.6.0 → elementpath-4.8.0}/doc/advanced.rst +13 -8
  4. {elementpath-4.6.0 → elementpath-4.8.0}/doc/conf.py +3 -3
  5. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/__init__.py +11 -12
  6. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/aliases.py +1 -2
  7. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/compare.py +33 -33
  8. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/datetime.py +8 -1
  9. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/numeric.py +1 -1
  10. elementpath-4.8.0/elementpath/decoder.py +170 -0
  11. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/etree.py +26 -6
  12. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/protocols.py +41 -12
  13. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/schema_proxy.py +63 -7
  14. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/sequence_types.py +9 -8
  15. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/serialization.py +6 -6
  16. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/tree_builders.py +97 -68
  17. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath1/_xpath1_axes.py +6 -4
  18. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath1/_xpath1_functions.py +12 -10
  19. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath1/_xpath1_operators.py +32 -120
  20. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath1/xpath1_parser.py +21 -4
  21. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath2/_xpath2_constructors.py +14 -0
  22. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath2/_xpath2_functions.py +45 -43
  23. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath2/_xpath2_operators.py +22 -33
  24. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath2/xpath2_parser.py +3 -21
  25. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath30/_xpath30_functions.py +27 -67
  26. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath30/_xpath30_operators.py +2 -6
  27. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath31/_xpath31_functions.py +5 -5
  28. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath31/_xpath31_operators.py +17 -8
  29. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath_context.py +146 -50
  30. elementpath-4.8.0/elementpath/xpath_nodes.py +1752 -0
  31. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath_selectors.py +70 -33
  32. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath_tokens.py +187 -264
  33. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath.egg-info/PKG-INFO +12 -2
  34. {elementpath-4.6.0 → elementpath-4.8.0}/setup.py +1 -1
  35. {elementpath-4.6.0 → elementpath-4.8.0}/tests/execute_w3c_tests.py +7 -3
  36. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_compare.py +5 -5
  37. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_datatypes.py +35 -9
  38. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_schema_context.py +0 -79
  39. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_schema_proxy.py +39 -57
  40. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_selectors.py +44 -0
  41. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_sequence_types.py +1 -1
  42. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_serialization.py +2 -2
  43. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath1_parser.py +6 -6
  44. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath2_constructors.py +9 -7
  45. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath2_functions.py +7 -6
  46. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath2_parser.py +48 -34
  47. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath30.py +10 -5
  48. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath31.py +33 -0
  49. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath_context.py +22 -18
  50. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath_nodes.py +77 -62
  51. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_xpath_tokens.py +38 -285
  52. {elementpath-4.6.0 → elementpath-4.8.0}/tests/xpath_test_class.py +5 -4
  53. {elementpath-4.6.0 → elementpath-4.8.0}/tox.ini +20 -9
  54. elementpath-4.6.0/elementpath/decoder.py +0 -176
  55. elementpath-4.6.0/elementpath/xpath_nodes.py +0 -1048
  56. {elementpath-4.6.0 → elementpath-4.8.0}/.coveragerc +0 -0
  57. {elementpath-4.6.0 → elementpath-4.8.0}/LICENSE +0 -0
  58. {elementpath-4.6.0 → elementpath-4.8.0}/MANIFEST.in +0 -0
  59. {elementpath-4.6.0 → elementpath-4.8.0}/README.rst +0 -0
  60. {elementpath-4.6.0 → elementpath-4.8.0}/doc/Makefile +0 -0
  61. {elementpath-4.6.0 → elementpath-4.8.0}/doc/index.rst +0 -0
  62. {elementpath-4.6.0 → elementpath-4.8.0}/doc/introduction.rst +0 -0
  63. {elementpath-4.6.0 → elementpath-4.8.0}/doc/make.bat +0 -0
  64. {elementpath-4.6.0 → elementpath-4.8.0}/doc/pratt_api.rst +0 -0
  65. {elementpath-4.6.0 → elementpath-4.8.0}/doc/requirements.txt +0 -0
  66. {elementpath-4.6.0 → elementpath-4.8.0}/doc/xpath_api.rst +0 -0
  67. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/_typing.py +0 -0
  68. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/collations.py +0 -0
  69. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/__init__.py +0 -0
  70. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/atomic_types.py +0 -0
  71. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/binary.py +0 -0
  72. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/proxies.py +0 -0
  73. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/qname.py +0 -0
  74. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/string.py +0 -0
  75. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/untyped.py +0 -0
  76. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/datatypes/uri.py +0 -0
  77. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/exceptions.py +0 -0
  78. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/helpers.py +0 -0
  79. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/namespaces.py +0 -0
  80. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/py.typed +0 -0
  81. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/regex/__init__.py +0 -0
  82. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/regex/character_classes.py +0 -0
  83. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/regex/codepoints.py +0 -0
  84. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/regex/patterns.py +0 -0
  85. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/regex/unicode_blocks.py +0 -0
  86. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/regex/unicode_categories.py +0 -0
  87. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/regex/unicode_subsets.py +0 -0
  88. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/tdop.py +0 -0
  89. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/validators/__init__.py +0 -0
  90. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/validators/analyze-string.xsd +0 -0
  91. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/validators/schema-for-json.xsd +0 -0
  92. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath1/__init__.py +0 -0
  93. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath2/__init__.py +0 -0
  94. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath3.py +0 -0
  95. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath30/__init__.py +0 -0
  96. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath30/_translation_maps.py +0 -0
  97. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath30/xpath30_helpers.py +0 -0
  98. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath30/xpath30_parser.py +0 -0
  99. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath31/__init__.py +0 -0
  100. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath/xpath31/xpath31_parser.py +0 -0
  101. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath.egg-info/SOURCES.txt +0 -0
  102. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath.egg-info/dependency_links.txt +0 -0
  103. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath.egg-info/requires.txt +0 -0
  104. {elementpath-4.6.0 → elementpath-4.8.0}/elementpath.egg-info/top_level.txt +0 -0
  105. {elementpath-4.6.0 → elementpath-4.8.0}/mypy.ini +0 -0
  106. {elementpath-4.6.0 → elementpath-4.8.0}/requirements-dev.txt +0 -0
  107. {elementpath-4.6.0 → elementpath-4.8.0}/scripts/generate_codepoints.py +0 -0
  108. {elementpath-4.6.0 → elementpath-4.8.0}/setup.cfg +0 -0
  109. {elementpath-4.6.0 → elementpath-4.8.0}/tests/__init__.py +0 -0
  110. {elementpath-4.6.0 → elementpath-4.8.0}/tests/memory_profiling.py +0 -0
  111. {elementpath-4.6.0 → elementpath-4.8.0}/tests/mypy_tests/advanced.py +0 -0
  112. {elementpath-4.6.0 → elementpath-4.8.0}/tests/mypy_tests/protocols.py +0 -0
  113. {elementpath-4.6.0 → elementpath-4.8.0}/tests/mypy_tests/selectors.py +0 -0
  114. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/analyze-string.xsd +0 -0
  115. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/external_entity.xml +0 -0
  116. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/sample.xml +0 -0
  117. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/schema-for-json.xsd +0 -0
  118. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/unparsed_entity.xml +0 -0
  119. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/unused_external_entity.xml +0 -0
  120. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/unused_unparsed_entity.xml +0 -0
  121. {elementpath-4.6.0 → elementpath-4.8.0}/tests/resources/with_entity.xml +0 -0
  122. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_collations.py +0 -0
  123. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_elementpath.py +0 -0
  124. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_etree.py +0 -0
  125. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_exceptions.py +0 -0
  126. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_helpers.py +0 -0
  127. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_namespaces.py +0 -0
  128. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_package.py +0 -0
  129. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_regex.py +0 -0
  130. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_tdop_parser.py +0 -0
  131. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_tree_builders.py +0 -0
  132. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_typing.py +0 -0
  133. {elementpath-4.6.0 → elementpath-4.8.0}/tests/test_validators.py +0 -0
@@ -2,6 +2,18 @@
2
2
  CHANGELOG
3
3
  *********
4
4
 
5
+ `v4.8.0`_ (2025-03-03)
6
+ ======================
7
+ * Add full PSVI type labeling in XDM to solve type errors with XSD 1.1 assertions
8
+ * Add *schema* optional argument to dynamic context
9
+ * Add a RootToken as a proxy of the parsed token tree for compatibility with xmlschema<=3.4.3
10
+ * Extend XDM to split ElementTree/lxml processing from schema nodes and to allow future extensions
11
+
12
+ `v4.7.0`_ (2024-12-20)
13
+ ======================
14
+ * Fix *fragment* argument usage (issue #81)
15
+ * Fix constructors nud() to skip argument check with XP31+ arrow operator (issue #83)
16
+
5
17
  `v4.6.0`_ (2024-10-27)
6
18
  ======================
7
19
  * Fix XsdAttributeGroupProtocol
@@ -478,3 +490,5 @@ CHANGELOG
478
490
  .. _v4.4.0: https://github.com/sissaschool/elementpath/compare/v4.3.0...v4.4.0
479
491
  .. _v4.5.0: https://github.com/sissaschool/elementpath/compare/v4.4.0...v4.5.0
480
492
  .. _v4.6.0: https://github.com/sissaschool/elementpath/compare/v4.5.0...v4.6.0
493
+ .. _v4.7.0: https://github.com/sissaschool/elementpath/compare/v4.6.0...v4.7.0
494
+ .. _v4.8.0: https://github.com/sissaschool/elementpath/compare/v4.7.0...v4.8.0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: elementpath
3
- Version: 4.6.0
3
+ Version: 4.8.0
4
4
  Summary: XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml
5
5
  Home-page: https://github.com/sissaschool/elementpath
6
6
  Author: Davide Brunato
@@ -40,6 +40,16 @@ Requires-Dist: memray; extra == "dev"
40
40
  Requires-Dist: flake8; extra == "dev"
41
41
  Requires-Dist: mypy; extra == "dev"
42
42
  Requires-Dist: lxml-stubs; extra == "dev"
43
+ Dynamic: author
44
+ Dynamic: author-email
45
+ Dynamic: classifier
46
+ Dynamic: description
47
+ Dynamic: home-page
48
+ Dynamic: keywords
49
+ Dynamic: license
50
+ Dynamic: provides-extra
51
+ Dynamic: requires-python
52
+ Dynamic: summary
43
53
 
44
54
  ***********
45
55
  elementpath
@@ -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
- [ElementNode(elem=<Element 'child' at ...)]
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
- ElementNode(elem=<Element 'root' at ...>)
132
+ EtreeElementNode(elem=<Element 'root' at ...>)
128
133
  >>> context.root.children
129
- [ElementNode(elem=<Element 'child' at ...>), ElementNode(elem=<Element 'child' at ...>)]
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
- DocumentNode(document=<xml.etree.ElementTree.ElementTree object at ...>)
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
- ElementNode(elem=<Element 'root' at ...>)
254
+ EtreeElementNode(elem=<Element 'root' at ...>)
250
255
  >>> context.item is context.root
251
256
  True
252
257
  >>> context.document
253
- DocumentNode(document=<xml.etree.ElementTree.ElementTree object at ...>)
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
- DocumentNode(document=<lxml.etree._ElementTree object at ...>)
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
- ElementNode(elem=<Element 'root' at ...>)
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-2024, SISSA (International School for Advanced Studies)'
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 = '4.6'
32
+ version = '4.8'
33
33
  # The full version, including alpha/beta/rc tags
34
- release = '4.6.0'
34
+ release = '4.8.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__ = '4.6.0'
10
+ __version__ = '4.8.0'
11
11
  __author__ = "Davide Brunato"
12
12
  __contact__ = "brunato@sissa.it"
13
- __copyright__ = "Copyright 2018-2024, SISSA"
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, DocumentNode, ElementNode, AttributeNode, \
30
- NamespaceNode, CommentNode, ProcessingInstructionNode, TextNode, \
31
- LazyElementNode, SchemaElementNode
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', 'ElementPathLocaleError',
46
- 'XPathContext', 'XPathSchemaContext', 'XPathNode', 'DocumentNode',
47
- 'ElementNode', 'AttributeNode', 'NamespaceNode', 'CommentNode',
48
- 'ProcessingInstructionNode', 'TextNode', 'LazyElementNode',
49
- 'SchemaElementNode', 'get_node_tree', 'build_node_tree',
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']
@@ -30,8 +30,7 @@ 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
@@ -18,8 +18,8 @@ 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, ElementNode, AttributeNode, \
22
- NamespaceNode, TextNode, CommentNode, ProcessingInstructionNode, DocumentNode
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 = (ElementNode, CommentNode, ProcessingInstructionNode)
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.kind != value2.kind:
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.elem, value2.elem):
79
+ if not etree_deep_equal(value1.obj, value2.obj):
80
80
  return False
81
- elif isinstance(value1, DocumentNode):
82
- assert isinstance(value2, DocumentNode)
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.kind != child2.kind:
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.elem, child2.elem):
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.value, child2.value):
94
+ if cm.ne(child1.obj, child2.obj):
95
95
  return False
96
96
 
97
- elif cm.ne(value1.value, value2.value):
97
+ elif cm.ne(value1.obj, value2.obj):
98
98
  return False
99
- elif isinstance(value1, AttributeNode):
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 = (ElementNode, CommentNode, ProcessingInstructionNode)
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.kind != value2.kind:
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.elem, value2.elem)
252
+ result = etree_deep_compare(value1.obj, value2.obj)
253
253
  if result:
254
254
  return result
255
- elif isinstance(value1, DocumentNode):
256
- assert isinstance(value2, DocumentNode)
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.kind != child2.kind:
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.elem, child2.elem)
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.value.strip(), child2.value.strip()
273
+ child1.obj.strip(), child2.obj.strip()
274
274
  )
275
275
  if result:
276
276
  return result
277
- else:
278
- result = cm.strcoll(value1.value, value2.value)
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):
@@ -131,8 +131,15 @@ class AbstractDateTime(AnyAtomicType):
131
131
  tzinfo: Optional[datetime.tzinfo] = None) -> None:
132
132
 
133
133
  if hour == 24 and minute == second == microsecond == 0:
134
- delta = datetime.timedelta(days=1)
135
134
  hour = 0
135
+ if year == 9999 and month == 12 and day == 31:
136
+ delta = datetime.timedelta(0)
137
+ year = 10000
138
+ month = 1
139
+ day = 1
140
+ else:
141
+ delta = datetime.timedelta(days=1)
142
+ hour = 0
136
143
  else:
137
144
  delta = datetime.timedelta(0)
138
145
 
@@ -169,7 +169,7 @@ class Integer(int, AnyAtomicType):
169
169
  def __subclasshook__(cls, subclass: Type[Any]) -> bool:
170
170
  if cls is Integer:
171
171
  return issubclass(subclass, int) and not issubclass(subclass, bool)
172
- return NotImplemented
172
+ return NotImplemented # type: ignore[no-any-return,unused-ignore]
173
173
 
174
174
  @classmethod
175
175
  def validate(cls, value: object) -> None:
@@ -0,0 +1,170 @@
1
+ #
2
+ # Copyright (c), 2024, SISSA (International School for Advanced Studies).
3
+ # All rights reserved.
4
+ # This file is distributed under the terms of the MIT License.
5
+ # See the file 'LICENSE' in the root directory of the present
6
+ # distribution, or http://opensource.org/licenses/MIT.
7
+ #
8
+ # @author Davide Brunato <brunato@sissa.it>
9
+ #
10
+ """
11
+ XSD atomic datatypes subpackage. Includes a class for UntypedAtomic data and
12
+ classes for other XSD built-in types. This subpackage raises only built-in
13
+ exceptions in order to be reusable in other packages.
14
+ """
15
+ from collections import namedtuple
16
+ from decimal import Decimal
17
+ from functools import lru_cache
18
+ from typing import List, Optional, Type
19
+
20
+ from elementpath._typing import Callable, Iterator, MutableMapping
21
+ from elementpath.aliases import AnyNsmapType
22
+ from elementpath.datatypes import AtomicType
23
+ from elementpath.protocols import XsdTypeProtocol
24
+ from elementpath.exceptions import xpath_error
25
+ from elementpath.namespaces import XSD_NAMESPACE
26
+
27
+ import elementpath.datatypes as dt
28
+
29
+ DecoderType = Callable[[str, AnyNsmapType], AtomicType]
30
+
31
+ Builder = namedtuple('Builder', 'cls text nsmap', defaults=(None, None))
32
+
33
+
34
+ class _Notation(dt.Notation):
35
+ """An instantiable xs:NOTATION."""
36
+
37
+
38
+ # noinspection PyArgumentList
39
+ ATOMIC_BUILDERS: MutableMapping[Optional[str], Builder] = {
40
+ f'{{{XSD_NAMESPACE}}}untypedAtomic': Builder(dt.UntypedAtomic, '1'),
41
+ f'{{{XSD_NAMESPACE}}}anyType': Builder(dt.UntypedAtomic, '1'),
42
+ f'{{{XSD_NAMESPACE}}}anySimpleType': Builder(dt.UntypedAtomic, '1'),
43
+ f'{{{XSD_NAMESPACE}}}anyAtomicType': Builder(dt.UntypedAtomic, '1'),
44
+ f'{{{XSD_NAMESPACE}}}boolean': Builder(bool, 'true'),
45
+ f'{{{XSD_NAMESPACE}}}decimal': Builder(Decimal, '1.0'),
46
+ f'{{{XSD_NAMESPACE}}}double': Builder(float, '1.0'),
47
+ f'{{{XSD_NAMESPACE}}}float': Builder(dt.Float10, '1.0'),
48
+ f'{{{XSD_NAMESPACE}}}string': Builder(str, ' alpha\t'),
49
+ f'{{{XSD_NAMESPACE}}}date': Builder(dt.Date, '2000-01-01'),
50
+ f'{{{XSD_NAMESPACE}}}dateTime': Builder(dt.DateTime, '2000-01-01T12:00:00'),
51
+ f'{{{XSD_NAMESPACE}}}gDay': Builder(dt.GregorianDay, '---31'),
52
+ f'{{{XSD_NAMESPACE}}}gMonth': Builder(dt.GregorianMonth, '--12'),
53
+ f'{{{XSD_NAMESPACE}}}gMonthDay': Builder(dt.GregorianMonthDay, '--12-01'),
54
+ f'{{{XSD_NAMESPACE}}}gYear': Builder(dt.GregorianYear, '1999'),
55
+ f'{{{XSD_NAMESPACE}}}gYearMonth': Builder(dt.GregorianYearMonth, '1999-09'),
56
+ f'{{{XSD_NAMESPACE}}}time': Builder(dt.Time, '09:26:54'),
57
+ f'{{{XSD_NAMESPACE}}}duration': Builder(dt.Duration, 'P1MT1S'),
58
+ f'{{{XSD_NAMESPACE}}}dayTimeDuration': Builder(dt.DayTimeDuration, 'P1DT1S'),
59
+ f'{{{XSD_NAMESPACE}}}yearMonthDuration': Builder(dt.YearMonthDuration, 'P1Y1M'),
60
+ f'{{{XSD_NAMESPACE}}}QName': Builder(dt.QName, 'xs:element'),
61
+ f'{{{XSD_NAMESPACE}}}NOTATION': Builder(_Notation, 'xs:element'),
62
+ f'{{{XSD_NAMESPACE}}}anyURI': Builder(dt.AnyURI, 'https://example.com'),
63
+ f'{{{XSD_NAMESPACE}}}normalizedString': Builder(dt.NormalizedString, ' alpha '),
64
+ f'{{{XSD_NAMESPACE}}}token': Builder(dt.XsdToken, 'a token'),
65
+ f'{{{XSD_NAMESPACE}}}language': Builder(dt.Language, 'en-US'),
66
+ f'{{{XSD_NAMESPACE}}}Name': Builder(dt.Name, '_a.name::'),
67
+ f'{{{XSD_NAMESPACE}}}NCName': Builder(dt.NCName, 'nc-name'),
68
+ f'{{{XSD_NAMESPACE}}}ID': Builder(dt.Id, 'id1'),
69
+ f'{{{XSD_NAMESPACE}}}IDREF': Builder(dt.Idref, 'id_ref1'),
70
+ f'{{{XSD_NAMESPACE}}}ENTITY': Builder(dt.Entity, 'entity1'),
71
+ f'{{{XSD_NAMESPACE}}}NMTOKEN': Builder(dt.NMToken, 'a_token'),
72
+ f'{{{XSD_NAMESPACE}}}base64Binary': Builder(dt.Base64Binary, 'YWxwaGE='),
73
+ f'{{{XSD_NAMESPACE}}}hexBinary': Builder(dt.HexBinary, '31'),
74
+ f'{{{XSD_NAMESPACE}}}dateTimeStamp':
75
+ Builder(dt.DateTimeStamp.fromstring, '2000-01-01T12:00:00+01:00'),
76
+ f'{{{XSD_NAMESPACE}}}integer': Builder(dt.Integer, '1'),
77
+ f'{{{XSD_NAMESPACE}}}long': Builder(dt.Long, '1'),
78
+ f'{{{XSD_NAMESPACE}}}int': Builder(dt.Int, '1'),
79
+ f'{{{XSD_NAMESPACE}}}short': Builder(dt.Short, '1'),
80
+ f'{{{XSD_NAMESPACE}}}byte': Builder(dt.Byte, '1'),
81
+ f'{{{XSD_NAMESPACE}}}positiveInteger': Builder(dt.PositiveInteger, '1'),
82
+ f'{{{XSD_NAMESPACE}}}negativeInteger': Builder(dt.NegativeInteger, '-1'),
83
+ f'{{{XSD_NAMESPACE}}}nonPositiveInteger': Builder(dt.NonPositiveInteger, '0'),
84
+ f'{{{XSD_NAMESPACE}}}nonNegativeInteger': Builder(dt.NonNegativeInteger, '0'),
85
+ f'{{{XSD_NAMESPACE}}}unsignedLong': Builder(dt.UnsignedLong, '1'),
86
+ f'{{{XSD_NAMESPACE}}}unsignedInt': Builder(dt.UnsignedInt, '1'),
87
+ f'{{{XSD_NAMESPACE}}}unsignedShort': Builder(dt.UnsignedShort, '1'),
88
+ f'{{{XSD_NAMESPACE}}}unsignedByte': Builder(dt.UnsignedByte, '1'),
89
+ }
90
+
91
+
92
+ @lru_cache(maxsize=None)
93
+ def get_builders(xsd_type: XsdTypeProtocol) -> List[Builder]:
94
+ """
95
+ Returns a list of atomic builtin XSD types that are in the base type of
96
+ the XSD type argument.
97
+ """
98
+ def iter_builders(root_type: XsdTypeProtocol, depth: int) -> Iterator[Builder]:
99
+ if depth > 15:
100
+ return
101
+ if root_type.name in ATOMIC_BUILDERS:
102
+ yield ATOMIC_BUILDERS[root_type.name]
103
+ elif hasattr(root_type, 'member_types'):
104
+ for member_type in root_type.member_types:
105
+ yield from iter_builders(member_type, depth + 1)
106
+
107
+ if xsd_type.name in ATOMIC_BUILDERS:
108
+ return [ATOMIC_BUILDERS[xsd_type.name]]
109
+ elif xsd_type.is_simple() or (simple_type := xsd_type.simple_type) is None:
110
+ return [builder for builder in iter_builders(xsd_type.root_type, 1)]
111
+ elif simple_type.name in ATOMIC_BUILDERS:
112
+ return [ATOMIC_BUILDERS[simple_type.name]]
113
+ return [builder for builder in iter_builders(simple_type.root_type, 1)]
114
+
115
+
116
+ def get_atomic_sequence(xsd_type: Optional[XsdTypeProtocol],
117
+ text: object = None,
118
+ namespaces: AnyNsmapType = None) -> Iterator[dt.AtomicType]:
119
+ """Returns a decoder function for atomic values of an XSD type instance."""
120
+ def decode(value: str) -> dt.AtomicType:
121
+ if issubclass(cls, (dt.AbstractDateTime, dt.Duration)):
122
+ return cls.fromstring(value)
123
+ elif not issubclass(cls, dt.AbstractQName):
124
+ return cls(value)
125
+ else:
126
+ nonlocal namespaces
127
+ if namespaces is None:
128
+ namespaces = {'xs': XSD_NAMESPACE}
129
+ if ':' not in value:
130
+ return cls(namespaces.get(''), value)
131
+ else:
132
+ return cls(namespaces[value.split(':')[0]], value)
133
+
134
+ if xsd_type is None:
135
+ yield dt.UntypedAtomic(text if isinstance(text, str) else '')
136
+ return
137
+
138
+ for k, builder in enumerate(get_builders(xsd_type), start=1):
139
+ cls: Type[dt.AtomicType] = builder.cls
140
+
141
+ _text = text if isinstance(text, str) else builder.text
142
+ if len(builder) < k and not xsd_type.is_valid(text, namespaces=namespaces):
143
+ continue
144
+
145
+ try:
146
+ if xsd_type.is_list():
147
+ for item in _text.split():
148
+ yield decode(item)
149
+ else:
150
+ yield decode(_text)
151
+
152
+ except ValueError as err:
153
+ raise xpath_error('FORG0001', err, namespaces=namespaces)
154
+ except ArithmeticError as err:
155
+ if issubclass(cls, dt.AbstractDateTime):
156
+ raise xpath_error('FODT0001', err, namespaces=namespaces)
157
+ elif issubclass(cls, dt.Duration):
158
+ raise xpath_error('FODT0002', err, namespaces=namespaces)
159
+ else:
160
+ raise xpath_error('FOCA0002', err, namespaces=namespaces)
161
+ else:
162
+ return
163
+ else:
164
+ if hasattr(xsd_type, 'decode'):
165
+ yield xsd_type.decode(text if isinstance(text, str) else '')
166
+ else:
167
+ yield dt.UntypedAtomic(text if isinstance(text, str) else '')
168
+
169
+
170
+ __all__ = ['ATOMIC_BUILDERS', 'get_atomic_sequence']
@@ -106,7 +106,17 @@ def is_etree_element(obj: Any) -> bool:
106
106
 
107
107
 
108
108
  def is_lxml_etree_element(obj: Any) -> bool:
109
- return is_etree_element(obj) and hasattr(obj, 'getparent') and hasattr(obj, 'nsmap')
109
+ return is_etree_element(obj) and \
110
+ hasattr(obj, 'getparent') and \
111
+ hasattr(obj, 'nsmap') and \
112
+ obj.__class__.__module__ in ('lxml.etree', 'lxml.html')
113
+
114
+
115
+ def is_etree_element_instance(obj: Any) -> bool:
116
+ """Strictly checks that the objects is an ElementTree or lxml.etree Element."""
117
+ return isinstance(obj, ElementTree.Element) or \
118
+ isinstance(obj, PyElementTree.Element) or \
119
+ is_lxml_etree_element(obj)
110
120
 
111
121
 
112
122
  def is_etree_document(obj: Any) -> bool:
@@ -114,7 +124,17 @@ def is_etree_document(obj: Any) -> bool:
114
124
 
115
125
 
116
126
  def is_lxml_etree_document(obj: Any) -> bool:
117
- return is_etree_document(obj) and hasattr(obj, 'xpath') and hasattr(obj, 'xslt')
127
+ return is_etree_document(obj) and \
128
+ hasattr(obj, 'xpath') and \
129
+ hasattr(obj, 'xslt') and \
130
+ obj.__class__.__module__ in ('lxml.etree', 'lxml.html')
131
+
132
+
133
+ def is_etree_document_instance(obj: Any) -> bool:
134
+ """Strictly checks that the objects is an ElementTree or lxml.etree document."""
135
+ return isinstance(obj, ElementTree.ElementTree) or \
136
+ isinstance(obj, PyElementTree.ElementTree) or \
137
+ is_lxml_etree_document(obj)
118
138
 
119
139
 
120
140
  def etree_iter_strings(elem: Union[DocumentProtocol, ElementProtocol],
@@ -237,7 +257,7 @@ def etree_tostring(elem: ElementProtocol,
237
257
  return indent + line
238
258
 
239
259
  etree_module: Any
240
- if not is_etree_element(elem):
260
+ if not is_etree_element_instance(elem):
241
261
  raise TypeError(f"{elem!r} is not an Element")
242
262
  elif isinstance(elem, PyElementTree.Element):
243
263
  etree_module = PyElementTree
@@ -308,6 +328,6 @@ def etree_tostring(elem: ElementProtocol,
308
328
 
309
329
 
310
330
  __all__ = ['ElementTree', 'PyElementTree', 'SafeXMLParser', 'defuse_xml',
311
- 'is_etree_element', 'is_lxml_etree_element', 'is_etree_document',
312
- 'is_lxml_etree_document', 'etree_iter_strings', 'etree_deep_equal',
313
- 'etree_iter_paths', 'etree_tostring']
331
+ 'is_etree_element', 'is_lxml_etree_element', 'is_etree_element_instance',
332
+ 'is_etree_document', 'is_lxml_etree_document', 'is_etree_document_instance',
333
+ 'etree_iter_strings', 'etree_deep_equal', 'etree_iter_paths', 'etree_tostring']