elementpath 4.6.0__tar.gz → 4.7.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.6.0 → elementpath-4.7.0}/CHANGELOG.rst +6 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/PKG-INFO +1 -1
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/conf.py +2 -2
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/__init__.py +1 -1
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/etree.py +26 -6
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/tree_builders.py +58 -37
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath2/_xpath2_constructors.py +14 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath2/xpath2_parser.py +3 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath30/_xpath30_functions.py +5 -2
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath31/_xpath31_operators.py +2 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath_context.py +17 -22
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath_nodes.py +63 -56
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath_selectors.py +59 -29
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath.egg-info/PKG-INFO +1 -1
- {elementpath-4.6.0 → elementpath-4.7.0}/setup.py +1 -1
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_selectors.py +44 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath31.py +33 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tox.ini +3 -2
- {elementpath-4.6.0 → elementpath-4.7.0}/.coveragerc +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/LICENSE +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/MANIFEST.in +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/README.rst +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/Makefile +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/advanced.rst +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/index.rst +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/introduction.rst +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/make.bat +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/pratt_api.rst +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/requirements.txt +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/doc/xpath_api.rst +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/_typing.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/aliases.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/collations.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/compare.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/atomic_types.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/binary.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/datetime.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/numeric.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/proxies.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/qname.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/string.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/untyped.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/datatypes/uri.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/decoder.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/exceptions.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/helpers.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/namespaces.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/protocols.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/py.typed +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/regex/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/regex/character_classes.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/regex/codepoints.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/regex/patterns.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/regex/unicode_blocks.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/regex/unicode_categories.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/regex/unicode_subsets.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/schema_proxy.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/sequence_types.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/serialization.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/tdop.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/validators/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/validators/analyze-string.xsd +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/validators/schema-for-json.xsd +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath1/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath1/_xpath1_axes.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath1/_xpath1_functions.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath1/_xpath1_operators.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath1/xpath1_parser.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath2/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath2/_xpath2_functions.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath2/_xpath2_operators.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath3.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath30/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath30/_translation_maps.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath30/_xpath30_operators.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath30/xpath30_helpers.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath30/xpath30_parser.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath31/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath31/_xpath31_functions.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath31/xpath31_parser.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath/xpath_tokens.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath.egg-info/SOURCES.txt +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath.egg-info/dependency_links.txt +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath.egg-info/requires.txt +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/elementpath.egg-info/top_level.txt +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/mypy.ini +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/requirements-dev.txt +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/scripts/generate_codepoints.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/setup.cfg +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/__init__.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/execute_w3c_tests.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/memory_profiling.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/mypy_tests/advanced.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/mypy_tests/protocols.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/mypy_tests/selectors.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/analyze-string.xsd +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/external_entity.xml +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/sample.xml +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/schema-for-json.xsd +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/unparsed_entity.xml +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/unused_external_entity.xml +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/unused_unparsed_entity.xml +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/resources/with_entity.xml +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_collations.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_compare.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_datatypes.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_elementpath.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_etree.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_exceptions.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_helpers.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_namespaces.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_package.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_regex.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_schema_context.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_schema_proxy.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_sequence_types.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_serialization.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_tdop_parser.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_tree_builders.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_typing.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_validators.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath1_parser.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath2_constructors.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath2_functions.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath2_parser.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath30.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath_context.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath_nodes.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/test_xpath_tokens.py +0 -0
- {elementpath-4.6.0 → elementpath-4.7.0}/tests/xpath_test_class.py +0 -0
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
CHANGELOG
|
|
3
3
|
*********
|
|
4
4
|
|
|
5
|
+
`v4.7.0`_ (2024-12-20)
|
|
6
|
+
======================
|
|
7
|
+
* Fix *fragment* argument usage (issue #81)
|
|
8
|
+
* Fix constructors nud() to skip argument check with XP31+ arrow operator (issue #83)
|
|
9
|
+
|
|
5
10
|
`v4.6.0`_ (2024-10-27)
|
|
6
11
|
======================
|
|
7
12
|
* Fix XsdAttributeGroupProtocol
|
|
@@ -478,3 +483,4 @@ CHANGELOG
|
|
|
478
483
|
.. _v4.4.0: https://github.com/sissaschool/elementpath/compare/v4.3.0...v4.4.0
|
|
479
484
|
.. _v4.5.0: https://github.com/sissaschool/elementpath/compare/v4.4.0...v4.5.0
|
|
480
485
|
.. _v4.6.0: https://github.com/sissaschool/elementpath/compare/v4.5.0...v4.6.0
|
|
486
|
+
.. _v4.7.0: https://github.com/sissaschool/elementpath/compare/v4.6.0...v4.7.0
|
|
@@ -29,9 +29,9 @@ copyright = '2018-2024, SISSA (International School for Advanced Studies)'
|
|
|
29
29
|
author = 'Davide Brunato'
|
|
30
30
|
|
|
31
31
|
# The short X.Y version
|
|
32
|
-
version = '4.
|
|
32
|
+
version = '4.7'
|
|
33
33
|
# The full version, including alpha/beta/rc tags
|
|
34
|
-
release = '4.
|
|
34
|
+
release = '4.7.0'
|
|
35
35
|
|
|
36
36
|
# -- General configuration ---------------------------------------------------
|
|
37
37
|
|
|
@@ -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
|
|
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
|
|
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
|
|
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', '
|
|
312
|
-
'
|
|
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']
|
|
@@ -14,7 +14,7 @@ from elementpath.aliases import NamespacesType
|
|
|
14
14
|
from elementpath.exceptions import ElementPathTypeError
|
|
15
15
|
from elementpath.protocols import ElementProtocol, LxmlElementProtocol, \
|
|
16
16
|
DocumentProtocol, LxmlDocumentProtocol, XsdElementProtocol
|
|
17
|
-
from elementpath.etree import is_etree_document, is_etree_element
|
|
17
|
+
from elementpath.etree import is_etree_document, is_etree_element, is_etree_element_instance
|
|
18
18
|
from elementpath.xpath_nodes import SchemaElemType, ChildNodeType, \
|
|
19
19
|
ElementMapType, TextNode, CommentNode, ProcessingInstructionNode, \
|
|
20
20
|
ElementNode, SchemaElementNode, DocumentNode
|
|
@@ -36,7 +36,7 @@ def is_schema(obj: Any) -> bool:
|
|
|
36
36
|
def get_node_tree(root: RootArgType,
|
|
37
37
|
namespaces: Optional[NamespacesType] = None,
|
|
38
38
|
uri: Optional[str] = None,
|
|
39
|
-
fragment: Optional[bool] =
|
|
39
|
+
fragment: Optional[bool] = None) -> Union[DocumentNode, ElementNode]:
|
|
40
40
|
"""
|
|
41
41
|
Returns a tree of XPath nodes that wrap the provided root tree.
|
|
42
42
|
|
|
@@ -44,64 +44,69 @@ def get_node_tree(root: RootArgType,
|
|
|
44
44
|
:param namespaces: an optional mapping from prefixes to namespace URIs, \
|
|
45
45
|
Ignored if root is a lxml etree or a schema structure.
|
|
46
46
|
:param uri: an optional URI associated with the root element or the document.
|
|
47
|
-
:param fragment: if `True`
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
kind is preserved.
|
|
47
|
+
:param fragment: if `True` is provided the root is considered a fragment. In this \
|
|
48
|
+
case if `root` is an ElementTree instance skips it and use the root Element. If \
|
|
49
|
+
`False` is provided creates a dummy document when the root is an Element instance. \
|
|
50
|
+
For default the root node kind is preserved.
|
|
52
51
|
"""
|
|
53
52
|
if isinstance(root, (DocumentNode, ElementNode)):
|
|
54
53
|
if uri is not None and root.uri is None:
|
|
55
54
|
root.uri = uri
|
|
56
55
|
|
|
57
|
-
if fragment
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
root_node.uri
|
|
61
|
-
|
|
56
|
+
if fragment:
|
|
57
|
+
if isinstance(root, DocumentNode):
|
|
58
|
+
root_node = root.getroot()
|
|
59
|
+
if root_node.uri is None:
|
|
60
|
+
root_node.uri = root.uri
|
|
61
|
+
return root_node
|
|
62
|
+
elif fragment is False and \
|
|
63
|
+
isinstance(root, ElementNode) and \
|
|
64
|
+
is_etree_element_instance(root.elem):
|
|
65
|
+
return root.get_document_node(replace=False)
|
|
62
66
|
|
|
63
67
|
return root
|
|
64
68
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
_root = cast(DocumentProtocol, root).getroot() if fragment else root
|
|
68
|
-
elif is_etree_element(root) and not callable(cast(ElementProtocol, root).tag):
|
|
69
|
-
_root = root
|
|
70
|
-
else:
|
|
69
|
+
if not is_etree_document(root) and \
|
|
70
|
+
(not is_etree_element(root) or callable(cast(ElementProtocol, root).tag)):
|
|
71
71
|
msg = "invalid root {!r}, an Element or an ElementTree or a schema node required"
|
|
72
72
|
raise ElementPathTypeError(msg.format(root))
|
|
73
|
-
|
|
74
|
-
if hasattr(_root, 'xpath'):
|
|
73
|
+
elif hasattr(root, 'xpath'):
|
|
75
74
|
# a lxml element tree data
|
|
76
75
|
return build_lxml_node_tree(
|
|
77
|
-
cast(LxmlRootType,
|
|
76
|
+
cast(LxmlRootType, root), uri, fragment
|
|
78
77
|
)
|
|
79
|
-
elif hasattr(
|
|
78
|
+
elif hasattr(root, 'xsd_version') and hasattr(root, 'maps'):
|
|
80
79
|
# a schema or a schema node
|
|
81
80
|
return build_schema_node_tree(
|
|
82
81
|
cast(SchemaElemType, root), uri
|
|
83
82
|
)
|
|
84
83
|
else:
|
|
85
84
|
return build_node_tree(
|
|
86
|
-
cast(ElementTreeRootType, root), namespaces, uri
|
|
85
|
+
cast(ElementTreeRootType, root), namespaces, uri, fragment
|
|
87
86
|
)
|
|
88
87
|
|
|
89
88
|
|
|
90
89
|
def build_node_tree(root: ElementTreeRootType,
|
|
91
90
|
namespaces: Optional[NamespacesType] = None,
|
|
92
|
-
uri: Optional[str] = None
|
|
91
|
+
uri: Optional[str] = None,
|
|
92
|
+
fragment: Optional[bool] = None) -> Union[DocumentNode, ElementNode]:
|
|
93
93
|
"""
|
|
94
94
|
Returns a tree of XPath nodes that wrap the provided root tree.
|
|
95
95
|
|
|
96
96
|
:param root: an Element or an ElementTree.
|
|
97
97
|
:param namespaces: an optional mapping from prefixes to namespace URIs.
|
|
98
98
|
:param uri: an optional URI associated with the document or the root element.
|
|
99
|
+
:param fragment: if `True` is provided the root is considered a fragment. In this \
|
|
100
|
+
case if `root` is an ElementTree instance skips it and use the root Element. If \
|
|
101
|
+
`False` is provided creates a dummy document when the root is an Element instance. \
|
|
102
|
+
For default the root node kind is preserved.
|
|
99
103
|
"""
|
|
100
104
|
elem: ElementProtocol
|
|
101
105
|
parent: Any
|
|
102
106
|
elements: Any
|
|
103
107
|
child: ChildNodeType
|
|
104
108
|
children: Iterator[Any]
|
|
109
|
+
document: Optional[DocumentProtocol]
|
|
105
110
|
|
|
106
111
|
position = 1
|
|
107
112
|
if namespaces:
|
|
@@ -111,10 +116,18 @@ def build_node_tree(root: ElementTreeRootType,
|
|
|
111
116
|
|
|
112
117
|
if hasattr(root, 'parse'):
|
|
113
118
|
document = cast(DocumentProtocol, root)
|
|
119
|
+
root_elem = document.getroot()
|
|
120
|
+
else:
|
|
121
|
+
document = None
|
|
122
|
+
root_elem = root
|
|
123
|
+
|
|
124
|
+
if fragment and root_elem is not None:
|
|
125
|
+
document = None # Explicitly requested a fragment: don't create a document node
|
|
126
|
+
|
|
127
|
+
if document is not None:
|
|
114
128
|
document_node = DocumentNode(document, uri, position)
|
|
115
129
|
position += 1
|
|
116
130
|
|
|
117
|
-
root_elem = document.getroot()
|
|
118
131
|
if root_elem is None:
|
|
119
132
|
return document_node
|
|
120
133
|
|
|
@@ -123,8 +136,9 @@ def build_node_tree(root: ElementTreeRootType,
|
|
|
123
136
|
elements = document_node.elements
|
|
124
137
|
document_node.children.append(root_node)
|
|
125
138
|
else:
|
|
139
|
+
assert root_elem is not None
|
|
126
140
|
document_node = None
|
|
127
|
-
elem =
|
|
141
|
+
elem = root_elem
|
|
128
142
|
root_node = ElementNode(elem, None, position, namespaces)
|
|
129
143
|
root_node.elements = elements = {}
|
|
130
144
|
if uri is not None:
|
|
@@ -176,7 +190,14 @@ def build_node_tree(root: ElementTreeRootType,
|
|
|
176
190
|
try:
|
|
177
191
|
children, parent = iterators.pop(), ancestors.pop()
|
|
178
192
|
except IndexError:
|
|
179
|
-
|
|
193
|
+
if document_node is not None:
|
|
194
|
+
return document_node
|
|
195
|
+
elif fragment is False and \
|
|
196
|
+
isinstance(root_node, ElementNode) and \
|
|
197
|
+
is_etree_element_instance(root_node.elem):
|
|
198
|
+
return root_node.get_document_node(replace=False)
|
|
199
|
+
else:
|
|
200
|
+
return root_node
|
|
180
201
|
else:
|
|
181
202
|
if (tail := parent.children[-1].elem.tail) is not None:
|
|
182
203
|
parent.children.append(TextNode(tail, parent, position))
|
|
@@ -185,17 +206,19 @@ def build_node_tree(root: ElementTreeRootType,
|
|
|
185
206
|
|
|
186
207
|
def build_lxml_node_tree(root: LxmlRootType,
|
|
187
208
|
uri: Optional[str] = None,
|
|
188
|
-
fragment: Optional[bool] =
|
|
209
|
+
fragment: Optional[bool] = None) -> Union[DocumentNode, ElementNode]:
|
|
189
210
|
"""
|
|
190
211
|
Returns a tree of XPath nodes that wrap the provided lxml root tree.
|
|
191
212
|
|
|
192
213
|
:param root: a lxml Element or a lxml ElementTree.
|
|
193
214
|
:param uri: an optional URI associated with the document or the root element.
|
|
194
|
-
:param fragment: if `True`
|
|
195
|
-
|
|
196
|
-
the root
|
|
215
|
+
:param fragment: if `True` is provided the root is considered a fragment. In this \
|
|
216
|
+
case if `root` is an ElementTree instance skips it and use the root Element. If \
|
|
217
|
+
`False` is provided creates a dummy document when the root is an Element instance. \
|
|
218
|
+
For default the root node kind is preserved.
|
|
197
219
|
"""
|
|
198
220
|
root_node: Union[DocumentNode, ElementNode]
|
|
221
|
+
document: Optional[LxmlDocumentProtocol]
|
|
199
222
|
parent: Any
|
|
200
223
|
elements: Any
|
|
201
224
|
child: ChildNodeType
|
|
@@ -204,18 +227,16 @@ def build_lxml_node_tree(root: LxmlRootType,
|
|
|
204
227
|
position = 1
|
|
205
228
|
|
|
206
229
|
if fragment:
|
|
207
|
-
# Explicitly requested a fragment: don't create a document node
|
|
208
|
-
document = None
|
|
230
|
+
document = None # Explicitly requested a fragment: don't create a document node
|
|
209
231
|
elif hasattr(root, 'parse'):
|
|
210
|
-
# A document (ElementTree instance)
|
|
211
232
|
document = cast(LxmlDocumentProtocol, root)
|
|
212
|
-
elif root.getparent() is None and (
|
|
233
|
+
elif fragment is False or root.getparent() is None and (
|
|
213
234
|
any(True for _sibling in root.itersiblings(preceding=True)) or
|
|
214
235
|
any(True for _sibling in root.itersiblings())):
|
|
215
|
-
#
|
|
236
|
+
# Despite a document is not explicitly requested create a dummy document
|
|
237
|
+
# because the root element has siblings
|
|
216
238
|
document = root.getroottree()
|
|
217
239
|
else:
|
|
218
|
-
# Explicitly provided a non-root element: do not parse root's siblings.
|
|
219
240
|
document = None
|
|
220
241
|
|
|
221
242
|
if document is not None:
|
|
@@ -363,6 +363,8 @@ def evaluate_datetime_stamp_type(self: XPathConstructor, context: ContextType =
|
|
|
363
363
|
def nud_datetime_stamp_type(self: XPathConstructor) -> XPathConstructor:
|
|
364
364
|
if self.parser.xsd_version == '1.0':
|
|
365
365
|
raise self.wrong_syntax("xs:dateTimeStamp is not recognized unless XSD 1.1 is enabled")
|
|
366
|
+
if not self.parser.parse_arguments:
|
|
367
|
+
return self
|
|
366
368
|
|
|
367
369
|
try:
|
|
368
370
|
self.parser.advance('(')
|
|
@@ -422,6 +424,9 @@ def cast_notation_type(self: XPathConstructor, value: AtomicType) -> Notation:
|
|
|
422
424
|
|
|
423
425
|
@method('NOTATION')
|
|
424
426
|
def nud_notation_type(self: XPathConstructor) -> None:
|
|
427
|
+
if not self.parser.parse_arguments:
|
|
428
|
+
return
|
|
429
|
+
|
|
425
430
|
self.parser.advance('(')
|
|
426
431
|
if self.parser.next_token.symbol == ')':
|
|
427
432
|
raise self.error('XPST0017', 'expected exactly one argument')
|
|
@@ -454,6 +459,9 @@ def cast_boolean_type(self: XPathConstructor, value: AtomicType) -> bool:
|
|
|
454
459
|
|
|
455
460
|
@method('boolean')
|
|
456
461
|
def nud_boolean_type_and_function(self: XPathConstructor) -> XPathConstructor:
|
|
462
|
+
if not self.parser.parse_arguments:
|
|
463
|
+
return self
|
|
464
|
+
|
|
457
465
|
self.parser.advance('(')
|
|
458
466
|
if self.parser.next_token.symbol == ')':
|
|
459
467
|
msg = 'Too few arguments: expected at least 1 argument'
|
|
@@ -502,6 +510,9 @@ def cast_string_type(self: XPathConstructor, value: AtomicType) -> str:
|
|
|
502
510
|
|
|
503
511
|
@method('string')
|
|
504
512
|
def nud_string_type_and_function(self: XPathConstructor) -> XPathConstructor:
|
|
513
|
+
if not self.parser.parse_arguments:
|
|
514
|
+
return self
|
|
515
|
+
|
|
505
516
|
try:
|
|
506
517
|
self.parser.advance('(')
|
|
507
518
|
if self.label != 'function' or self.parser.next_token.symbol != ')':
|
|
@@ -576,6 +587,9 @@ def cast_datetime_type(self: XPathConstructor, value: AtomicType) \
|
|
|
576
587
|
@method('QName')
|
|
577
588
|
@method('dateTime')
|
|
578
589
|
def nud_qname_and_datetime(self: XPathConstructor) -> XPathConstructor:
|
|
590
|
+
if not self.parser.parse_arguments:
|
|
591
|
+
return self
|
|
592
|
+
|
|
579
593
|
try:
|
|
580
594
|
self.parser.advance('(')
|
|
581
595
|
self[0:] = self.parser.expression(5),
|
|
@@ -249,6 +249,9 @@ class XPath2Parser(XPath1Parser):
|
|
|
249
249
|
of the module where the method is called.
|
|
250
250
|
"""
|
|
251
251
|
def nud_(self: XPathConstructor) -> XPathConstructor:
|
|
252
|
+
if not self.parser.parse_arguments:
|
|
253
|
+
return self
|
|
254
|
+
|
|
252
255
|
try:
|
|
253
256
|
self.parser.advance('(')
|
|
254
257
|
self[0:] = self.parser.expression(5),
|
|
@@ -1492,7 +1492,7 @@ def evaluate_parse_xml_fragment_function(self: XPathFunction, context: ContextTy
|
|
|
1492
1492
|
else:
|
|
1493
1493
|
root = etree.XML(arg)
|
|
1494
1494
|
except etree.ParseError as err:
|
|
1495
|
-
# A not parsable fragment try to parse including XML data in a dummy element.
|
|
1495
|
+
# A not parsable fragment: try to parse including XML data in a dummy element.
|
|
1496
1496
|
try:
|
|
1497
1497
|
dummy_element_node = get_node_tree(
|
|
1498
1498
|
root=etree.XML(f'<document>{arg}</document>'),
|
|
@@ -1502,7 +1502,7 @@ def evaluate_parse_xml_fragment_function(self: XPathFunction, context: ContextTy
|
|
|
1502
1502
|
raise self.error('FODC0006', str(err)) from None
|
|
1503
1503
|
else:
|
|
1504
1504
|
assert isinstance(dummy_element_node, ElementNode)
|
|
1505
|
-
return
|
|
1505
|
+
return dummy_element_node.get_document_node(replace=True)
|
|
1506
1506
|
else:
|
|
1507
1507
|
return cast(DocumentNode, get_node_tree(
|
|
1508
1508
|
root=etree.ElementTree(root),
|
|
@@ -1955,6 +1955,9 @@ def cast_error_type(self: XPathConstructor, value: AtomicType) -> Emptiable[None
|
|
|
1955
1955
|
@method('error')
|
|
1956
1956
|
def nud_error_type_and_function(self: XPathConstructor) -> XPathConstructor:
|
|
1957
1957
|
self.clear()
|
|
1958
|
+
if not self.parser.parse_arguments:
|
|
1959
|
+
return self
|
|
1960
|
+
|
|
1958
1961
|
try:
|
|
1959
1962
|
self.parser.advance('(')
|
|
1960
1963
|
if self.namespace == XSD_NAMESPACE:
|
|
@@ -235,6 +235,8 @@ def led_arrow_operator(self: XPathToken, left: XPathToken) -> XPathToken:
|
|
|
235
235
|
self.parser.advance()
|
|
236
236
|
elif isinstance(next_token, XPathFunction):
|
|
237
237
|
self[:] = left, next_token
|
|
238
|
+
if next_token.label == 'kind test':
|
|
239
|
+
raise next_token.wrong_syntax()
|
|
238
240
|
self.parser.advance() # Skip static evaluation of function arguments
|
|
239
241
|
else:
|
|
240
242
|
next_token.expected('(name)', ':', 'Q{', '(')
|
|
@@ -19,9 +19,9 @@ from elementpath.protocols import ElementProtocol, DocumentProtocol
|
|
|
19
19
|
from elementpath.exceptions import ElementPathTypeError
|
|
20
20
|
from elementpath.tdop import Token
|
|
21
21
|
from elementpath.datatypes import AnyAtomicType, AtomicType, Timezone, Language
|
|
22
|
-
from elementpath.etree import is_etree_element, is_etree_document
|
|
22
|
+
from elementpath.etree import is_etree_element, is_etree_element_instance, is_etree_document
|
|
23
23
|
from elementpath.xpath_nodes import ChildNodeType, XPathNode, AttributeNode, NamespaceNode, \
|
|
24
|
-
CommentNode, ProcessingInstructionNode, ElementNode, DocumentNode
|
|
24
|
+
CommentNode, ProcessingInstructionNode, ElementNode, DocumentNode
|
|
25
25
|
from elementpath.tree_builders import RootArgType, get_node_tree
|
|
26
26
|
|
|
27
27
|
if TYPE_CHECKING:
|
|
@@ -60,10 +60,11 @@ class XPathContext:
|
|
|
60
60
|
This can be useful when the dynamic context has additional namespaces and root \
|
|
61
61
|
is an Element or an ElementTree instance of the standard library.
|
|
62
62
|
:param uri: an optional URI associated with the root element or the document.
|
|
63
|
-
:param fragment: if `True`
|
|
64
|
-
|
|
65
|
-
is
|
|
66
|
-
|
|
63
|
+
:param fragment: if `True` is provided the root is considered a fragment. In this \
|
|
64
|
+
case if `root` is an ElementTree instance skips it and use the root Element. If \
|
|
65
|
+
`False` is provided creates a dummy document when the root is an Element instance. \
|
|
66
|
+
In this case the dummy document value is not included in results. For default the \
|
|
67
|
+
root node kind is preserved.
|
|
67
68
|
:param item: the context item. A `None` value means that the context is positioned on \
|
|
68
69
|
the document node.
|
|
69
70
|
:param position: the current position of the node within the input sequence.
|
|
@@ -104,7 +105,7 @@ class XPathContext:
|
|
|
104
105
|
root: Optional[RootArgType] = None,
|
|
105
106
|
namespaces: Optional[NamespacesType] = None,
|
|
106
107
|
uri: Optional[str] = None,
|
|
107
|
-
fragment: Optional[bool] =
|
|
108
|
+
fragment: Optional[bool] = None,
|
|
108
109
|
item: Optional[ItemArgType] = None,
|
|
109
110
|
position: int = 1,
|
|
110
111
|
size: int = 1,
|
|
@@ -142,15 +143,11 @@ class XPathContext:
|
|
|
142
143
|
|
|
143
144
|
if isinstance(self.root, DocumentNode):
|
|
144
145
|
self.document = self.root
|
|
145
|
-
elif fragment
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
# Creates a dummy document
|
|
149
|
-
document =
|
|
150
|
-
self.document = DocumentNode(document, self.root.uri, position=0)
|
|
151
|
-
self.document.children.append(self.root)
|
|
152
|
-
self.document.elements = cast(Dict[ElementProtocol, ElementNode], self.root.elements)
|
|
153
|
-
# self.root.parent = self.document TODO for v5? It's necessary?
|
|
146
|
+
elif fragment is None and \
|
|
147
|
+
isinstance(self.root, ElementNode) and \
|
|
148
|
+
is_etree_element_instance(self.root.elem):
|
|
149
|
+
# Creates a dummy document that will be not included in results
|
|
150
|
+
self.document = self.root.get_document_node(replace=False, as_parent=False)
|
|
154
151
|
|
|
155
152
|
self.position = position
|
|
156
153
|
self.size = size
|
|
@@ -214,12 +211,10 @@ class XPathContext:
|
|
|
214
211
|
else:
|
|
215
212
|
module_name = 'xml.etree.ElementTree'
|
|
216
213
|
|
|
217
|
-
if
|
|
218
|
-
|
|
214
|
+
if module_name in ('lxml.etree', 'lxml.html'):
|
|
215
|
+
self._etree: ModuleType = importlib.import_module('lxml.etree')
|
|
219
216
|
else:
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
self._etree: ModuleType = importlib.import_module(etree_module_name)
|
|
217
|
+
self._etree = importlib.import_module('xml.etree.ElementTree')
|
|
223
218
|
|
|
224
219
|
return self._etree
|
|
225
220
|
|
|
@@ -255,7 +250,7 @@ class XPathContext:
|
|
|
255
250
|
def get_context_item(self, item: ItemArgType,
|
|
256
251
|
namespaces: Optional[NamespacesType] = None,
|
|
257
252
|
uri: Optional[str] = None,
|
|
258
|
-
fragment: Optional[bool] =
|
|
253
|
+
fragment: Optional[bool] = None) -> ItemType:
|
|
259
254
|
"""
|
|
260
255
|
Checks the item and returns an item suitable for XPath processing.
|
|
261
256
|
For XML trees and elements try a match with an existing node in the
|