From 004fca18366974c34193176bd3a356f711330ca0 Mon Sep 17 00:00:00 2001 From: Davide Brunato Date: Mon, 11 Mar 2024 17:37:49 +0100 Subject: [PATCH] Add a protocol for XSD attribute groups - Bump minor release and update release info --- .github/workflows/test-elementpath.yml | 2 +- CHANGELOG.rst | 6 ++- doc/advanced.rst | 2 +- doc/conf.py | 4 +- elementpath/__init__.py | 6 +-- elementpath/compare.py | 10 ++++- elementpath/protocols.py | 51 ++++++++++++++++++++++---- elementpath/serialization.py | 3 +- publiccode.yml | 4 +- setup.py | 2 +- tox.ini | 4 +- 11 files changed, 68 insertions(+), 26 deletions(-) diff --git a/.github/workflows/test-elementpath.yml b/.github/workflows/test-elementpath.yml index 77324bf1..fe67c046 100644 --- a/.github/workflows/test-elementpath.yml +++ b/.github/workflows/test-elementpath.yml @@ -41,7 +41,7 @@ jobs: flake8 elementpath --max-line-length=100 --statistics - name: Lint with mypy run: | - pip install mypy==1.8.0 xmlschema lxml-stubs + pip install mypy==1.9.0 xmlschema lxml-stubs mypy --show-error-codes --strict elementpath - name: Test with unittest run: | diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 9b8d538b..59ea9a36 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,8 +2,10 @@ CHANGELOG ********* -`v4.3.1`_ (2024-03-xx) +`v4.4.0`_ (2024-03-11) ====================== +* Improve stand-alone XPath functions builder (issue #70) +* Update tokens and parsers __repr__ * Fix static typing protocols to work with etree and XSD elements `v4.3.0`_ (2024-02-17) @@ -460,4 +462,4 @@ CHANGELOG .. _v4.2.0: https://github.com/sissaschool/elementpath/compare/v4.1.5...v4.2.0 .. _v4.2.1: https://github.com/sissaschool/elementpath/compare/v4.2.0...v4.2.1 .. _v4.3.0: https://github.com/sissaschool/elementpath/compare/v4.2.1...v4.3.0 -.. _v4.3.1: https://github.com/sissaschool/elementpath/compare/v4.3.0...v4.3.1 +.. _v4.4.0: https://github.com/sissaschool/elementpath/compare/v4.3.0...v4.4.0 diff --git a/doc/advanced.rst b/doc/advanced.rst index aba263dd..d63c515f 100644 --- a/doc/advanced.rst +++ b/doc/advanced.rst @@ -23,7 +23,7 @@ having as result a tree of tokens: >>> isinstance(token, XPathToken) True >>> token - _SolidusOperator(...) + <_SolidusOperator object at 0x... >>> str(token) "'/' operator" >>> token.tree diff --git a/doc/conf.py b/doc/conf.py index 96ae08f2..675b1dfd 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -29,9 +29,9 @@ author = 'Davide Brunato' # The short X.Y version -version = '4.3' +version = '4.4' # The full version, including alpha/beta/rc tags -release = '4.3.1' +release = '4.4.0' # -- General configuration --------------------------------------------------- diff --git a/elementpath/__init__.py b/elementpath/__init__.py index 4214290d..674c69d1 100644 --- a/elementpath/__init__.py +++ b/elementpath/__init__.py @@ -7,7 +7,7 @@ # # @author Davide Brunato # -__version__ = '4.3.1' +__version__ = '4.4.0' __author__ = "Davide Brunato" __contact__ = "brunato@sissa.it" __copyright__ = "Copyright 2018-2024, SISSA" @@ -38,8 +38,6 @@ from .schema_proxy import AbstractSchemaProxy from .regex import RegexError, translate_pattern -TypedElement = ElementNode # for backward compatibility with xmlschema<=1.10.0 - __all__ = ['datatypes', 'protocols', 'etree', 'ElementPathError', 'MissingContextError', 'ElementPathKeyError', 'ElementPathZeroDivisionError', 'ElementPathNameError', 'ElementPathOverflowError', 'ElementPathRuntimeError', 'ElementPathSyntaxError', @@ -47,7 +45,7 @@ 'XPathContext', 'XPathSchemaContext', 'XPathNode', 'DocumentNode', 'ElementNode', 'AttributeNode', 'NamespaceNode', 'CommentNode', 'ProcessingInstructionNode', 'TextNode', 'LazyElementNode', - 'SchemaElementNode', 'TypedElement', 'get_node_tree', 'build_node_tree', + 'SchemaElementNode', 'get_node_tree', 'build_node_tree', 'build_lxml_node_tree', 'build_schema_node_tree', 'XPathToken', 'XPathFunction', 'XPath1Parser', 'XPath2Parser', 'select', 'iter_select', 'Selector', 'AbstractSchemaProxy', 'RegexError', 'translate_pattern'] diff --git a/elementpath/compare.py b/elementpath/compare.py index adeb92af..53b03fe1 100644 --- a/elementpath/compare.py +++ b/elementpath/compare.py @@ -39,8 +39,14 @@ def etree_deep_equal(e1: ElementProtocol, e2: ElementProtocol) -> bool: elif len(e1) != len(e2) or len(e1.attrib) != len(e2.attrib): return False - items1 = {(cm.strxfrm(k or ''), cm.strxfrm(v)) for k, v in e1.attrib.items()} - items2 = {(cm.strxfrm(k or ''), cm.strxfrm(v)) for k, v in e2.attrib.items()} + try: + items1 = {(cm.strxfrm(k or ''), cm.strxfrm(v)) # type: ignore[arg-type] + for k, v in e1.attrib.items()} + items2 = {(cm.strxfrm(k or ''), cm.strxfrm(v)) # type: ignore[arg-type] + for k, v in e2.attrib.items()} + except TypeError: + return False + if items1 != items2: return False return all(etree_deep_equal(c1, c2) for c1, c2 in zip(e1, e2)) diff --git a/elementpath/protocols.py b/elementpath/protocols.py index 8fc545ee..fc3feee0 100644 --- a/elementpath/protocols.py +++ b/elementpath/protocols.py @@ -10,7 +10,7 @@ """ Define type hints protocols for XPath related objects. """ -from typing import overload, Any, Dict, Iterator, Iterable, Optional, Sequence, \ +from typing import overload, Any, Dict, Iterator, Iterable, Optional, Sequence, ItemsView, \ Protocol, Sized, Hashable, Union, TypeVar, Mapping, Tuple, Set, MutableMapping _T = TypeVar("_T") @@ -32,7 +32,10 @@ def __len__(self) -> int: ... AttribType = Union[ - MutableMapping[str, Any], MutableMapping[Optional[str], Any], LxmlAttribProtocol + MutableMapping[str, Any], + MutableMapping[Optional[str], Any], + LxmlAttribProtocol, + 'XsdAttributeGroupProtocol' ] @@ -67,6 +70,13 @@ def attrib(self) -> AttribType: ... class EtreeElementProtocol(ElementProtocol, Protocol): """A protocol for xml.etree.ElementTree elements.""" + def __iter__(self) -> Iterator['EtreeElementProtocol']: ... + + def find( + self, path: str, namespaces: Optional[Dict[str, str]] = ... + ) -> Optional['EtreeElementProtocol']: ... + def iter(self, tag: Optional[str] = ...) -> Iterator['EtreeElementProtocol']: ... + @property def attrib(self) -> Dict[str, str]: ... @@ -106,7 +116,7 @@ def parse(self, source: Any, *args: Any, **kwargs: Any) -> LxmlElementProtocol: def iter(self, tag: Optional[str] = ...) -> Iterator[LxmlElementProtocol]: ... -class XsdValidatorProtocol(Protocol): +class XsdValidatorProtocol(Hashable, Protocol): def is_matching(self, name: Optional[str], default_namespace: Optional[str] = None) -> bool: ... @@ -216,6 +226,30 @@ def ref(self) -> Optional[Any]: ... XsdXPathNodeType = Union['XsdSchemaProtocol', 'XsdElementProtocol'] +class XsdAttributeGroupProtocol(XsdComponentProtocol, Protocol): + + @overload + def get(self, key: Optional[str]) -> Optional[XsdAttributeProtocol]: ... + + @overload + def get(self, key: Optional[str], default: _T) -> Union[XsdAttributeProtocol, _T]: ... + + def items(self) -> ItemsView[Optional[str], XsdAttributeProtocol]: ... + + def __contains__(self, key: Optional[str]) -> bool: ... + + def __getitem__(self, key: Optional[str]) -> XsdAttributeProtocol: ... + + def __iter__(self) -> Iterator[Optional[str]]: ... + + def __len__(self) -> int: ... + + def __hash__(self) -> int: ... + + @property + def ref(self) -> Optional[Any]: ... + + class XsdElementProtocol(XsdComponentProtocol, ElementProtocol, Protocol): def __iter__(self) -> Iterator['XsdElementProtocol']: ... @@ -235,7 +269,7 @@ def type(self) -> Optional[XsdTypeProtocol]: ... def ref(self) -> Optional[Any]: ... @property - def attrib(self) -> MutableMapping[Optional[str], XsdAttributeProtocol]: ... + def attrib(self) -> XsdAttributeGroupProtocol: ... GT = TypeVar("GT") @@ -273,7 +307,8 @@ def tag(self) -> str: ... def attrib(self) -> MutableMapping[Optional[str], 'XsdAttributeProtocol']: ... -__allx__ = ['ElementProtocol', 'EtreeElementProtocol', 'LxmlElementProtocol', - 'DocumentProtocol', 'LxmlDocumentProtocol', 'XsdValidatorProtocol', - 'XsdSchemaProtocol', 'XsdComponentProtocol', 'XsdTypeProtocol', - 'XsdElementProtocol', 'XsdAttributeProtocol', 'GlobalMapsProtocol'] +__all__ = ['ElementProtocol', 'EtreeElementProtocol', 'LxmlAttribProtocol', + 'LxmlElementProtocol', 'DocumentProtocol', 'LxmlDocumentProtocol', + 'XsdValidatorProtocol', 'XsdComponentProtocol', 'XsdTypeProtocol', + 'XsdAttributeProtocol', 'XsdAttributeGroupProtocol', + 'XsdElementProtocol', 'GlobalMapsProtocol', 'XsdSchemaProtocol',] diff --git a/elementpath/serialization.py b/elementpath/serialization.py index e273a329..947353eb 100644 --- a/elementpath/serialization.py +++ b/elementpath/serialization.py @@ -20,6 +20,7 @@ from .xpath_nodes import XPathNode, ElementNode, AttributeNode, DocumentNode, \ NamespaceNode, TextNode, CommentNode from .xpath_tokens import XPathToken, XPathMap, XPathArray +from .protocols import EtreeElementProtocol, LxmlElementProtocol # XSLT and XQuery Serialization parameters SERIALIZATION_PARAMS = '{%s}serialization-parameters' % XSLT_XQUERY_SERIALIZATION_NAMESPACE @@ -135,7 +136,7 @@ def get_serialization_params(params: Union[None, ElementNode, XPathMap] = None, kwargs[key] = value elif isinstance(params, ElementNode): - root = params.elem + root = cast(Union[EtreeElementProtocol, LxmlElementProtocol], params.elem) if root.tag != SERIALIZATION_PARAMS: msg = 'output:serialization-parameters tag expected' raise xpath_error('XPTY0004', msg, token) diff --git a/publiccode.yml b/publiccode.yml index b790f4a9..8b635913 100644 --- a/publiccode.yml +++ b/publiccode.yml @@ -6,8 +6,8 @@ publiccodeYmlVersion: '0.2' name: elementpath url: 'https://github.com/sissaschool/elementpath' landingURL: 'https://github.com/sissaschool/elementpath' -releaseDate: '2024-03-xx' -softwareVersion: v4.3.1 +releaseDate: '2024-03-11' +softwareVersion: v4.4.0 developmentStatus: stable platforms: - linux diff --git a/setup.py b/setup.py index 223652d2..1c04e628 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ setup( name='elementpath', - version='4.3.1', + version='4.4.0', packages=find_packages(include=['elementpath', 'elementpath.*']), package_data={ 'elementpath': ['py.typed'], diff --git a/tox.ini b/tox.ini index 8ee7f951..142eb412 100644 --- a/tox.ini +++ b/tox.ini @@ -46,7 +46,7 @@ commands = [testenv:mypy-py{38,39,310,311,312,py3}] deps = - mypy==1.8.0 + mypy==1.9.0 xmlschema>=3.0.1 lxml-stubs commands = @@ -92,7 +92,7 @@ allowlist_externals = tar [testenv:w3c-xsdtests] platform = (linux|darwin) set_env = - VERSION = 3.0.1 + VERSION = 3.0.2 COMMIT = 4293d6fb026af778aa7ad381c2a310354578cbe3 CHECKSUM = 3c7a44dbb59553d09ba96fee898255be78966960c22e9a7886c0b426a03255d7 change_dir = {env_tmp_dir}