elementpath 5.1.1__tar.gz → 5.1.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.1.1 → elementpath-5.1.2}/CHANGELOG.rst +6 -0
- {elementpath-5.1.1/elementpath.egg-info → elementpath-5.1.2}/PKG-INFO +1 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/conf.py +1 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/__init__.py +1 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/any_types.py +2 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/untyped.py +1 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/helpers.py +7 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/character_classes.py +27 -3
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/serialization.py +25 -8
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/base.py +5 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/functions.py +4 -6
- {elementpath-5.1.1 → elementpath-5.1.2/elementpath.egg-info}/PKG-INFO +1 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/pyproject.toml +1 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_regex.py +4 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath2_parser.py +15 -1
- {elementpath-5.1.1 → elementpath-5.1.2}/tox.ini +18 -7
- {elementpath-5.1.1 → elementpath-5.1.2}/LICENSE +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/MANIFEST.in +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/README.rst +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/Makefile +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/advanced.rst +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/index.rst +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/introduction.rst +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/make.bat +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/pratt_api.rst +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/requirements.txt +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/doc/xpath_api.rst +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/aliases.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/collations.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/compare.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/binary.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/datetime.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/lists.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/numeric.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/proxies.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/qname.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/string.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/datatypes/uri.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/decoder.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/etree.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/exceptions.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/extras/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/extras/pathnodes.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/namespaces.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/protocols.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/py.typed +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/categories_fallback.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/codepoints.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/patterns.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/unicode_blocks.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/unicode_categories.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/regex/unicode_subsets.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/schema_proxy.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/sequence_types.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/sequences.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/tdop.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/tree_builders.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/validators/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/validators/analyze-string.xsd +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/validators/schema-for-json.xsd +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath1/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath1/_xpath1_axes.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath1/_xpath1_functions.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath1/_xpath1_operators.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath1/xpath1_parser.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath2/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath2/_xpath2_constructors.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath2/_xpath2_functions.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath2/_xpath2_operators.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath2/xpath2_parser.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath3.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath30/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath30/_translation_maps.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath30/_xpath30_functions.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath30/_xpath30_operators.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath30/xpath30_helpers.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath30/xpath30_parser.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath31/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath31/_xpath31_functions.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath31/_xpath31_operators.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath31/xpath31_parser.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_context.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_nodes.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_selectors.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/arrays.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/axes.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/contructors.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/maps.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath/xpath_tokens/tokens.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath.egg-info/SOURCES.txt +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath.egg-info/dependency_links.txt +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath.egg-info/requires.txt +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/elementpath.egg-info/top_level.txt +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/requirements-dev.txt +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/scripts/generate_codepoints.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/setup.cfg +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/__init__.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/memory_profiling.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/mypy_tests/advanced.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/mypy_tests/protocols.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/mypy_tests/selectors.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/analyze-string.xsd +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/external_entity.xml +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/sample.xml +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/schema-for-json.xsd +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/unparsed_entity.xml +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/unused_external_entity.xml +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/unused_unparsed_entity.xml +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/resources/with_entity.xml +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/run_all_tests.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/run_typing_tests.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/run_w3c_tests.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_collations.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_compare.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_datatypes.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_decoder.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_etree.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_exceptions.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_helpers.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_namespaces.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_package.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_pathnodes.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_schema_context.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_schema_proxy.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_selectors.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_sequence_types.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_sequences.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_serialization.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_tdop_parser.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_tree_builders.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_validators.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath1_parser.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath2_constructors.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath2_functions.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath30.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath31.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath_context.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath_nodes.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/test_xpath_tokens.py +0 -0
- {elementpath-5.1.1 → elementpath-5.1.2}/tests/xpath_test_class.py +0 -0
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
CHANGELOG
|
|
3
3
|
*********
|
|
4
4
|
|
|
5
|
+
`v5.1.2`_ (2026-06-13)
|
|
6
|
+
======================
|
|
7
|
+
* Include PR #101 (invalid sequence type error code for external functions)
|
|
8
|
+
* Fix issues #100 and #102
|
|
9
|
+
|
|
5
10
|
`v5.1.1`_ (2026-01-20)
|
|
6
11
|
======================
|
|
7
12
|
* Fix external function registrations (issue #099)
|
|
@@ -537,3 +542,4 @@ CHANGELOG
|
|
|
537
542
|
.. _v5.0.4: https://github.com/sissaschool/elementpath/compare/v5.0.3...v5.0.4
|
|
538
543
|
.. _v5.1.0: https://github.com/sissaschool/elementpath/compare/v5.0.4...v5.1.0
|
|
539
544
|
.. _v5.1.1: https://github.com/sissaschool/elementpath/compare/v5.1.0...v5.1.1
|
|
545
|
+
.. _v5.1.2: https://github.com/sissaschool/elementpath/compare/v5.1.1...v5.1.2
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
#
|
|
8
8
|
# @author Davide Brunato <brunato@sissa.it>
|
|
9
9
|
#
|
|
10
|
+
import sys
|
|
10
11
|
from abc import ABCMeta, abstractmethod
|
|
11
12
|
from types import MappingProxyType
|
|
12
13
|
from typing import Any
|
|
@@ -58,7 +59,7 @@ class AtomicTypeMeta(ABCMeta):
|
|
|
58
59
|
extended_name = f'{{{namespace}}}{name}' if namespace else name
|
|
59
60
|
prefixed_name = f'{prefix}:{name}' if prefix else name
|
|
60
61
|
|
|
61
|
-
if not cls.__doc__:
|
|
62
|
+
if not cls.__doc__ and sys.flags.optimize < 2:
|
|
62
63
|
if ver := getattr(cls, '_xsd_version', ''):
|
|
63
64
|
ver = ver + ' '
|
|
64
65
|
params = getattr(cls, '__init__', None).__doc__ or ''
|
|
@@ -112,6 +112,13 @@ class LazyPattern:
|
|
|
112
112
|
self._compiled = re.compile(self._pattern, self._flags)
|
|
113
113
|
return self._compiled.search(string)
|
|
114
114
|
|
|
115
|
+
def split(self, string: str) -> list[str]:
|
|
116
|
+
try:
|
|
117
|
+
return self._compiled.split(string)
|
|
118
|
+
except AttributeError:
|
|
119
|
+
self._compiled = re.compile(self._pattern, self._flags)
|
|
120
|
+
return self._compiled.split(string)
|
|
121
|
+
|
|
115
122
|
|
|
116
123
|
class Patterns:
|
|
117
124
|
"""
|
|
@@ -14,6 +14,7 @@ from collections.abc import Callable, Iterator, MutableSet
|
|
|
14
14
|
from itertools import chain
|
|
15
15
|
from typing import AbstractSet, Any, Optional, Union
|
|
16
16
|
|
|
17
|
+
from elementpath.helpers import LazyPattern
|
|
17
18
|
from .codepoints import RegexError
|
|
18
19
|
from .unicode_subsets import UnicodeSubset, lazy_subset, unicode_subset, unicode_category
|
|
19
20
|
|
|
@@ -87,6 +88,29 @@ CHARACTER_ESCAPES: dict[str, Union[str, Callable[[], UnicodeSubset]]] = {
|
|
|
87
88
|
'\\W': w_shortcut,
|
|
88
89
|
}
|
|
89
90
|
|
|
91
|
+
re_char_set = LazyPattern(r'(\\[nrt|.\-^?*+{}()\]sSdDiIcCwW]|\\[pP]{[a-zA-Z\-0-9]+})')
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def get_charset_parts(charset: str) -> list[str]:
|
|
95
|
+
parts: list[str] = []
|
|
96
|
+
prev = ''
|
|
97
|
+
for part in re_char_set.split(charset):
|
|
98
|
+
if not part:
|
|
99
|
+
continue
|
|
100
|
+
elif part.endswith('-') and part != '\\-':
|
|
101
|
+
prev = part
|
|
102
|
+
elif part.startswith('-') and parts:
|
|
103
|
+
parts[-1] += prev + part
|
|
104
|
+
prev = ''
|
|
105
|
+
else:
|
|
106
|
+
parts.append(prev + part)
|
|
107
|
+
prev = ''
|
|
108
|
+
else:
|
|
109
|
+
if prev:
|
|
110
|
+
parts.append(prev)
|
|
111
|
+
|
|
112
|
+
return parts
|
|
113
|
+
|
|
90
114
|
|
|
91
115
|
class CharacterClass(MutableSet[int]):
|
|
92
116
|
"""
|
|
@@ -96,7 +120,7 @@ class CharacterClass(MutableSet[int]):
|
|
|
96
120
|
:param xsd_version: the reference XSD version for syntax variants. Defaults to '1.0'.
|
|
97
121
|
TODO: implement __ior__, __iand__, __ixor__ operators for a full mutable set class.
|
|
98
122
|
"""
|
|
99
|
-
_re_char_set = re.compile(r'(
|
|
123
|
+
_re_char_set = re.compile(r'(\\[nrt|.\-^?*+{}()\]sSdDiIcCwW]|\\[pP]{[a-zA-Z\-0-9]+})')
|
|
100
124
|
_re_unicode_ref = re.compile(r'\\([pP]){([\w-]+)}')
|
|
101
125
|
|
|
102
126
|
__slots__ = 'xsd_version', 'positive', 'negative'
|
|
@@ -172,7 +196,7 @@ class CharacterClass(MutableSet[int]):
|
|
|
172
196
|
if isinstance(charset, int):
|
|
173
197
|
charset = chr(charset)
|
|
174
198
|
|
|
175
|
-
for part in
|
|
199
|
+
for part in get_charset_parts(charset):
|
|
176
200
|
if part in CHARACTER_ESCAPES:
|
|
177
201
|
value = CHARACTER_ESCAPES[part]
|
|
178
202
|
if isinstance(value, str):
|
|
@@ -204,7 +228,7 @@ class CharacterClass(MutableSet[int]):
|
|
|
204
228
|
if isinstance(charset, int):
|
|
205
229
|
charset = chr(charset)
|
|
206
230
|
|
|
207
|
-
for part in
|
|
231
|
+
for part in get_charset_parts(charset):
|
|
208
232
|
if part in CHARACTER_ESCAPES:
|
|
209
233
|
value = CHARACTER_ESCAPES[part]
|
|
210
234
|
if isinstance(value, str):
|
|
@@ -38,6 +38,31 @@ SER_PARAM_STANDALONE = '{%s}standalone' % XSLT_XQUERY_SERIALIZATION_NAMESPACE
|
|
|
38
38
|
SER_PARAM_ITEM_SEPARATOR = '{%s}item-separator' % XSLT_XQUERY_SERIALIZATION_NAMESPACE
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
class MapEncodingDict(dict[AnyAtomicType | None, Any]):
|
|
42
|
+
"""
|
|
43
|
+
A bridge dict-type for encoding XPath maps to JSON.
|
|
44
|
+
"""
|
|
45
|
+
def __init__(self, items: Any) -> None:
|
|
46
|
+
self[None] = None
|
|
47
|
+
self._items = items
|
|
48
|
+
|
|
49
|
+
def __getitem__(self, key: AnyAtomicType | None, /) -> Any:
|
|
50
|
+
for k, v in self._items:
|
|
51
|
+
if key == k:
|
|
52
|
+
return v
|
|
53
|
+
raise KeyError(key)
|
|
54
|
+
|
|
55
|
+
def __iter__(self) -> Iterator[AnyAtomicType | None]:
|
|
56
|
+
for k, _ in self._items:
|
|
57
|
+
yield k
|
|
58
|
+
|
|
59
|
+
def __len__(self) -> int:
|
|
60
|
+
return len(self._items)
|
|
61
|
+
|
|
62
|
+
def items(self) -> Any:
|
|
63
|
+
return self._items
|
|
64
|
+
|
|
65
|
+
|
|
41
66
|
def get_serialization_params(params: Union[None, ElementNode, XPathMap] = None,
|
|
42
67
|
token: Optional[XPathToken] = None) -> dict['str', Any]:
|
|
43
68
|
|
|
@@ -337,14 +362,6 @@ def serialize_to_json(elements: Iterable[Any],
|
|
|
337
362
|
if etree_module is None:
|
|
338
363
|
etree_module = ElementTree
|
|
339
364
|
|
|
340
|
-
class MapEncodingDict(dict): # type: ignore[type-arg]
|
|
341
|
-
def __init__(self, items: Any) -> None:
|
|
342
|
-
self[None] = None
|
|
343
|
-
self._items = items
|
|
344
|
-
|
|
345
|
-
def items(self) -> Any:
|
|
346
|
-
return self._items
|
|
347
|
-
|
|
348
365
|
class XPathEncoder(json.JSONEncoder):
|
|
349
366
|
|
|
350
367
|
def default(self, obj: Any) -> Any:
|
|
@@ -154,6 +154,10 @@ class XPathToken(Token[ta.XPathTokenType]):
|
|
|
154
154
|
return True
|
|
155
155
|
return not self._items[1].label.endswith('function')
|
|
156
156
|
|
|
157
|
+
def is_reference(self) -> bool:
|
|
158
|
+
"""Is `True` if the token is a function reference, `False` otherwise."""
|
|
159
|
+
return False
|
|
160
|
+
|
|
157
161
|
###
|
|
158
162
|
# Tokens tree analysis methods
|
|
159
163
|
def iter_leaf_elements(self) -> Iterator[str]:
|
|
@@ -321,7 +325,7 @@ class XPathToken(Token[ta.XPathTokenType]):
|
|
|
321
325
|
else:
|
|
322
326
|
return default
|
|
323
327
|
else:
|
|
324
|
-
if
|
|
328
|
+
if token.is_reference():
|
|
325
329
|
return token # It's a function reference
|
|
326
330
|
|
|
327
331
|
item = None
|
|
@@ -227,10 +227,8 @@ class XPathFunction(XPathToken):
|
|
|
227
227
|
else:
|
|
228
228
|
return None
|
|
229
229
|
|
|
230
|
-
def is_reference(self) ->
|
|
231
|
-
|
|
232
|
-
return False
|
|
233
|
-
return self.nargs and not len(self._items)
|
|
230
|
+
def is_reference(self) -> bool:
|
|
231
|
+
return isinstance(self.nargs, int) and self.nargs != 0 and not self._items
|
|
234
232
|
|
|
235
233
|
def nud(self) -> 'XPathFunction':
|
|
236
234
|
if not self.parser.parse_arguments:
|
|
@@ -413,12 +411,12 @@ class ExternalFunction(XPathFunction):
|
|
|
413
411
|
for k, (arg, st) in enumerate(zip(args, self.sequence_types), start=1):
|
|
414
412
|
if not match_sequence_type(arg, st, self.parser):
|
|
415
413
|
msg = f"{ordinal(k)} argument does not match sequence type {st!r}"
|
|
416
|
-
raise self.error('
|
|
414
|
+
raise self.error('XPTY0004', msg)
|
|
417
415
|
|
|
418
416
|
result = self.callback(*args)
|
|
419
417
|
if not match_sequence_type(result, self.sequence_types[-1], self.parser):
|
|
420
418
|
msg = f"Result does not match sequence type {self.sequence_types[-1]!r}"
|
|
421
|
-
raise self.error('
|
|
419
|
+
raise self.error('XPTY0004', msg)
|
|
422
420
|
return result
|
|
423
421
|
|
|
424
422
|
return self.callback(*args)
|
|
@@ -799,6 +799,10 @@ class TestPatterns(unittest.TestCase):
|
|
|
799
799
|
self.assertIsNone(pattern.search('first\tsecond\tthird'))
|
|
800
800
|
self.assertEqual(pattern.search('first second third').group(0), 'first second third')
|
|
801
801
|
|
|
802
|
+
def test_issue_100(self):
|
|
803
|
+
self.assertNotIn('\\s', translate_pattern(r"[\s\-',]{1,255}"))
|
|
804
|
+
self.assertNotIn('\\s', translate_pattern(r"[\-\s',]{1,255}"))
|
|
805
|
+
|
|
802
806
|
def test_dot_wildcard(self):
|
|
803
807
|
regex = translate_pattern('.+', anchors=False)
|
|
804
808
|
self.assertEqual(regex, '^([^\\r\\n]+)$(?!\\n\\Z)')
|
|
@@ -1287,7 +1287,7 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest):
|
|
|
1287
1287
|
self.check_selector('//@*/local-name()', root, result)
|
|
1288
1288
|
self.check_selector('//@*/name()', root, result)
|
|
1289
1289
|
|
|
1290
|
-
def
|
|
1290
|
+
def test_external_function_registration__issue_099(self):
|
|
1291
1291
|
parser = self.parser.__class__(namespaces={'tns0': 'https://elementpath.test/tns0'})
|
|
1292
1292
|
|
|
1293
1293
|
def foo(x):
|
|
@@ -1354,6 +1354,20 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest):
|
|
|
1354
1354
|
|
|
1355
1355
|
assert issubclass(token_class, ProxyToken)
|
|
1356
1356
|
self.assertEqual(token_class.lookup_name, 'foo')
|
|
1357
|
+
self.assertFalse(hasattr(token_class, 'nargs'))
|
|
1358
|
+
self.assertIsNone(token_class.namespace)
|
|
1359
|
+
|
|
1360
|
+
token_class = parser.symbol_table['{https://elementpath.test/tns0}foo']
|
|
1361
|
+
self.assertTrue(issubclass(token_class, XPathFunction))
|
|
1362
|
+
|
|
1363
|
+
assert issubclass(token_class, XPathFunction)
|
|
1364
|
+
self.assertEqual(token_class.lookup_name, '{https://elementpath.test/tns0}foo')
|
|
1365
|
+
self.assertEqual(token_class.nargs, 2)
|
|
1366
|
+
self.assertEqual(token_class.namespace, 'https://elementpath.test/tns0')
|
|
1367
|
+
|
|
1368
|
+
with self.assertRaises(ValueError) as ctx:
|
|
1369
|
+
parser.external_function(foo2, 'foo', prefix='tns0')
|
|
1370
|
+
self.assertEqual(str(ctx.exception), "function 'tns0:foo' is already registered")
|
|
1357
1371
|
|
|
1358
1372
|
token = parser.parse('foo(8)')
|
|
1359
1373
|
self.assertEqual(token.evaluate(), '8')
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
# and then run "tox" from this directory.
|
|
5
5
|
|
|
6
6
|
[tox]
|
|
7
|
-
min_version = 4.
|
|
7
|
+
min_version = 4.50
|
|
8
8
|
envlist =
|
|
9
|
-
py{310,311,312,313,314,314,315,315-lxml,
|
|
10
|
-
mypy-py{310,311,312,313,314,315
|
|
9
|
+
flake8, py{310,311,312,313,314,314,314t,314-opt,315,315-lxml,py310,py311},
|
|
10
|
+
docs, mypy-py{310,311,312,313,314,315}, pytest, coverage,
|
|
11
11
|
xmlschema{401,410}, w3c-xsdtests, w3c-qt3tests
|
|
12
12
|
skip_missing_interpreters = true
|
|
13
13
|
work_dir = {tox_root}/../.tox/elementpath
|
|
@@ -22,6 +22,18 @@ set_env =
|
|
|
22
22
|
py313: TEST_UNICODE_INSTALLATION = 6.2.0
|
|
23
23
|
commands = python -m unittest
|
|
24
24
|
|
|
25
|
+
[testenv:py314-opt]
|
|
26
|
+
deps =
|
|
27
|
+
lxml
|
|
28
|
+
xmlschema~=4.2.0
|
|
29
|
+
commands = python -OO -m unittest
|
|
30
|
+
|
|
31
|
+
[testenv:py314t]
|
|
32
|
+
deps =
|
|
33
|
+
lxml
|
|
34
|
+
xmlschema~=4.2.0
|
|
35
|
+
commands = python -Xgil=0 -m unittest
|
|
36
|
+
|
|
25
37
|
[testenv:py315]
|
|
26
38
|
deps =
|
|
27
39
|
xmlschema~=4.2.0
|
|
@@ -54,10 +66,9 @@ commands =
|
|
|
54
66
|
flake8 elementpath
|
|
55
67
|
flake8 tests
|
|
56
68
|
|
|
57
|
-
[testenv:mypy-py{310,311,312,313,314,315
|
|
69
|
+
[testenv:mypy-py{310,311,312,313,314,315}]
|
|
58
70
|
deps =
|
|
59
|
-
mypy==1.
|
|
60
|
-
mypy==1.18.2; platform_python_implementation == 'PyPy'
|
|
71
|
+
mypy==2.1.0
|
|
61
72
|
xmlschema~=4.2.0
|
|
62
73
|
lxml-stubs
|
|
63
74
|
commands =
|
|
@@ -89,7 +100,7 @@ set_env =
|
|
|
89
100
|
xmlschema420: VERSION = 4.2.0
|
|
90
101
|
change_dir = {env_tmp_dir}
|
|
91
102
|
deps =
|
|
92
|
-
mypy==1.
|
|
103
|
+
mypy==2.1.0
|
|
93
104
|
lxml
|
|
94
105
|
lxml-stubs
|
|
95
106
|
jinja2
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|