elementpath 5.0.0__tar.gz → 5.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. {elementpath-5.0.0 → elementpath-5.0.1}/CHANGELOG.rst +5 -0
  2. {elementpath-5.0.0/elementpath.egg-info → elementpath-5.0.1}/PKG-INFO +1 -1
  3. {elementpath-5.0.0 → elementpath-5.0.1}/doc/conf.py +1 -1
  4. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/__init__.py +1 -1
  5. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/protocols.py +35 -41
  6. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/categories_fallback.py +0 -1
  7. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/schema_proxy.py +84 -47
  8. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/tree_builders.py +10 -6
  9. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath2/_xpath2_operators.py +1 -2
  10. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath_context.py +16 -16
  11. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath_nodes.py +40 -44
  12. {elementpath-5.0.0 → elementpath-5.0.1/elementpath.egg-info}/PKG-INFO +1 -1
  13. {elementpath-5.0.0 → elementpath-5.0.1}/pyproject.toml +1 -1
  14. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_schema_proxy.py +241 -1
  15. {elementpath-5.0.0 → elementpath-5.0.1}/LICENSE +0 -0
  16. {elementpath-5.0.0 → elementpath-5.0.1}/MANIFEST.in +0 -0
  17. {elementpath-5.0.0 → elementpath-5.0.1}/README.rst +0 -0
  18. {elementpath-5.0.0 → elementpath-5.0.1}/doc/Makefile +0 -0
  19. {elementpath-5.0.0 → elementpath-5.0.1}/doc/advanced.rst +0 -0
  20. {elementpath-5.0.0 → elementpath-5.0.1}/doc/index.rst +0 -0
  21. {elementpath-5.0.0 → elementpath-5.0.1}/doc/introduction.rst +0 -0
  22. {elementpath-5.0.0 → elementpath-5.0.1}/doc/make.bat +0 -0
  23. {elementpath-5.0.0 → elementpath-5.0.1}/doc/pratt_api.rst +0 -0
  24. {elementpath-5.0.0 → elementpath-5.0.1}/doc/requirements.txt +0 -0
  25. {elementpath-5.0.0 → elementpath-5.0.1}/doc/xpath_api.rst +0 -0
  26. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/aliases.py +0 -0
  27. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/collations.py +0 -0
  28. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/compare.py +0 -0
  29. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/__init__.py +0 -0
  30. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/atomic_types.py +0 -0
  31. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/binary.py +0 -0
  32. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/datetime.py +0 -0
  33. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/numeric.py +0 -0
  34. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/proxies.py +0 -0
  35. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/qname.py +0 -0
  36. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/string.py +0 -0
  37. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/untyped.py +0 -0
  38. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/datatypes/uri.py +0 -0
  39. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/decoder.py +0 -0
  40. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/etree.py +0 -0
  41. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/exceptions.py +0 -0
  42. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/extras/__init__.py +0 -0
  43. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/extras/pathnodes.py +0 -0
  44. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/helpers.py +0 -0
  45. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/namespaces.py +0 -0
  46. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/py.typed +0 -0
  47. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/__init__.py +0 -0
  48. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/character_classes.py +0 -0
  49. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/codepoints.py +0 -0
  50. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/patterns.py +0 -0
  51. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/unicode_blocks.py +0 -0
  52. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/unicode_categories.py +0 -0
  53. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/regex/unicode_subsets.py +0 -0
  54. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/sequence_types.py +0 -0
  55. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/serialization.py +0 -0
  56. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/tdop.py +0 -0
  57. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/validators/__init__.py +0 -0
  58. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/validators/analyze-string.xsd +0 -0
  59. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/validators/schema-for-json.xsd +0 -0
  60. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath1/__init__.py +0 -0
  61. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath1/_xpath1_axes.py +0 -0
  62. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath1/_xpath1_functions.py +0 -0
  63. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath1/_xpath1_operators.py +0 -0
  64. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath1/xpath1_parser.py +0 -0
  65. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath2/__init__.py +0 -0
  66. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath2/_xpath2_constructors.py +0 -0
  67. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath2/_xpath2_functions.py +0 -0
  68. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath2/xpath2_parser.py +0 -0
  69. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath3.py +0 -0
  70. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath30/__init__.py +0 -0
  71. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath30/_translation_maps.py +0 -0
  72. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath30/_xpath30_functions.py +0 -0
  73. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath30/_xpath30_operators.py +0 -0
  74. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath30/xpath30_helpers.py +0 -0
  75. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath30/xpath30_parser.py +0 -0
  76. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath31/__init__.py +0 -0
  77. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath31/_xpath31_functions.py +0 -0
  78. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath31/_xpath31_operators.py +0 -0
  79. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath31/xpath31_parser.py +0 -0
  80. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath_selectors.py +0 -0
  81. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath/xpath_tokens.py +0 -0
  82. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath.egg-info/SOURCES.txt +0 -0
  83. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath.egg-info/dependency_links.txt +0 -0
  84. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath.egg-info/requires.txt +0 -0
  85. {elementpath-5.0.0 → elementpath-5.0.1}/elementpath.egg-info/top_level.txt +0 -0
  86. {elementpath-5.0.0 → elementpath-5.0.1}/requirements-dev.txt +0 -0
  87. {elementpath-5.0.0 → elementpath-5.0.1}/scripts/generate_codepoints.py +0 -0
  88. {elementpath-5.0.0 → elementpath-5.0.1}/setup.cfg +0 -0
  89. {elementpath-5.0.0 → elementpath-5.0.1}/tests/__init__.py +0 -0
  90. {elementpath-5.0.0 → elementpath-5.0.1}/tests/memory_profiling.py +0 -0
  91. {elementpath-5.0.0 → elementpath-5.0.1}/tests/mypy_tests/advanced.py +0 -0
  92. {elementpath-5.0.0 → elementpath-5.0.1}/tests/mypy_tests/protocols.py +0 -0
  93. {elementpath-5.0.0 → elementpath-5.0.1}/tests/mypy_tests/selectors.py +0 -0
  94. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/analyze-string.xsd +0 -0
  95. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/external_entity.xml +0 -0
  96. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/sample.xml +0 -0
  97. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/schema-for-json.xsd +0 -0
  98. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/unparsed_entity.xml +0 -0
  99. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/unused_external_entity.xml +0 -0
  100. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/unused_unparsed_entity.xml +0 -0
  101. {elementpath-5.0.0 → elementpath-5.0.1}/tests/resources/with_entity.xml +0 -0
  102. {elementpath-5.0.0 → elementpath-5.0.1}/tests/run_all_tests.py +0 -0
  103. {elementpath-5.0.0 → elementpath-5.0.1}/tests/run_typing_tests.py +0 -0
  104. {elementpath-5.0.0 → elementpath-5.0.1}/tests/run_w3c_tests.py +0 -0
  105. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_collations.py +0 -0
  106. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_compare.py +0 -0
  107. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_datatypes.py +0 -0
  108. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_decoder.py +0 -0
  109. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_etree.py +0 -0
  110. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_exceptions.py +0 -0
  111. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_helpers.py +0 -0
  112. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_namespaces.py +0 -0
  113. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_package.py +0 -0
  114. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_pathnodes.py +0 -0
  115. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_regex.py +0 -0
  116. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_schema_context.py +0 -0
  117. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_selectors.py +0 -0
  118. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_sequence_types.py +0 -0
  119. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_serialization.py +0 -0
  120. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_tdop_parser.py +0 -0
  121. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_tree_builders.py +0 -0
  122. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_validators.py +0 -0
  123. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath1_parser.py +0 -0
  124. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath2_constructors.py +0 -0
  125. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath2_functions.py +0 -0
  126. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath2_parser.py +0 -0
  127. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath30.py +0 -0
  128. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath31.py +0 -0
  129. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath_context.py +0 -0
  130. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath_nodes.py +0 -0
  131. {elementpath-5.0.0 → elementpath-5.0.1}/tests/test_xpath_tokens.py +0 -0
  132. {elementpath-5.0.0 → elementpath-5.0.1}/tests/xpath_test_class.py +0 -0
  133. {elementpath-5.0.0 → elementpath-5.0.1}/tox.ini +0 -0
@@ -2,6 +2,10 @@
2
2
  CHANGELOG
3
3
  *********
4
4
 
5
+ `v5.0.1`_ (2025-05-11)
6
+ ======================
7
+ * Fix XDM type labeling with element and xsi:type substitutions (issue #89)
8
+
5
9
  `v5.0.0`_ (2025-04-27)
6
10
  ======================
7
11
  * Replace SafeXMLParser with SafeExpatParser
@@ -502,3 +506,4 @@ CHANGELOG
502
506
  .. _v4.7.0: https://github.com/sissaschool/elementpath/compare/v4.6.0...v4.7.0
503
507
  .. _v4.8.0: https://github.com/sissaschool/elementpath/compare/v4.7.0...v4.8.0
504
508
  .. _v5.0.0: https://github.com/sissaschool/elementpath/compare/v4.8.0...v5.0.0
509
+ .. _v5.0.1: https://github.com/sissaschool/elementpath/compare/v5.0.0...v5.0.1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: elementpath
3
- Version: 5.0.0
3
+ Version: 5.0.1
4
4
  Summary: XPath 1.0/2.0/3.0/3.1 parsers and selectors for ElementTree and lxml
5
5
  Author-email: Davide Brunato <brunato@sissa.it>
6
6
  License-Expression: MIT
@@ -31,7 +31,7 @@ author = 'Davide Brunato'
31
31
  # The short X.Y version
32
32
  version = '5.0'
33
33
  # The full version, including alpha/beta/rc tags
34
- release = '5.0.0'
34
+ release = '5.0.1'
35
35
 
36
36
  # -- General configuration ---------------------------------------------------
37
37
 
@@ -7,7 +7,7 @@
7
7
  #
8
8
  # @author Davide Brunato <brunato@sissa.it>
9
9
  #
10
- __version__ = '5.0.0'
10
+ __version__ = '5.0.1'
11
11
  __author__ = "Davide Brunato"
12
12
  __contact__ = "brunato@sissa.it"
13
13
  __copyright__ = "Copyright 2018-2025, SISSA"
@@ -11,15 +11,12 @@
11
11
  Define type hints protocols for XPath related objects.
12
12
  """
13
13
  from collections.abc import Hashable, Iterator, Iterable, ItemsView, Mapping, Sequence, Sized
14
- from typing import overload, Any, Optional, Protocol, Union, TypeVar, TYPE_CHECKING
14
+ from typing import overload, Any, Optional, Protocol, Union, TypeVar
15
15
  from xml.etree.ElementTree import Element, ElementTree
16
16
 
17
17
  from collections.abc import MutableMapping
18
18
  from elementpath.aliases import NamespacesType, NsmapType
19
19
 
20
- if TYPE_CHECKING:
21
- from elementpath.schema_proxy import AbstractSchemaProxy
22
-
23
20
  _T = TypeVar("_T")
24
21
  _AnyStr = Union[str, bytes]
25
22
 
@@ -146,6 +143,9 @@ class XsdComponentProtocol(XsdValidatorProtocol, Protocol):
146
143
  @property
147
144
  def parent(self) -> Optional['XsdComponentProtocol']: ...
148
145
 
146
+ @property
147
+ def ref(self) -> Optional['XsdComponentProtocol']: ...
148
+
149
149
 
150
150
  class XsdTypeProtocol(XsdComponentProtocol, Protocol):
151
151
 
@@ -260,17 +260,29 @@ class XsdTypeProtocol(XsdComponentProtocol, Protocol):
260
260
  """
261
261
  ...
262
262
 
263
+ @property
264
+ def model_group(self) -> Optional['XsdGroupProtocol']:
265
+ """
266
+ A model group if it's a complexType with mixed or element-only content, `None` otherwise.
267
+ """
268
+ ...
263
269
 
264
- class XsdAttributeProtocol(XsdComponentProtocol, Protocol):
265
270
 
266
- @property
267
- def type(self) -> Optional[XsdTypeProtocol]: ...
271
+ class XsdComplexTypeProtocol(XsdComponentProtocol, Protocol):
268
272
 
269
273
  @property
270
- def ref(self) -> Optional[Any]: ...
274
+ def attributes(self) -> 'XsdAttributeGroupProtocol': ...
275
+
276
+
277
+ class XsdGroupProtocol(XsdComponentProtocol, Protocol):
278
+
279
+ def iter_elements(self) -> Iterator['XsdElementProtocol']: ...
271
280
 
272
281
 
273
- XsdXPathNodeType = Union['XsdSchemaProtocol', 'XsdElementProtocol']
282
+ class XsdAttributeProtocol(XsdComponentProtocol, Protocol):
283
+
284
+ @property
285
+ def type(self) -> Optional[XsdTypeProtocol]: ...
274
286
 
275
287
 
276
288
  class XsdAttributeGroupProtocol(XsdComponentProtocol, Protocol):
@@ -296,62 +308,40 @@ class XsdAttributeGroupProtocol(XsdComponentProtocol, Protocol):
296
308
 
297
309
  def __hash__(self) -> int: ...
298
310
 
299
- @property
300
- def ref(self) -> Optional[Any]: ...
301
-
302
311
 
303
312
  class XsdElementProtocol(XsdComponentProtocol, ElementProtocol, Protocol):
304
313
 
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
314
  @property
313
315
  def name(self) -> Optional[str]: ...
314
316
 
315
317
  @property
316
318
  def type(self) -> Optional[XsdTypeProtocol]: ...
317
319
 
318
- @property
319
- def ref(self) -> Optional[Any]: ...
320
-
321
320
  @property
322
321
  def attrib(self) -> XsdAttributeGroupProtocol: ...
323
322
 
324
- @property
325
- def xpath_proxy(self) -> 'AbstractSchemaProxy': ...
326
-
323
+ def __iter__(self) -> Iterator['XsdElementProtocol']: ...
327
324
 
328
- GT = TypeVar("GT")
329
- XsdGlobalValue = Union[GT, tuple[ElementProtocol, Any]]
325
+ def find(self, path: str, namespaces: Optional[NamespacesType] = ...) \
326
+ -> Union['XsdElementProtocol', None]: ...
330
327
 
331
328
 
332
329
  class GlobalMapsProtocol(Protocol):
333
330
 
334
331
  @property
335
- def types(self) -> Mapping[str, XsdGlobalValue[XsdTypeProtocol]]: ...
332
+ def types(self) -> Mapping[str, XsdTypeProtocol]: ...
336
333
 
337
334
  @property
338
- def attributes(self) -> Mapping[str, XsdGlobalValue[XsdAttributeProtocol]]: ...
335
+ def attributes(self) -> Mapping[str, XsdAttributeProtocol]: ...
339
336
 
340
337
  @property
341
- def elements(self) -> Mapping[str, XsdGlobalValue[XsdElementProtocol]]: ...
338
+ def elements(self) -> Mapping[str, XsdElementProtocol]: ...
342
339
 
343
340
  @property
344
341
  def substitution_groups(self) -> Mapping[str, set[Any]]: ...
345
342
 
346
343
 
347
- class XsdSchemaProtocol(XsdValidatorProtocol, ElementProtocol, Protocol):
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]: ...
344
+ class XsdSchemaProtocol(XsdValidatorProtocol, Protocol):
355
345
 
356
346
  @property
357
347
  def validity(self) -> str: ...
@@ -365,10 +355,13 @@ class XsdSchemaProtocol(XsdValidatorProtocol, ElementProtocol, Protocol):
365
355
  @property
366
356
  def attrib(self) -> MutableMapping[Optional[str], 'XsdAttributeProtocol']: ...
367
357
 
368
- @property
369
- def xpath_proxy(self) -> 'AbstractSchemaProxy': ...
358
+ def __iter__(self) -> Iterator['XsdXPathNodeType']: ...
359
+
360
+ def find(self, path: str, namespaces: Optional[NamespacesType] = ...) \
361
+ -> Union['XsdSchemaProtocol', 'XsdElementProtocol', None]: ...
370
362
 
371
363
 
364
+ XsdXPathNodeType = Union[XsdSchemaProtocol, XsdElementProtocol]
372
365
  DocumentType = Union[ElementTree, DocumentProtocol]
373
366
  ElementType = Union[Element, ElementProtocol]
374
367
  SchemaElemType = Union[XsdSchemaProtocol, XsdElementProtocol]
@@ -386,4 +379,5 @@ __all__ = ['ElementProtocol', 'EtreeElementProtocol', 'LxmlAttribProtocol',
386
379
  'XsdValidatorProtocol', 'XsdComponentProtocol', 'XsdTypeProtocol',
387
380
  'XsdAttributeProtocol', 'XsdAttributeGroupProtocol', 'XsdElementProtocol',
388
381
  'GlobalMapsProtocol', 'XsdSchemaProtocol', 'DocumentType', 'ElementType',
389
- 'SchemaElemType', 'CommentType', 'ProcessingInstructionType', 'AttribType']
382
+ 'SchemaElemType', 'CommentType', 'ProcessingInstructionType',
383
+ 'AttribType', 'XsdXPathNodeType']
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env python
2
1
  #
3
2
  # Copyright (c), 2025, SISSA (International School for Advanced Studies).
4
3
  # All rights reserved.
@@ -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 TYPE_CHECKING, Any, Optional, Union
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', '_find', '_is_fully_valid')
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
- return self._find(path, namespaces)
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 path as XPath expression.
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
- return self._find(expanded_path)
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. A concrete implementation must
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
- xsd_type = self._schema.maps.types.get(qname)
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. A concrete implementation must
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 attribute or `None`.
151
+ :returns: an object that represents an XSD type or `None`.
162
152
  """
163
- xsd_attribute = self._schema.maps.attributes.get(qname)
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. A concrete implementation must
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 element or `None`.
160
+ :returns: an object that represents an XSD type or `None`.
176
161
  """
177
- xsd_element = self._schema.maps.elements.get(qname)
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. A concrete implementation must return a list containing
185
- substitution elements or `None` if the substitution group is not found. Moreover,
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
  """
@@ -61,7 +61,7 @@ def get_node_tree(root: RootArgType,
61
61
  elif fragment is False and \
62
62
  isinstance(root, ElementNode) and \
63
63
  is_etree_element_instance(root.obj):
64
- return root.get_document_node(replace=False)
64
+ return root.get_document_node()
65
65
 
66
66
  return root
67
67
 
@@ -186,7 +186,7 @@ def build_node_tree(root: ElementTreeRootType,
186
186
  elif fragment is False and \
187
187
  isinstance(root_node, ElementNode) and \
188
188
  is_etree_element_instance(root_node.elem):
189
- return root_node.get_document_node(replace=False)
189
+ return root_node.get_document_node()
190
190
  else:
191
191
  return root_node
192
192
  else:
@@ -350,8 +350,8 @@ def build_schema_node_tree(root: SchemaElemType,
350
350
  :param global_elements: a list for schema global elements, used for linking \
351
351
  the elements declared by reference.
352
352
  """
353
- parent: Any
354
- elem: Any
353
+ parent: SchemaElementNode
354
+ elem: XsdElementProtocol
355
355
  child: SchemaElementNode
356
356
  children: Iterator[Any]
357
357
 
@@ -381,7 +381,11 @@ def build_schema_node_tree(root: SchemaElemType,
381
381
  local_nodes = {root: root_node} # Irrelevant even if it's the schema
382
382
  ref_nodes: list[SchemaElementNode] = []
383
383
 
384
- children = iter(root)
384
+ if is_schema(root):
385
+ children = iter(e for e in root.maps.elements.values() if e.maps is root.maps)
386
+ else:
387
+ children = iter(root)
388
+
385
389
  iterators: list[Any] = []
386
390
  ancestors: list[Any] = []
387
391
  parent = root_node
@@ -412,7 +416,7 @@ def build_schema_node_tree(root: SchemaElemType,
412
416
  except IndexError:
413
417
  # connect references to proper nodes
414
418
  for element_node in ref_nodes:
415
- elem = element_node.elem
419
+ elem = cast(XsdElementProtocol, element_node.elem)
416
420
  ref = cast(XsdElementProtocol, elem.ref)
417
421
 
418
422
  other: Any
@@ -848,8 +848,7 @@ def select_schema_element_kind_test(self: XPathFunction, context: ContextType =
848
848
 
849
849
  if self.parser.schema is not None:
850
850
  for _ in context.iter_children_or_self():
851
- if self.parser.schema.get_element(qname) is None \
852
- and self.parser.schema.get_substitution_group(qname) is None:
851
+ if self.parser.schema.get_element(qname) is None:
853
852
  raise self.error('XPST0008', "element %r not found in schema" % element_name)
854
853
 
855
854
  if isinstance(context.item, ElementNode) and context.item.name == qname:
@@ -23,7 +23,7 @@ from elementpath.datatypes import AnyAtomicType, AtomicType, Timezone, Language
23
23
  from elementpath.etree import is_etree_element, is_etree_element_instance, is_etree_document
24
24
  from elementpath.xpath_nodes import ChildNodeType, XPathNode, AttributeNode, NamespaceNode, \
25
25
  CommentNode, ProcessingInstructionNode, ElementNode, DocumentNode, RootNodeType, \
26
- RootArgType, SchemaElementNode
26
+ RootArgType
27
27
  from elementpath.tree_builders import get_node_tree
28
28
 
29
29
  if TYPE_CHECKING:
@@ -144,9 +144,6 @@ class XPathContext:
144
144
  else:
145
145
  self.item = self.root
146
146
 
147
- if schema is not None:
148
- self.root.apply_schema(schema)
149
-
150
147
  elif item is not None:
151
148
  self.root = None
152
149
  self.item = self.get_context_item(item, self.namespaces, uri, fragment)
@@ -159,7 +156,7 @@ class XPathContext:
159
156
  isinstance(self.root, ElementNode) and \
160
157
  is_etree_element_instance(self.root.obj):
161
158
  # Creates a dummy document that will be not included in results
162
- self.document = self.root.get_document_node(replace=False, as_parent=False)
159
+ self.document = self.root.get_document_node(as_parent=False)
163
160
  else:
164
161
  self.document = None
165
162
 
@@ -180,7 +177,9 @@ class XPathContext:
180
177
  if v is not None else v for k, v in documents.items()
181
178
  }
182
179
 
183
- self.schema = schema
180
+ if schema is not None:
181
+ self.schema = schema
182
+
184
183
  self.variables = {}
185
184
  if variables is not None:
186
185
  for varname, value in variables.items():
@@ -243,11 +242,21 @@ class XPathContext:
243
242
  @schema.setter
244
243
  def schema(self, schema: Optional['AbstractSchemaProxy']) -> None:
245
244
  self._schema = schema
246
- if schema is not None:
245
+ if schema is None:
246
+ if self.root is not None:
247
+ self.root.clear_types()
248
+ elif isinstance(self.item, XPathNode):
249
+ self.item.clear_types()
250
+ elif hasattr(schema, 'is_assertion_based'):
247
251
  if self.root is not None:
252
+ self.root.clear_types()
248
253
  self.root.apply_schema(schema)
249
254
  elif isinstance(self.item, XPathNode):
255
+ self.item.clear_types()
250
256
  self.item.apply_schema(schema)
257
+ else:
258
+ msg = f"{schema!r} is not an instance of AbstractSchemaProxy"
259
+ raise ElementPathTypeError(msg)
251
260
 
252
261
  def get_root(self, node: Any) -> Optional[RootNodeType]:
253
262
  if isinstance(self.root, (DocumentNode, ElementNode)):
@@ -622,15 +631,6 @@ class XPathSchemaContext(XPathContext):
622
631
  """
623
632
  root: ElementNode
624
633
 
625
- def __init__(self, *args: Any, **kwargs: Any) -> None:
626
- super().__init__(*args, **kwargs)
627
- if self.schema is None:
628
- if isinstance(self.root, SchemaElementNode):
629
- try:
630
- self._schema = self.root.obj.xpath_proxy
631
- except AttributeError:
632
- pass
633
-
634
634
  @property
635
635
  def schema(self) -> Optional['AbstractSchemaProxy']:
636
636
  return self._schema