elementpath 5.0.0__tar.gz → 5.0.2__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-5.0.0 → elementpath-5.0.2}/CHANGELOG.rst +11 -0
- {elementpath-5.0.0/elementpath.egg-info → elementpath-5.0.2}/PKG-INFO +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/conf.py +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/__init__.py +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/compare.py +15 -6
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/datetime.py +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/etree.py +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/helpers.py +11 -10
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/protocols.py +38 -43
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/categories_fallback.py +0 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/schema_proxy.py +84 -47
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/serialization.py +11 -6
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/tree_builders.py +18 -19
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath1/_xpath1_operators.py +6 -4
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath1/xpath1_parser.py +4 -2
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath2/_xpath2_functions.py +5 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath2/_xpath2_operators.py +1 -2
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath2/xpath2_parser.py +10 -3
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath30/_xpath30_functions.py +10 -6
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath31/_xpath31_functions.py +6 -4
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath_context.py +17 -17
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath_nodes.py +48 -51
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath_selectors.py +22 -5
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath_tokens.py +2 -2
- {elementpath-5.0.0 → elementpath-5.0.2/elementpath.egg-info}/PKG-INFO +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/pyproject.toml +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/mypy_tests/protocols.py +1 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_helpers.py +12 -7
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_schema_proxy.py +241 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_selectors.py +11 -1
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath1_parser.py +37 -13
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath2_functions.py +6 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath2_parser.py +25 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath30.py +31 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath_nodes.py +3 -3
- {elementpath-5.0.0 → elementpath-5.0.2}/tox.ini +26 -13
- {elementpath-5.0.0 → elementpath-5.0.2}/LICENSE +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/MANIFEST.in +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/README.rst +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/Makefile +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/advanced.rst +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/index.rst +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/introduction.rst +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/make.bat +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/pratt_api.rst +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/requirements.txt +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/doc/xpath_api.rst +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/aliases.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/collations.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/atomic_types.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/binary.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/numeric.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/proxies.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/qname.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/string.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/untyped.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/datatypes/uri.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/decoder.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/exceptions.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/extras/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/extras/pathnodes.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/namespaces.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/py.typed +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/character_classes.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/codepoints.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/patterns.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/unicode_blocks.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/unicode_categories.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/regex/unicode_subsets.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/sequence_types.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/tdop.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/validators/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/validators/analyze-string.xsd +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/validators/schema-for-json.xsd +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath1/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath1/_xpath1_axes.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath1/_xpath1_functions.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath2/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath2/_xpath2_constructors.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath3.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath30/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath30/_translation_maps.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath30/_xpath30_operators.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath30/xpath30_helpers.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath30/xpath30_parser.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath31/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath31/_xpath31_operators.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath/xpath31/xpath31_parser.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath.egg-info/SOURCES.txt +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath.egg-info/dependency_links.txt +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath.egg-info/requires.txt +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/elementpath.egg-info/top_level.txt +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/requirements-dev.txt +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/scripts/generate_codepoints.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/setup.cfg +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/__init__.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/memory_profiling.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/mypy_tests/advanced.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/mypy_tests/selectors.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/analyze-string.xsd +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/external_entity.xml +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/sample.xml +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/schema-for-json.xsd +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/unparsed_entity.xml +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/unused_external_entity.xml +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/unused_unparsed_entity.xml +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/resources/with_entity.xml +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/run_all_tests.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/run_typing_tests.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/run_w3c_tests.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_collations.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_compare.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_datatypes.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_decoder.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_etree.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_exceptions.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_namespaces.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_package.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_pathnodes.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_regex.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_schema_context.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_sequence_types.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_serialization.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_tdop_parser.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_tree_builders.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_validators.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath2_constructors.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath31.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath_context.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/test_xpath_tokens.py +0 -0
- {elementpath-5.0.0 → elementpath-5.0.2}/tests/xpath_test_class.py +0 -0
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
CHANGELOG
|
|
3
3
|
*********
|
|
4
4
|
|
|
5
|
+
`v5.0.2`_ (2025-06-18)
|
|
6
|
+
======================
|
|
7
|
+
* Fix for XPath 2.0 *fn:node-name* (issue #91)
|
|
8
|
+
* Workaround for processing arguments with multiple occurrences for external functions (issue #92)
|
|
9
|
+
|
|
10
|
+
`v5.0.1`_ (2025-05-11)
|
|
11
|
+
======================
|
|
12
|
+
* Fix XDM type labeling with element and xsi:type substitutions (issue #89)
|
|
13
|
+
|
|
5
14
|
`v5.0.0`_ (2025-04-27)
|
|
6
15
|
======================
|
|
7
16
|
* Replace SafeXMLParser with SafeExpatParser
|
|
@@ -502,3 +511,5 @@ CHANGELOG
|
|
|
502
511
|
.. _v4.7.0: https://github.com/sissaschool/elementpath/compare/v4.6.0...v4.7.0
|
|
503
512
|
.. _v4.8.0: https://github.com/sissaschool/elementpath/compare/v4.7.0...v4.8.0
|
|
504
513
|
.. _v5.0.0: https://github.com/sissaschool/elementpath/compare/v4.8.0...v5.0.0
|
|
514
|
+
.. _v5.0.1: https://github.com/sissaschool/elementpath/compare/v5.0.0...v5.0.1
|
|
515
|
+
.. _v5.0.2: https://github.com/sissaschool/elementpath/compare/v5.0.1...v5.0.2
|
|
@@ -66,10 +66,17 @@ def deep_equal(seq1: Iterable[Any],
|
|
|
66
66
|
|
|
67
67
|
if (value1 is None) ^ (value2 is None):
|
|
68
68
|
return False
|
|
69
|
+
elif isinstance(value1, (XPathArray, XPathMap, XPathNode)) ^ \
|
|
70
|
+
isinstance(value2, (XPathArray, XPathMap, XPathNode)):
|
|
71
|
+
return False
|
|
69
72
|
elif value1 is None:
|
|
70
73
|
return True
|
|
71
|
-
elif isinstance(value1,
|
|
72
|
-
|
|
74
|
+
elif isinstance(value1, XPathMap):
|
|
75
|
+
assert isinstance(value2, XPathMap)
|
|
76
|
+
return value1 == value2
|
|
77
|
+
elif isinstance(value1, XPathArray):
|
|
78
|
+
assert isinstance(value2, XPathArray)
|
|
79
|
+
return value1 == value2
|
|
73
80
|
elif isinstance(value1, XPathNode):
|
|
74
81
|
assert isinstance(value2, XPathNode)
|
|
75
82
|
if value1.__class__ != value2.__class__:
|
|
@@ -187,9 +194,10 @@ def deep_compare(obj1: Any,
|
|
|
187
194
|
|
|
188
195
|
def etree_deep_compare(e1: ElementProtocol, e2: ElementProtocol) -> int:
|
|
189
196
|
nonlocal result
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
if not callable(e1.tag) and not callable(e2.tag):
|
|
198
|
+
result = cm.strcoll(e1.tag, e2.tag)
|
|
199
|
+
if result:
|
|
200
|
+
return result
|
|
193
201
|
|
|
194
202
|
result = cm.strcoll((e1.text or '').strip(), (e2.text or '').strip())
|
|
195
203
|
if result:
|
|
@@ -315,7 +323,8 @@ def deep_compare(obj1: Any,
|
|
|
315
323
|
|
|
316
324
|
elif isinstance(value1, float):
|
|
317
325
|
if math.isnan(value1):
|
|
318
|
-
if not
|
|
326
|
+
if not isinstance(value2, (float, Decimal)) \
|
|
327
|
+
or not math.isnan(value2):
|
|
319
328
|
return -1
|
|
320
329
|
elif math.isinf(value1):
|
|
321
330
|
if value1 != value2:
|
|
@@ -258,7 +258,7 @@ class AbstractDateTime(AnyAtomicType):
|
|
|
258
258
|
return cast(Timezone, self._dt.tzinfo)
|
|
259
259
|
|
|
260
260
|
@tzinfo.setter
|
|
261
|
-
def tzinfo(self, tz: Timezone) -> None:
|
|
261
|
+
def tzinfo(self, tz: Optional[Timezone]) -> None:
|
|
262
262
|
self._dt = self._dt.replace(tzinfo=tz)
|
|
263
263
|
|
|
264
264
|
def tzname(self) -> Optional[str]:
|
|
@@ -156,7 +156,7 @@ def etree_iter_paths(elem: ElementProtocol, path: str = '.') \
|
|
|
156
156
|
|
|
157
157
|
for child in elem:
|
|
158
158
|
if callable(child.tag):
|
|
159
|
-
if child.tag.__name__ == 'Comment':
|
|
159
|
+
if child.tag.__name__ == 'Comment':
|
|
160
160
|
comment_nodes += 1
|
|
161
161
|
yield child, f'{path}/comment()[{comment_nodes}]'
|
|
162
162
|
continue
|
|
@@ -84,7 +84,7 @@ class Patterns:
|
|
|
84
84
|
# Regex patterns related to names and namespaces
|
|
85
85
|
namespace_uri = LazyPattern(r'{([^}]+)}')
|
|
86
86
|
expanded_name = LazyPattern(
|
|
87
|
-
r'^(?:{(?P<namespace>[^}]
|
|
87
|
+
r'^(?:(?:Q{|{)(?P<namespace>[^}]*)})?'
|
|
88
88
|
r'(?P<local>[^\d\W][\w\-.\u00B7\u0300-\u036F\u0387\u06DD\u06DE\u203F\u2040]*)$',
|
|
89
89
|
)
|
|
90
90
|
unbound_expanded_name = LazyPattern(
|
|
@@ -259,19 +259,20 @@ def not_equal(op1: Any, op2: Any) -> bool:
|
|
|
259
259
|
|
|
260
260
|
|
|
261
261
|
def match_wildcard(name: Optional[str], wildcard: str) -> bool:
|
|
262
|
-
if name
|
|
262
|
+
if not name:
|
|
263
263
|
return False
|
|
264
|
-
elif wildcard
|
|
264
|
+
elif wildcard in ('*', '{*}*'):
|
|
265
265
|
return True
|
|
266
|
-
elif wildcard
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
else:
|
|
270
|
-
return name == wildcard[2:]
|
|
271
|
-
elif wildcard.startswith('{') and wildcard.endswith('}*') or wildcard.endswith(':*'):
|
|
266
|
+
elif wildcard == '{}*':
|
|
267
|
+
return name[0] != '{' or name[:2] == '{}'
|
|
268
|
+
elif wildcard[-1] == '*':
|
|
272
269
|
return name.startswith(wildcard[:-1])
|
|
273
|
-
|
|
270
|
+
elif not wildcard.startswith('{*}'):
|
|
274
271
|
return False
|
|
272
|
+
elif name[0] == '{':
|
|
273
|
+
return name.endswith(wildcard[2:])
|
|
274
|
+
else:
|
|
275
|
+
return name == wildcard[3:]
|
|
275
276
|
|
|
276
277
|
|
|
277
278
|
def escape_json_string(s: str, escaped: bool = False) -> str:
|
|
@@ -10,16 +10,14 @@
|
|
|
10
10
|
"""
|
|
11
11
|
Define type hints protocols for XPath related objects.
|
|
12
12
|
"""
|
|
13
|
-
from collections.abc import Hashable, Iterator, Iterable,
|
|
14
|
-
|
|
13
|
+
from collections.abc import Callable, Hashable, Iterator, Iterable, \
|
|
14
|
+
ItemsView, Mapping, Sequence, Sized
|
|
15
|
+
from typing import overload, Any, Optional, Protocol, Union, TypeVar
|
|
15
16
|
from xml.etree.ElementTree import Element, ElementTree
|
|
16
17
|
|
|
17
18
|
from collections.abc import MutableMapping
|
|
18
19
|
from elementpath.aliases import NamespacesType, NsmapType
|
|
19
20
|
|
|
20
|
-
if TYPE_CHECKING:
|
|
21
|
-
from elementpath.schema_proxy import AbstractSchemaProxy
|
|
22
|
-
|
|
23
21
|
_T = TypeVar("_T")
|
|
24
22
|
_AnyStr = Union[str, bytes]
|
|
25
23
|
|
|
@@ -67,7 +65,7 @@ class ElementProtocol(Sized, Hashable, Protocol):
|
|
|
67
65
|
def get(self, key: str, default: Optional[_T] = None) -> Union[str, _T, None]: ...
|
|
68
66
|
|
|
69
67
|
@property
|
|
70
|
-
def tag(self) -> str: ...
|
|
68
|
+
def tag(self) -> Union[str, Callable[[], 'ElementProtocol']]: ...
|
|
71
69
|
|
|
72
70
|
@property
|
|
73
71
|
def text(self) -> Optional[str]: ...
|
|
@@ -146,6 +144,9 @@ class XsdComponentProtocol(XsdValidatorProtocol, Protocol):
|
|
|
146
144
|
@property
|
|
147
145
|
def parent(self) -> Optional['XsdComponentProtocol']: ...
|
|
148
146
|
|
|
147
|
+
@property
|
|
148
|
+
def ref(self) -> Optional['XsdComponentProtocol']: ...
|
|
149
|
+
|
|
149
150
|
|
|
150
151
|
class XsdTypeProtocol(XsdComponentProtocol, Protocol):
|
|
151
152
|
|
|
@@ -260,17 +261,29 @@ class XsdTypeProtocol(XsdComponentProtocol, Protocol):
|
|
|
260
261
|
"""
|
|
261
262
|
...
|
|
262
263
|
|
|
264
|
+
@property
|
|
265
|
+
def model_group(self) -> Optional['XsdGroupProtocol']:
|
|
266
|
+
"""
|
|
267
|
+
A model group if it's a complexType with mixed or element-only content, `None` otherwise.
|
|
268
|
+
"""
|
|
269
|
+
...
|
|
263
270
|
|
|
264
|
-
class XsdAttributeProtocol(XsdComponentProtocol, Protocol):
|
|
265
271
|
|
|
266
|
-
|
|
267
|
-
def type(self) -> Optional[XsdTypeProtocol]: ...
|
|
272
|
+
class XsdComplexTypeProtocol(XsdComponentProtocol, Protocol):
|
|
268
273
|
|
|
269
274
|
@property
|
|
270
|
-
def
|
|
275
|
+
def attributes(self) -> 'XsdAttributeGroupProtocol': ...
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
class XsdGroupProtocol(XsdComponentProtocol, Protocol):
|
|
271
279
|
|
|
280
|
+
def iter_elements(self) -> Iterator['XsdElementProtocol']: ...
|
|
272
281
|
|
|
273
|
-
|
|
282
|
+
|
|
283
|
+
class XsdAttributeProtocol(XsdComponentProtocol, Protocol):
|
|
284
|
+
|
|
285
|
+
@property
|
|
286
|
+
def type(self) -> Optional[XsdTypeProtocol]: ...
|
|
274
287
|
|
|
275
288
|
|
|
276
289
|
class XsdAttributeGroupProtocol(XsdComponentProtocol, Protocol):
|
|
@@ -296,62 +309,40 @@ class XsdAttributeGroupProtocol(XsdComponentProtocol, Protocol):
|
|
|
296
309
|
|
|
297
310
|
def __hash__(self) -> int: ...
|
|
298
311
|
|
|
299
|
-
@property
|
|
300
|
-
def ref(self) -> Optional[Any]: ...
|
|
301
|
-
|
|
302
312
|
|
|
303
313
|
class XsdElementProtocol(XsdComponentProtocol, ElementProtocol, Protocol):
|
|
304
314
|
|
|
305
|
-
def __iter__(self) -> Iterator['XsdElementProtocol']: ...
|
|
306
|
-
|
|
307
|
-
def find(
|
|
308
|
-
self, path: str, namespaces: Optional[NamespacesType] = ...
|
|
309
|
-
) -> Optional[XsdXPathNodeType]: ...
|
|
310
|
-
def iter(self, tag: Optional[str] = ...) -> Iterator['XsdElementProtocol']: ...
|
|
311
|
-
|
|
312
315
|
@property
|
|
313
316
|
def name(self) -> Optional[str]: ...
|
|
314
317
|
|
|
315
318
|
@property
|
|
316
319
|
def type(self) -> Optional[XsdTypeProtocol]: ...
|
|
317
320
|
|
|
318
|
-
@property
|
|
319
|
-
def ref(self) -> Optional[Any]: ...
|
|
320
|
-
|
|
321
321
|
@property
|
|
322
322
|
def attrib(self) -> XsdAttributeGroupProtocol: ...
|
|
323
323
|
|
|
324
|
-
|
|
325
|
-
def xpath_proxy(self) -> 'AbstractSchemaProxy': ...
|
|
326
|
-
|
|
324
|
+
def __iter__(self) -> Iterator['XsdElementProtocol']: ...
|
|
327
325
|
|
|
328
|
-
|
|
329
|
-
|
|
326
|
+
def find(self, path: str, namespaces: Optional[NamespacesType] = ...) \
|
|
327
|
+
-> Union['XsdElementProtocol', None]: ...
|
|
330
328
|
|
|
331
329
|
|
|
332
330
|
class GlobalMapsProtocol(Protocol):
|
|
333
331
|
|
|
334
332
|
@property
|
|
335
|
-
def types(self) -> Mapping[str,
|
|
333
|
+
def types(self) -> Mapping[str, XsdTypeProtocol]: ...
|
|
336
334
|
|
|
337
335
|
@property
|
|
338
|
-
def attributes(self) -> Mapping[str,
|
|
336
|
+
def attributes(self) -> Mapping[str, XsdAttributeProtocol]: ...
|
|
339
337
|
|
|
340
338
|
@property
|
|
341
|
-
def elements(self) -> Mapping[str,
|
|
339
|
+
def elements(self) -> Mapping[str, XsdElementProtocol]: ...
|
|
342
340
|
|
|
343
341
|
@property
|
|
344
342
|
def substitution_groups(self) -> Mapping[str, set[Any]]: ...
|
|
345
343
|
|
|
346
344
|
|
|
347
|
-
class XsdSchemaProtocol(XsdValidatorProtocol,
|
|
348
|
-
|
|
349
|
-
def __iter__(self) -> Iterator[XsdXPathNodeType]: ...
|
|
350
|
-
|
|
351
|
-
def find(
|
|
352
|
-
self, path: str, namespaces: Optional[NamespacesType] = ...
|
|
353
|
-
) -> Optional[XsdXPathNodeType]: ...
|
|
354
|
-
def iter(self, tag: Optional[str] = ...) -> Iterator[XsdXPathNodeType]: ...
|
|
345
|
+
class XsdSchemaProtocol(XsdValidatorProtocol, Protocol):
|
|
355
346
|
|
|
356
347
|
@property
|
|
357
348
|
def validity(self) -> str: ...
|
|
@@ -365,10 +356,13 @@ class XsdSchemaProtocol(XsdValidatorProtocol, ElementProtocol, Protocol):
|
|
|
365
356
|
@property
|
|
366
357
|
def attrib(self) -> MutableMapping[Optional[str], 'XsdAttributeProtocol']: ...
|
|
367
358
|
|
|
368
|
-
|
|
369
|
-
|
|
359
|
+
def __iter__(self) -> Iterator['XsdXPathNodeType']: ...
|
|
360
|
+
|
|
361
|
+
def find(self, path: str, namespaces: Optional[NamespacesType] = ...) \
|
|
362
|
+
-> Union['XsdSchemaProtocol', 'XsdElementProtocol', None]: ...
|
|
370
363
|
|
|
371
364
|
|
|
365
|
+
XsdXPathNodeType = Union[XsdSchemaProtocol, XsdElementProtocol]
|
|
372
366
|
DocumentType = Union[ElementTree, DocumentProtocol]
|
|
373
367
|
ElementType = Union[Element, ElementProtocol]
|
|
374
368
|
SchemaElemType = Union[XsdSchemaProtocol, XsdElementProtocol]
|
|
@@ -386,4 +380,5 @@ __all__ = ['ElementProtocol', 'EtreeElementProtocol', 'LxmlAttribProtocol',
|
|
|
386
380
|
'XsdValidatorProtocol', 'XsdComponentProtocol', 'XsdTypeProtocol',
|
|
387
381
|
'XsdAttributeProtocol', 'XsdAttributeGroupProtocol', 'XsdElementProtocol',
|
|
388
382
|
'GlobalMapsProtocol', 'XsdSchemaProtocol', 'DocumentType', 'ElementType',
|
|
389
|
-
'SchemaElemType', 'CommentType', 'ProcessingInstructionType',
|
|
383
|
+
'SchemaElemType', 'CommentType', 'ProcessingInstructionType',
|
|
384
|
+
'AttribType', 'XsdXPathNodeType']
|
|
@@ -10,19 +10,20 @@
|
|
|
10
10
|
from abc import ABCMeta, abstractmethod
|
|
11
11
|
from collections.abc import Iterator
|
|
12
12
|
from functools import lru_cache
|
|
13
|
-
from typing import
|
|
13
|
+
from typing import cast, Any, Optional, Union
|
|
14
14
|
|
|
15
15
|
from elementpath.exceptions import ElementPathTypeError
|
|
16
16
|
from elementpath.protocols import XsdTypeProtocol, XsdAttributeProtocol, \
|
|
17
|
-
XsdElementProtocol, XsdSchemaProtocol
|
|
17
|
+
XsdElementProtocol, XsdSchemaProtocol, XsdAttributeGroupProtocol
|
|
18
|
+
from elementpath.namespaces import XSD_ANY_ATOMIC_TYPE
|
|
18
19
|
from elementpath.datatypes import AtomicType
|
|
19
20
|
from elementpath.etree import is_etree_element
|
|
20
21
|
from elementpath.xpath_context import XPathSchemaContext
|
|
21
|
-
|
|
22
|
-
if TYPE_CHECKING:
|
|
23
|
-
from elementpath.xpath_tokens import XPath2ParserType
|
|
22
|
+
from elementpath.xpath_tokens import XPath2ParserType
|
|
24
23
|
|
|
25
24
|
PathResult = Union[XsdSchemaProtocol, XsdElementProtocol, XsdAttributeProtocol]
|
|
25
|
+
FindAttrType = Optional[XsdAttributeProtocol]
|
|
26
|
+
FindElemType = Optional[XsdElementProtocol]
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
class AbstractSchemaProxy(metaclass=ABCMeta):
|
|
@@ -33,7 +34,7 @@ class AbstractSchemaProxy(metaclass=ABCMeta):
|
|
|
33
34
|
:param schema: a schema instance compatible with the XsdSchemaProtocol.
|
|
34
35
|
:param base_element: the schema element used as base item for static analysis.
|
|
35
36
|
"""
|
|
36
|
-
__slots__ = ('_schema', '_base_element', '
|
|
37
|
+
__slots__ = ('_schema', '_base_element', '_is_fully_valid')
|
|
37
38
|
|
|
38
39
|
def __init__(self, schema: XsdSchemaProtocol,
|
|
39
40
|
base_element: Optional[XsdElementProtocol] = None) -> None:
|
|
@@ -48,12 +49,6 @@ class AbstractSchemaProxy(metaclass=ABCMeta):
|
|
|
48
49
|
|
|
49
50
|
self._schema = schema
|
|
50
51
|
self._base_element: Optional[XsdElementProtocol] = base_element
|
|
51
|
-
|
|
52
|
-
if self._base_element is not None:
|
|
53
|
-
self._find = self._base_element.find
|
|
54
|
-
else:
|
|
55
|
-
self._find = self._schema.find
|
|
56
|
-
|
|
57
52
|
self._is_fully_valid = False
|
|
58
53
|
|
|
59
54
|
@property
|
|
@@ -109,24 +104,26 @@ class AbstractSchemaProxy(metaclass=ABCMeta):
|
|
|
109
104
|
def find(self, path: str, namespaces: Optional[dict[str, str]] = None) \
|
|
110
105
|
-> Optional[PathResult]:
|
|
111
106
|
"""
|
|
112
|
-
Find a schema element or attribute using an XPath expression.
|
|
107
|
+
Find a schema element or attribute using an XPath expression. Currently unused
|
|
108
|
+
in the code, it may be deprecated in the future.
|
|
113
109
|
|
|
114
110
|
:param path: an XPath expression that selects an element or an attribute node.
|
|
115
111
|
:param namespaces: an optional mapping from namespace prefix to namespace URI.
|
|
116
112
|
:return: The first matching schema component, or ``None`` if there is no match.
|
|
117
113
|
"""
|
|
118
|
-
|
|
114
|
+
if self._base_element is not None:
|
|
115
|
+
return self._base_element.find(path, namespaces=namespaces)
|
|
116
|
+
return self._schema.find(path, namespaces)
|
|
119
117
|
|
|
120
118
|
@lru_cache(maxsize=None)
|
|
121
119
|
def cached_find(self, expanded_path: str) -> Optional[PathResult]:
|
|
122
120
|
"""
|
|
123
|
-
Find a schema element or attribute using an expanded
|
|
124
|
-
|
|
125
|
-
:param expanded_path: an XPath expression with qualified names already resolved \
|
|
126
|
-
to expanded form.
|
|
127
|
-
:return: The first matching schema component, or ``None`` if there is no match.
|
|
121
|
+
Find a schema element or attribute using an expanded XPath expression.
|
|
122
|
+
Currently unused in the code, it may be deprecated in the future.
|
|
128
123
|
"""
|
|
129
|
-
|
|
124
|
+
if self._base_element is not None:
|
|
125
|
+
return self._base_element.find(expanded_path)
|
|
126
|
+
return self._schema.find(expanded_path)
|
|
130
127
|
|
|
131
128
|
@property
|
|
132
129
|
def xsd_version(self) -> str:
|
|
@@ -139,58 +136,98 @@ class AbstractSchemaProxy(metaclass=ABCMeta):
|
|
|
139
136
|
|
|
140
137
|
def get_type(self, qname: str) -> Optional[XsdTypeProtocol]:
|
|
141
138
|
"""
|
|
142
|
-
Get the XSD global type from the schema's scope.
|
|
143
|
-
return an object that supports the protocols `XsdTypeProtocol`, or `None` if
|
|
144
|
-
the global type is not found.
|
|
139
|
+
Get the XSD global type from the schema's scope.
|
|
145
140
|
|
|
146
141
|
:param qname: the fully qualified name of the type to retrieve.
|
|
147
142
|
:returns: an object that represents an XSD type or `None`.
|
|
148
143
|
"""
|
|
149
|
-
|
|
150
|
-
if isinstance(xsd_type, tuple):
|
|
151
|
-
return None
|
|
152
|
-
return xsd_type
|
|
144
|
+
return self._schema.maps.types.get(qname)
|
|
153
145
|
|
|
154
146
|
def get_attribute(self, qname: str) -> Optional[XsdAttributeProtocol]:
|
|
155
147
|
"""
|
|
156
|
-
Get the XSD global attribute from the schema's scope.
|
|
157
|
-
return an object that supports the protocol `XsdAttributeProtocol`, or `None` if
|
|
158
|
-
the global attribute is not found.
|
|
148
|
+
Get the XSD global attribute from the schema's scope.
|
|
159
149
|
|
|
160
150
|
:param qname: the fully qualified name of the attribute to retrieve.
|
|
161
|
-
:returns: an object that represents an XSD
|
|
151
|
+
:returns: an object that represents an XSD type or `None`.
|
|
162
152
|
"""
|
|
163
|
-
|
|
164
|
-
if isinstance(xsd_attribute, tuple):
|
|
165
|
-
return None
|
|
166
|
-
return xsd_attribute
|
|
153
|
+
return self._schema.maps.attributes.get(qname)
|
|
167
154
|
|
|
168
155
|
def get_element(self, qname: str) -> Optional[XsdElementProtocol]:
|
|
169
156
|
"""
|
|
170
|
-
Get the XSD global element from the schema's scope.
|
|
171
|
-
return an object that supports the protocol `XsdElementProtocol` interface, or
|
|
172
|
-
`None` if the global element is not found.
|
|
157
|
+
Get the XSD global element from the schema's scope.
|
|
173
158
|
|
|
174
159
|
:param qname: the fully qualified name of the element to retrieve.
|
|
175
|
-
:returns: an object that represents an XSD
|
|
160
|
+
:returns: an object that represents an XSD type or `None`.
|
|
176
161
|
"""
|
|
177
|
-
|
|
178
|
-
if isinstance(xsd_element, tuple):
|
|
179
|
-
return None
|
|
180
|
-
return xsd_element
|
|
162
|
+
return self._schema.maps.elements.get(qname)
|
|
181
163
|
|
|
182
164
|
def get_substitution_group(self, qname: str) -> Optional[set[XsdElementProtocol]]:
|
|
183
165
|
"""
|
|
184
|
-
Get a substitution group.
|
|
185
|
-
|
|
186
|
-
each item of the returned list must be an object that implements the
|
|
187
|
-
`AbstractXsdElement` interface.
|
|
166
|
+
Get a substitution group. Currently unused in the code, it may be deprecated
|
|
167
|
+
in the future.
|
|
188
168
|
|
|
189
169
|
:param qname: the fully qualified name of the substitution group to retrieve.
|
|
190
170
|
:returns: a list containing substitution elements or `None`.
|
|
191
171
|
"""
|
|
192
172
|
return self._schema.maps.substitution_groups.get(qname)
|
|
193
173
|
|
|
174
|
+
def get_attribute_type(self, name: str, parent_type: Optional[XsdTypeProtocol] = None) \
|
|
175
|
+
-> Optional[XsdTypeProtocol]:
|
|
176
|
+
"""
|
|
177
|
+
Get the XSD attribute type if the provided name is matching in the scope,
|
|
178
|
+
otherwise return None.
|
|
179
|
+
|
|
180
|
+
:param name: the name of the attribute to retrieve.
|
|
181
|
+
:param parent_type: an optional XSD type that represents the scope where matching \
|
|
182
|
+
the attribute name. If not provided, the scope is assumed to be the global scope.
|
|
183
|
+
"""
|
|
184
|
+
if parent_type is None:
|
|
185
|
+
try:
|
|
186
|
+
return self._schema.maps.attributes[name].type
|
|
187
|
+
except KeyError:
|
|
188
|
+
return None
|
|
189
|
+
elif hasattr(parent_type, 'attributes'):
|
|
190
|
+
attributes = cast(XsdAttributeGroupProtocol, parent_type.attributes)
|
|
191
|
+
if name in attributes:
|
|
192
|
+
return attributes[name].type
|
|
193
|
+
elif None in attributes and attributes[None].is_matching(name):
|
|
194
|
+
try:
|
|
195
|
+
return self._schema.maps.attributes[name].type
|
|
196
|
+
except KeyError:
|
|
197
|
+
return None
|
|
198
|
+
elif name.startswith('{http://www.w3.org/2001/XMLSchema-instance}'):
|
|
199
|
+
return self._schema.maps.types.get(XSD_ANY_ATOMIC_TYPE)
|
|
200
|
+
|
|
201
|
+
return None
|
|
202
|
+
|
|
203
|
+
def get_child_type(self, name: str, parent_type: Optional[XsdTypeProtocol] = None) \
|
|
204
|
+
-> Optional[XsdTypeProtocol]:
|
|
205
|
+
"""
|
|
206
|
+
Get the child XSD type if the provided name is matching in the scope,
|
|
207
|
+
otherwise return None.
|
|
208
|
+
|
|
209
|
+
:param name: the name of the child element to match.
|
|
210
|
+
:param parent_type: an optional XSD type that represents the scope where matching \
|
|
211
|
+
the child element name. If `None` or not provided the scope is the schema.
|
|
212
|
+
"""
|
|
213
|
+
if parent_type is None:
|
|
214
|
+
try:
|
|
215
|
+
return self._schema.maps.elements[name].type
|
|
216
|
+
except KeyError:
|
|
217
|
+
return None
|
|
218
|
+
elif (content := parent_type.model_group) is not None:
|
|
219
|
+
for xsd_element in content.iter_elements():
|
|
220
|
+
if xsd_element.is_matching(name):
|
|
221
|
+
if xsd_element.name == name:
|
|
222
|
+
return xsd_element.type
|
|
223
|
+
else:
|
|
224
|
+
# a wildcard or a substitute
|
|
225
|
+
try:
|
|
226
|
+
return self._schema.maps.elements[name].type
|
|
227
|
+
except KeyError:
|
|
228
|
+
return None
|
|
229
|
+
return None
|
|
230
|
+
|
|
194
231
|
@abstractmethod
|
|
195
232
|
def is_instance(self, obj: Any, type_qname: str) -> bool:
|
|
196
233
|
"""
|
|
@@ -20,6 +20,7 @@ from elementpath.datatypes import AnyAtomicType, AnyURI, AbstractDateTime, \
|
|
|
20
20
|
AbstractBinary, UntypedAtomic, QName
|
|
21
21
|
from elementpath.xpath_nodes import XPathNode, ElementNode, AttributeNode, DocumentNode, \
|
|
22
22
|
NamespaceNode, TextNode, CommentNode
|
|
23
|
+
from elementpath.xpath_nodes import EtreeElementNode
|
|
23
24
|
from elementpath.xpath_tokens import XPathToken, XPathMap, XPathArray
|
|
24
25
|
from elementpath.protocols import EtreeElementProtocol, LxmlElementProtocol
|
|
25
26
|
|
|
@@ -147,7 +148,9 @@ def get_serialization_params(params: Union[None, ElementNode, XPathMap] = None,
|
|
|
147
148
|
raise xpath_error('SEPM0019', token=token)
|
|
148
149
|
|
|
149
150
|
for child in root:
|
|
150
|
-
if child.tag
|
|
151
|
+
if callable(child.tag):
|
|
152
|
+
continue
|
|
153
|
+
elif child.tag == SER_PARAM_OMIT_XML_DECLARATION:
|
|
151
154
|
value = child.get('value')
|
|
152
155
|
if value not in ('yes', 'no') or len(child.attrib) > 1:
|
|
153
156
|
raise xpath_error('SEPM0017', token=token)
|
|
@@ -289,7 +292,8 @@ def serialize_to_xml(elements: Iterable[Any],
|
|
|
289
292
|
chunks = []
|
|
290
293
|
for item in iter_normalized(elements, item_separator):
|
|
291
294
|
if isinstance(item, ElementNode):
|
|
292
|
-
item
|
|
295
|
+
assert isinstance(item, EtreeElementNode)
|
|
296
|
+
elem = item.obj
|
|
293
297
|
elif isinstance(item, (AttributeNode, NamespaceNode)):
|
|
294
298
|
raise xpath_error('SENR0001', token=token)
|
|
295
299
|
elif isinstance(item, TextNode):
|
|
@@ -306,15 +310,15 @@ def serialize_to_xml(elements: Iterable[Any],
|
|
|
306
310
|
|
|
307
311
|
try:
|
|
308
312
|
cks = etree_module.tostringlist(
|
|
309
|
-
|
|
313
|
+
elem, encoding='utf-8', method=method, **kwargs
|
|
310
314
|
)
|
|
311
315
|
except TypeError:
|
|
312
|
-
ck = etree_module.tostring(
|
|
313
|
-
chunks.append(ck.decode('utf-8').rstrip(
|
|
316
|
+
ck = etree_module.tostring(elem, encoding='utf-8', method=method)
|
|
317
|
+
chunks.append(ck.decode('utf-8').rstrip(elem.tail))
|
|
314
318
|
else:
|
|
315
319
|
if cks and cks[0].startswith(b'<?'):
|
|
316
320
|
cks[0] = cks[0].replace(b'\'', b'"')
|
|
317
|
-
chunks.append(b'\n'.join(cks).decode('utf-8').rstrip(
|
|
321
|
+
chunks.append(b'\n'.join(cks).decode('utf-8').rstrip(elem.tail))
|
|
318
322
|
|
|
319
323
|
if not character_map:
|
|
320
324
|
return (item_separator or '').join(chunks)
|
|
@@ -347,6 +351,7 @@ def serialize_to_json(elements: Iterable[Any],
|
|
|
347
351
|
if isinstance(obj, DocumentNode):
|
|
348
352
|
return ''.join(self.default(child) for child in obj)
|
|
349
353
|
elif isinstance(obj, ElementNode):
|
|
354
|
+
assert isinstance(obj, EtreeElementNode)
|
|
350
355
|
elem = obj.obj
|
|
351
356
|
assert etree_module is not None
|
|
352
357
|
|