Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-elementpath for openSUSE:Factory checked in at 2022-06-06 11:10:29 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-elementpath (Old) and /work/SRC/openSUSE:Factory/.python-elementpath.new.1548 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-elementpath" Mon Jun 6 11:10:29 2022 rev:13 rq:980772 version:2.5.3 Changes: -------- --- /work/SRC/openSUSE:Factory/python-elementpath/python-elementpath.changes 2022-03-29 18:14:10.519059500 +0200 +++ /work/SRC/openSUSE:Factory/.python-elementpath.new.1548/python-elementpath.changes 2022-06-06 11:10:37.003315803 +0200 @@ -1,0 +2,10 @@ +Sat Jun 4 14:00:38 UTC 2022 - Dirk M??ller <dmuel...@suse.com> + +- update to 2.5.3: + * Fix unary path step operator (issue #46) + * Fix sphinx warnings *'reference target not found'* (issue #45) + * Include PR #43 with fixes for `XPathContext.iter_siblings()` (issues #42 and #44) + * Fix for failed floats equality tests (issue #41) + * Static typing tested with mypy==0.950 + +------------------------------------------------------------------- Old: ---- v2.5.0.tar.gz New: ---- v2.5.3.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-elementpath.spec ++++++ --- /var/tmp/diff_new_pack.yaYnD6/_old 2022-06-06 11:10:37.851317031 +0200 +++ /var/tmp/diff_new_pack.yaYnD6/_new 2022-06-06 11:10:37.855317036 +0200 @@ -20,7 +20,7 @@ %define skip_python2 1 %define skip_python36 1 Name: python-elementpath -Version: 2.5.0 +Version: 2.5.3 Release: 0 Summary: XPath 1.0/20 parsers and selectors for ElementTree and lxml License: MIT ++++++ v2.5.0.tar.gz -> v2.5.3.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/.github/workflows/test-elementpath.yml new/elementpath-2.5.3/.github/workflows/test-elementpath.yml --- old/elementpath-2.5.0/.github/workflows/test-elementpath.yml 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/.github/workflows/test-elementpath.yml 2022-05-30 14:52:08.000000000 +0200 @@ -41,11 +41,16 @@ run: | pip install flake8 flake8 elementpath --max-line-length=100 --statistics - - name: Lint with mypy - if: ${{ matrix.python-version != '3.7' && matrix.python-version == 'pypy-3.8' }} + - name: Lint with mypy if Python version != 3.7 + if: ${{ matrix.python-version != '3.7' }} run: | - pip install mypy==0.931 - mypy --show-error-codes elementpath + pip install mypy==0.950 + mypy --show-error-codes --strict elementpath + - name: Lint with mypy if Python version == 3.7 + if: ${{ matrix.python-version == '3.7' }} + run: | + pip install mypy==0.950 + mypy --show-error-codes --no-warn-redundant-casts --no-warn-unused-ignores --no-warn-return-any --strict elementpath - name: Test with unittest run: | pip install lxml xmlschema>=1.9.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/CHANGELOG.rst new/elementpath-2.5.3/CHANGELOG.rst --- old/elementpath-2.5.0/CHANGELOG.rst 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/CHANGELOG.rst 2022-05-30 14:52:08.000000000 +0200 @@ -2,6 +2,20 @@ CHANGELOG ********* +`v2.5.3`_ (2022-05-30) +====================== +* Fix unary path step operator (issue #46) +* Fix sphinx warnings *'reference target not found'* (issue #45) + +`v2.5.2`_ (2022-05-17) +====================== +* Include PR #43 with fixes for `XPathContext.iter_siblings()` (issues #42 and #44) + +`v2.5.1`_ (2022-04-28) +====================== +* Fix for failed floats equality tests (issue #41) +* Static typing tested with mypy==0.950 + `v2.5.0`_ (2022-03-04) ====================== * Add XPath 3.0 support @@ -350,3 +364,6 @@ .. _v2.3.2: https://github.com/sissaschool/elementpath/compare/v2.3.1...v2.3.2 .. _v2.4.0: https://github.com/sissaschool/elementpath/compare/v2.3.3...v2.4.0 .. _v2.5.0: https://github.com/sissaschool/elementpath/compare/v2.4.0...v2.5.0 +.. _v2.5.1: https://github.com/sissaschool/elementpath/compare/v2.5.0...v2.5.1 +.. _v2.5.2: https://github.com/sissaschool/elementpath/compare/v2.5.1...v2.5.2 +.. _v2.5.3: https://github.com/sissaschool/elementpath/compare/v2.5.2...v2.5.3 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/README.rst new/elementpath-2.5.3/README.rst --- old/elementpath-2.5.0/README.rst 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/README.rst 2022-05-30 14:52:08.000000000 +0200 @@ -15,8 +15,6 @@ :target: https://travis-ci.org/sissaschool/elementpath .. image:: https://img.shields.io/pypi/dm/elementpath.svg :target: https://pypi.python.org/pypi/elementpath/ -.. image:: https://img.shields.io/badge/Maintained%3F-yes-green.svg - :target: https://GitHub.com/Naereen/StrapDown.js/graphs/commit-activity .. elementpath-introduction @@ -31,7 +29,7 @@ Installation and usage ====================== -You can install the package with *pip* in a Python 3.6+ environment:: +You can install the package with *pip* in a Python 3.7+ environment:: pip install elementpath diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/doc/conf.py new/elementpath-2.5.3/doc/conf.py --- old/elementpath-2.5.0/doc/conf.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/doc/conf.py 2022-05-30 14:52:08.000000000 +0200 @@ -25,13 +25,13 @@ # -- Project information ----------------------------------------------------- project = 'elementpath' -copyright = '2018-2021, SISSA (International School for Advanced Studies)' +copyright = '2018-2022, SISSA (International School for Advanced Studies)' author = 'Davide Brunato' # The short X.Y version version = '2.5' # The full version, including alpha/beta/rc tags -release = '2.5.0' +release = '2.5.3' # -- General configuration --------------------------------------------------- @@ -48,8 +48,13 @@ 'sphinx.ext.doctest', ] -# Option for autodoc: do not add module name as prefix to classes or functions. -add_module_names = False +# Options for autodoc +add_module_names = False # do not add module name as prefix to classes or functions. +autodoc_typehints = 'none' # do not add type annotations + +nitpick_ignore = [ + ('py:class', 'XMLSchemaProxy') +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -68,7 +73,8 @@ # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. -language = None +# language = None +language = 'en' # required by Sphinx v5.0.0 # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/doc/xpath_api.rst new/elementpath-2.5.3/doc/xpath_api.rst --- old/elementpath-2.5.0/doc/xpath_api.rst 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/doc/xpath_api.rst 2022-05-30 14:52:08.000000000 +0200 @@ -90,7 +90,7 @@ The XPath 2.0 parser can be interfaced with an XML Schema processor through a schema proxy. An :class:`XMLSchemaProxy` class is defined for interfacing schemas created with the *xmlschema* package. -This class is based on an abstract class :class:`AbstractSchemaProxy`, that can be used for +This class is based on an abstract class :class:`elementpath.AbstractSchemaProxy`, that can be used for implementing concrete interfaces to other types of XML Schema processors. .. autoclass:: elementpath.AbstractSchemaProxy @@ -110,7 +110,10 @@ XPath nodes =========== -XPath nodes are processed using a set of classes derived from :class:`XPathNode`: +XPath nodes are processed using a set of classes derived from +:class:`elementpath.XPathNode`: + +.. autoclass:: elementpath.XPathNode .. autoclass:: elementpath.AttributeNode .. autoclass:: elementpath.TextNode @@ -133,7 +136,7 @@ .. autoexception:: elementpath.RegexError There are also other exceptions, multiple derived from the base exception -:class:`ElementPathError` and Python built-in exceptions: +:class:`elementpath.ElementPathError` and Python built-in exceptions: .. autoexception:: elementpath.ElementPathKeyError .. autoexception:: elementpath.ElementPathLocaleError diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/__init__.py new/elementpath-2.5.3/elementpath/__init__.py --- old/elementpath-2.5.0/elementpath/__init__.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/__init__.py 2022-05-30 14:52:08.000000000 +0200 @@ -1,5 +1,5 @@ # -# Copyright (c), 2018-2021, SISSA (International School for Advanced Studies). +# Copyright (c), 2018-2022, SISSA (International School for Advanced Studies). # All rights reserved. # This file is distributed under the terms of the MIT License. # See the file 'LICENSE' in the root directory of the present @@ -7,7 +7,7 @@ # # @author Davide Brunato <brun...@sissa.it> # -__version__ = '2.5.0' +__version__ = '2.5.3' __author__ = "Davide Brunato" __contact__ = "brun...@sissa.it" __copyright__ = "Copyright 2018-2022, SISSA" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/regex/unicode_subsets.py new/elementpath-2.5.3/elementpath/regex/unicode_subsets.py --- old/elementpath-2.5.0/elementpath/regex/unicode_subsets.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/regex/unicode_subsets.py 2022-05-30 14:52:08.000000000 +0200 @@ -326,7 +326,7 @@ else: return self._codepoints == other - def __ior__(self, other: object) -> 'UnicodeSubset': # type: ignore[override] + def __ior__(self, other: object) -> 'UnicodeSubset': if not isinstance(other, Iterable): return NotImplemented elif isinstance(other, UnicodeSubset): @@ -376,7 +376,7 @@ obj = self.copy() return obj.__iand__(other) - def __ixor__(self, other: object) -> 'UnicodeSubset': # type: ignore[override] + def __ixor__(self, other: object) -> 'UnicodeSubset': if other is self: self.clear() return self diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/tdop.py new/elementpath-2.5.3/elementpath/tdop.py --- old/elementpath-2.5.0/elementpath/tdop.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/tdop.py 2022-05-30 14:52:08.000000000 +0200 @@ -357,7 +357,7 @@ class ParserMeta(ABCMeta): - token_base_class: Type[Token[Any]] + token_base_class: Type[Any] literals_pattern: Pattern[str] name_pattern: Pattern[str] tokenizer: Optional[Pattern[str]] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/xpath1/_xpath1_operators.py new/elementpath-2.5.3/elementpath/xpath1/_xpath1_operators.py --- old/elementpath-2.5.0/elementpath/xpath1/_xpath1_operators.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/xpath1/_xpath1_operators.py 2022-05-30 14:52:08.000000000 +0200 @@ -675,10 +675,11 @@ if is_document_node(context.root): yield context.root elif len(self) == 1: - if is_document_node(context.root) or context.item is context.root: - if not isinstance(context, XPathSchemaContext): - context.item = None - yield from self[0].select(context) + if not isinstance(context, XPathSchemaContext): + context.item = None + else: + context.item = context.root + yield from self[0].select(context) else: items = set() for _ in context.inner_focus_select(self[0]): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/xpath1/xpath1_parser.py new/elementpath-2.5.3/elementpath/xpath1/xpath1_parser.py --- old/elementpath-2.5.0/elementpath/xpath1/xpath1_parser.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/xpath1/xpath1_parser.py 2022-05-30 14:52:08.000000000 +0200 @@ -20,7 +20,7 @@ from ..protocols import XsdTypeProtocol from ..datatypes import AnyAtomicType, NumericProxy, UntypedAtomic, QName, \ xsd10_atomic_types, xsd11_atomic_types, ATOMIC_VALUES, AtomicValueType -from ..tdop import Parser +from ..tdop import Token, Parser from ..namespaces import NamespacesType, XML_NAMESPACE, XSD_NAMESPACE, XSD_ERROR, \ XPATH_FUNCTIONS_NAMESPACE, XPATH_MATH_FUNCTIONS_NAMESPACE, XSD_ANY_SIMPLE_TYPE, \ XSD_ANY_ATOMIC_TYPE, XSD_UNTYPED_ATOMIC, get_namespace, get_expanded_name, \ @@ -50,7 +50,7 @@ version = '1.0' """The XPath version string.""" - token_base_class = XPathToken + token_base_class: Type[Token[Any]] = XPathToken literals_pattern = re.compile( r"""'(?:[^']|'')*'|"(?:[^"]|"")*"|(?:\d+|\.\d+)(?:\.\d*)?(?:[Ee][+-]?\d+)?""" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/xpath30/xpath30_helpers.py new/elementpath-2.5.3/elementpath/xpath30/xpath30_helpers.py --- old/elementpath-2.5.0/elementpath/xpath30/xpath30_helpers.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/xpath30/xpath30_helpers.py 2022-05-30 14:52:08.000000000 +0200 @@ -453,6 +453,9 @@ else: zero_cp, zero_ch = ord('0'), '0' + min_width: int + max_width: Optional[int] + digits = sum(c.isdigit() for c in presentation) opt_digits = presentation.count('#') if not width or width == '*': diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/xpath30/xpath30_parser.py new/elementpath-2.5.3/elementpath/xpath30/xpath30_parser.py --- old/elementpath-2.5.0/elementpath/xpath30/xpath30_parser.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/xpath30/xpath30_parser.py 2022-05-30 14:52:08.000000000 +0200 @@ -30,9 +30,9 @@ arguments, but the *strict* option is ignored because XPath 3.0+ has braced URI literals and the expanded name syntax is not compatible. - :param args: the same positional arguments of class :class:`XPath2Parser`. + :param args: the same positional arguments of class :class:`elementpath.XPath2Parser`. :param decimal_formats: a mapping with statically known decimal formats. - :param kwargs: the same keyword arguments of class :class:`XPath2Parser`. + :param kwargs: the same keyword arguments of class :class:`elementpath.XPath2Parser`. """ version = '3.0' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/elementpath/xpath_context.py new/elementpath-2.5.3/elementpath/xpath_context.py --- old/elementpath-2.5.0/elementpath/xpath_context.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/elementpath/xpath_context.py 2022-05-30 14:52:08.000000000 +0200 @@ -12,7 +12,7 @@ from itertools import chain from types import ModuleType from typing import TYPE_CHECKING, cast, Dict, Any, List, Iterator, \ - Optional, Sequence, Union, Callable, MutableMapping, Set, Tuple + Optional, Sequence, Union, Callable, MutableMapping, Set from .exceptions import ElementPathTypeError, ElementPathValueError from .namespaces import XML_NAMESPACE @@ -539,14 +539,20 @@ :param axis: the context axis, default is 'following-sibling'. """ + item: Union[TextNode, ElementProtocol] + if isinstance(self.item, TypedElement): item = self.item.elem + parent = self.get_parent(item) + elif isinstance(self.item, TextNode): + item = self.item + parent = item.parent elif not is_etree_element(self.item) or callable(getattr(self.item, 'tag')): return else: item = cast(ElementNode, self.item) + parent = self.get_parent(item) - parent = self.get_parent(item) if parent is None: return @@ -554,6 +560,15 @@ self.axis = axis or 'following-sibling' if axis == 'preceding-sibling': + if is_element_node(parent): + elem = cast(ElementNode, parent) + if elem.text is not None: + self.item = TextNode(elem.text, elem) + if self.item == item: + self.item, self.axis = status + return + yield self.item + for child in parent: # pragma: no cover if child is item: break @@ -561,9 +576,16 @@ yield child if child.tail is not None: self.item = TextNode(child.tail, child, True) + if self.item == item: + break yield self.item else: follows = False + if is_element_node(parent): + elem = cast(ElementNode, parent) + if elem.text is not None and item == TextNode(elem.text, elem): + follows = True + for child in parent: if follows: self.item = child @@ -573,6 +595,11 @@ yield self.item elif child is item: follows = True + if child.tail is not None: + self.item = TextNode(child.tail, child, True) + yield self.item + elif child.tail is not None and item == TextNode(child.tail, child, True): + follows = True self.item, self.axis = status @@ -586,7 +613,7 @@ In this case set the context size at start and change both position and \ item at each iteration. For default only context item is changed. """ - descendants: Union[Iterator[Union[XPathNodeType, None]], Tuple[XPathNode]] + descendants: Any with_self = axis != 'descendant' if self.item is None: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/mypy.ini new/elementpath-2.5.3/mypy.ini --- old/elementpath-2.5.0/mypy.ini 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/mypy.ini 2022-05-30 14:52:08.000000000 +0200 @@ -1,3 +1,2 @@ [mypy] show_error_codes = True -warn_unused_ignores = True \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/publiccode.yml new/elementpath-2.5.3/publiccode.yml --- old/elementpath-2.5.0/publiccode.yml 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/publiccode.yml 2022-05-30 14:52:08.000000000 +0200 @@ -6,8 +6,8 @@ name: elementpath url: 'https://github.com/sissaschool/elementpath' landingURL: 'https://github.com/sissaschool/elementpath' -releaseDate: '2022-03-04' -softwareVersion: v2.5.0 +releaseDate: '2022-05-30' +softwareVersion: v2.5.3 developmentStatus: stable platforms: - linux @@ -43,10 +43,10 @@ apiDocumentation: 'https://elementpath.readthedocs.io/en/latest/xpath_api.html' documentation: 'https://elementpath.readthedocs.io/en/latest/' shortDescription: >- - Python library that provides XPath 1.0/2.0 parsers and selectors for + Python library that provides XPath 1.0/2.0/3.0 parsers and selectors for ElementTree and lxml longDescription: | - This is a library for Python 3.6+ that provides XPath 1.0 and 2.0 + This is a library for Python 3.7+ that provides XPath 1.0/2.0/3.0 selectors for Python's ElementTree XML data structures, both for the standard **ElementTree** library and for the **lxml** library. For lxml this package can be useful for providing XPath 2.0 selectors, because lxml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/requirements-dev.txt new/elementpath-2.5.3/requirements-dev.txt --- old/elementpath-2.5.0/requirements-dev.txt 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/requirements-dev.txt 2022-05-30 14:52:08.000000000 +0200 @@ -7,5 +7,5 @@ Sphinx memory-profiler flake8 -mypy==0.931 +mypy==0.950 -e . diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/setup.py new/elementpath-2.5.3/setup.py --- old/elementpath-2.5.0/setup.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/setup.py 2022-05-30 14:52:08.000000000 +0200 @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (c), 2018-2021, SISSA (International School for Advanced Studies). +# Copyright (c), 2018-2022, SISSA (International School for Advanced Studies). # All rights reserved. # This file is distributed under the terms of the MIT License. # See the file 'LICENSE' in the root directory of the present @@ -15,7 +15,7 @@ setup( name='elementpath', - version='2.5.0', + version='2.5.3', packages=find_packages(include=['elementpath', 'elementpath.*']), include_package_data=True, author='Davide Brunato', @@ -24,12 +24,12 @@ keywords=['XPath', 'XPath2', 'XPath3', 'Pratt-parser', 'ElementTree', 'lxml'], license='MIT', license_file='LICENSE', - description='XPath 1.0/2.0 parsers and selectors for ElementTree and lxml', + description='XPath 1.0/2.0/3.0 parsers and selectors for ElementTree and lxml', long_description=long_description, python_requires='>=3.7', extras_require={ - 'dev': ['tox', 'coverage', 'lxml', 'xmlschema>=1.8.0', - 'Sphinx', 'memory-profiler', 'flake8', 'mypy==0.910'] + 'dev': ['tox', 'coverage', 'lxml', 'xmlschema>=1.9.0', + 'Sphinx', 'memory-profiler', 'flake8', 'mypy==0.950'] }, classifiers=[ 'Development Status :: 5 - Production/Stable', diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/tests/test_selectors.py new/elementpath-2.5.3/tests/test_selectors.py --- old/elementpath-2.5.0/tests/test_selectors.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/tests/test_selectors.py 2022-05-30 14:52:08.000000000 +0200 @@ -49,6 +49,17 @@ root = ElementTree.XML('<FullPath>High Temp</FullPath>') self.assertListEqual(selector.select(root), [root]) + def test_issue_042(self): + selector1 = Selector('text()') + selector2 = Selector('sup[last()]/preceding-sibling::text()') + root = ElementTree.XML('<root>a<sup>1</sup>b<sup>2</sup>c<sup>3</sup></root>') + self.assertListEqual(selector1.select(root), selector2.select(root)) + + selector2 = Selector('sup[1]/following-sibling::text()') + root = ElementTree.XML('<root><sup>1</sup>b<sup>2</sup>c<sup>3</sup>d</root>') + self.assertListEqual(selector1.select(root), selector2.select(root)) + + if __name__ == '__main__': unittest.main() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/tests/test_xpath1_parser.py new/elementpath-2.5.3/tests/test_xpath1_parser.py --- old/elementpath-2.5.0/tests/test_xpath1_parser.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/tests/test_xpath1_parser.py 2022-05-30 14:52:08.000000000 +0200 @@ -451,7 +451,9 @@ context = XPathContext(root) context.item = None - self.check_value("/self::node()", expected=[], context=context) + # lxml differs: doesn't consider the document position even if select from an ElementTree + self.check_value("/self::node()", expected=[root], context=context) + context.item = 1 self.check_value("self::node()", expected=[], context=context) @@ -1254,7 +1256,7 @@ self.check_value('/A', [root], context=context) context = XPathContext(root, item=root[0][0]) - self.check_value('/A', [], context=context) + self.check_value('/A', [root], context=context) def test_path_step_operator_with_duplicates(self): root = self.etree.XML('<A>10<B a="2">11</B>10<B a="2"/>10<B>11</B></A>') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/tests/test_xpath30.py new/elementpath-2.5.3/tests/test_xpath30.py --- old/elementpath-2.5.0/tests/test_xpath30.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/tests/test_xpath30.py 2022-05-30 14:52:08.000000000 +0200 @@ -111,7 +111,7 @@ self.assertIn('XQST0046', str(ctx.exception)) token = self.parser.parse('Q{http://www.w3.org/2005/xpath-functions/math}pi()') - self.assertEqual(token.evaluate(), math.pi) + self.assertAlmostEqual(token.evaluate(), math.pi) # '{' is unusable for non-standard braced URI literals # because is used for inline functions body @@ -129,33 +129,33 @@ def test_exp_math_function(self): token = self.parser.parse('math:exp(())') self.assertIsNone(token.evaluate()) - self.assertEqual(self.parser.parse('math:exp(0)').evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:exp(1)').evaluate(), 2.718281828459045) - self.assertEqual(self.parser.parse('math:exp(2)').evaluate(), 7.38905609893065) - self.assertEqual(self.parser.parse('math:exp(-1)').evaluate(), 0.36787944117144233) - self.assertEqual(self.parser.parse('math:exp(math:pi())').evaluate(), 23.140692632779267) + self.assertAlmostEqual(self.parser.parse('math:exp(0)').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:exp(1)').evaluate(), 2.718281828459045) + self.assertAlmostEqual(self.parser.parse('math:exp(2)').evaluate(), 7.38905609893065) + self.assertAlmostEqual(self.parser.parse('math:exp(-1)').evaluate(), 0.36787944117144233) + self.assertAlmostEqual(self.parser.parse('math:exp(math:pi())').evaluate(), 23.140692632779267) self.assertTrue(math.isnan(self.parser.parse('math:exp(xs:double("NaN"))').evaluate())) self.assertEqual(self.parser.parse("math:exp(xs:double('INF'))").evaluate(), float('inf')) - self.assertEqual(self.parser.parse("math:exp(xs:double('-INF'))").evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse("math:exp(xs:double('-INF'))").evaluate(), 0.0) def test_exp10_math_function(self): token = self.parser.parse('math:exp10(())') self.assertIsNone(token.evaluate()) - self.assertEqual(self.parser.parse('math:exp10(0)').evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:exp10(1)').evaluate(), 10) - self.assertEqual(self.parser.parse('math:exp10(0.5)').evaluate(), 3.1622776601683795) - self.assertEqual(self.parser.parse('math:exp10(-1)').evaluate(), 0.1) + self.assertAlmostEqual(self.parser.parse('math:exp10(0)').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:exp10(1)').evaluate(), 10) + self.assertAlmostEqual(self.parser.parse('math:exp10(0.5)').evaluate(), 3.1622776601683795) + self.assertAlmostEqual(self.parser.parse('math:exp10(-1)').evaluate(), 0.1) self.assertTrue(math.isnan(self.parser.parse('math:exp10(xs:double("NaN"))').evaluate())) self.assertEqual(self.parser.parse("math:exp10(xs:double('INF'))").evaluate(), float('inf')) - self.assertEqual(self.parser.parse("math:exp10(xs:double('-INF'))").evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse("math:exp10(xs:double('-INF'))").evaluate(), 0.0) def test_log_math_function(self): token = self.parser.parse('math:log(())') self.assertIsNone(token.evaluate()) self.assertEqual(self.parser.parse('math:log(0)').evaluate(), float('-inf')) - self.assertEqual(self.parser.parse('math:log(math:exp(1))').evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:log(1.0e-3)').evaluate(), -6.907755278982137) - self.assertEqual(self.parser.parse('math:log(2)').evaluate(), 0.6931471805599453) + self.assertAlmostEqual(self.parser.parse('math:log(math:exp(1))').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:log(1.0e-3)').evaluate(), -6.907755278982137) + self.assertAlmostEqual(self.parser.parse('math:log(2)').evaluate(), 0.6931471805599453) self.assertTrue(math.isnan(self.parser.parse('math:log(-1)').evaluate())) self.assertTrue(math.isnan(self.parser.parse('math:log(xs:double("NaN"))').evaluate())) self.assertEqual(self.parser.parse("math:log(xs:double('INF'))").evaluate(), float('inf')) @@ -165,9 +165,9 @@ token = self.parser.parse('math:log10(())') self.assertIsNone(token.evaluate()) self.assertEqual(self.parser.parse('math:log10(0)').evaluate(), float('-inf')) - self.assertEqual(self.parser.parse('math:log10(1.0e3)').evaluate(), 3.0) - self.assertEqual(self.parser.parse('math:log10(1.0e-3)').evaluate(), -3.0) - self.assertEqual(self.parser.parse('math:log10(2)').evaluate(), 0.3010299956639812) + self.assertAlmostEqual(self.parser.parse('math:log10(1.0e3)').evaluate(), 3.0) + self.assertAlmostEqual(self.parser.parse('math:log10(1.0e-3)').evaluate(), -3.0) + self.assertAlmostEqual(self.parser.parse('math:log10(2)').evaluate(), 0.3010299956639812) self.assertTrue(math.isnan(self.parser.parse('math:log10(-1)').evaluate())) self.assertTrue(math.isnan(self.parser.parse('math:log10(xs:double("NaN"))').evaluate())) self.assertEqual(self.parser.parse("math:log10(xs:double('INF'))").evaluate(), float('inf')) @@ -175,49 +175,49 @@ def test_pow_math_function(self): self.assertIsNone(self.parser.parse('math:pow((), 93.7)').evaluate()) - self.assertEqual(self.parser.parse('math:pow(2, 3)').evaluate(), 8.0) - self.assertEqual(self.parser.parse('math:pow(-2, 3)').evaluate(), -8.0) - self.assertEqual(self.parser.parse('math:pow(2, -3)').evaluate(), 0.125) - self.assertEqual(self.parser.parse('math:pow(-2, -3)').evaluate(), -0.125) - self.assertEqual(self.parser.parse('math:pow(2, 0)').evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:pow(0, 0)').evaluate(), 1.0) - self.assertEqual(self.parser.parse("math:pow(xs:double('INF'), 0)").evaluate(), 1.0) - self.assertEqual(self.parser.parse("math:pow(xs:double('NaN'), 0)").evaluate(), 1.0) - self.assertEqual(self.parser.parse("math:pow(-math:pi(), 0)").evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:pow(0e0, 3)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:pow(0e0, 4)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:pow(-0e0, 3)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:pow(0, 3)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:pow(2, 3)').evaluate(), 8.0) + self.assertAlmostEqual(self.parser.parse('math:pow(-2, 3)').evaluate(), -8.0) + self.assertAlmostEqual(self.parser.parse('math:pow(2, -3)').evaluate(), 0.125) + self.assertAlmostEqual(self.parser.parse('math:pow(-2, -3)').evaluate(), -0.125) + self.assertAlmostEqual(self.parser.parse('math:pow(2, 0)').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:pow(0, 0)').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse("math:pow(xs:double('INF'), 0)").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse("math:pow(xs:double('NaN'), 0)").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse("math:pow(-math:pi(), 0)").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:pow(0e0, 3)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:pow(0e0, 4)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:pow(-0e0, 3)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:pow(0, 3)').evaluate(), 0.0) self.assertEqual(self.parser.parse('math:pow(0e0, -3)').evaluate(), float('inf')) self.assertEqual(self.parser.parse('math:pow(0e0, -4)').evaluate(), float('inf')) # self.assertEqual(self.parser.parse('math:pow(-0e0, -3)').evaluate(), float('-inf')) self.assertEqual(self.parser.parse('math:pow(0, -4)').evaluate(), float('inf')) - self.assertEqual(self.parser.parse('math:pow(16, 0.5e0)').evaluate(), 4.0) - self.assertEqual(self.parser.parse('math:pow(16, 0.25e0)').evaluate(), 2.0) + self.assertAlmostEqual(self.parser.parse('math:pow(16, 0.5e0)').evaluate(), 4.0) + self.assertAlmostEqual(self.parser.parse('math:pow(16, 0.25e0)').evaluate(), 2.0) self.assertEqual(self.parser.parse('math:pow(0e0, -3.0e0)').evaluate(), float('inf')) # self.assertEqual(self.parser.parse('math:pow(-0e0, -3.0e0)').evaluate(), float('-inf')) self.assertEqual(self.parser.parse('math:pow(0e0, -3.1e0)').evaluate(), float('inf')) self.assertEqual(self.parser.parse('math:pow(-0e0, -3.1e0)').evaluate(), float('inf')) - self.assertEqual(self.parser.parse('math:pow(0e0, 3.0e0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:pow(-0e0, 3.0e0)').evaluate(), -0.0) - self.assertEqual(self.parser.parse('math:pow(0e0, 3.1e0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:pow(-0e0, 3.1e0)').evaluate(), -0.0) - - self.assertEqual(self.parser.parse("math:pow(-1, xs:double('INF'))").evaluate(), 1.0) - self.assertEqual(self.parser.parse("math:pow(-1, xs:double('-INF'))").evaluate(), 1.0) - self.assertEqual(self.parser.parse("math:pow(1, xs:double('INF'))").evaluate(), 1.0) - self.assertEqual(self.parser.parse("math:pow(1, xs:double('-INF'))").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:pow(0e0, 3.0e0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:pow(-0e0, 3.0e0)').evaluate(), -0.0) + self.assertAlmostEqual(self.parser.parse('math:pow(0e0, 3.1e0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:pow(-0e0, 3.1e0)').evaluate(), -0.0) + + self.assertAlmostEqual(self.parser.parse("math:pow(-1, xs:double('INF'))").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse("math:pow(-1, xs:double('-INF'))").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse("math:pow(1, xs:double('INF'))").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse("math:pow(1, xs:double('-INF'))").evaluate(), 1.0) - self.assertEqual(self.parser.parse("math:pow(1, xs:double('NaN'))").evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:pow(-2.5e0, 2.0e0)').evaluate(), 6.25) + self.assertAlmostEqual(self.parser.parse("math:pow(1, xs:double('NaN'))").evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:pow(-2.5e0, 2.0e0)').evaluate(), 6.25) self.assertTrue(math.isnan(self.parser.parse('math:pow(-2.5e0, 2.00000001e0)').evaluate())) def test_sqrt_math_function(self): self.assertIsNone(self.parser.parse('math:sqrt(())').evaluate()) - self.assertEqual(self.parser.parse('math:sqrt(0.0e0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:sqrt(-0.0e0)').evaluate(), -0.0) - self.assertEqual(self.parser.parse('math:sqrt(1.0e6)').evaluate(), 1.0e3) - self.assertEqual(self.parser.parse('math:sqrt(2.0e0)').evaluate(), 1.4142135623730951) + self.assertAlmostEqual(self.parser.parse('math:sqrt(0.0e0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:sqrt(-0.0e0)').evaluate(), -0.0) + self.assertAlmostEqual(self.parser.parse('math:sqrt(1.0e6)').evaluate(), 1.0e3) + self.assertAlmostEqual(self.parser.parse('math:sqrt(2.0e0)').evaluate(), 1.4142135623730951) self.assertTrue(math.isnan(self.parser.parse('math:sqrt(-2.0e0)').evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:sqrt(xs:double('NaN'))").evaluate())) self.assertEqual(self.parser.parse("math:sqrt(xs:double('INF'))").evaluate(), float('inf')) @@ -225,69 +225,58 @@ def test_sin_math_function(self): self.assertIsNone(self.parser.parse('math:sin(())').evaluate()) - self.assertEqual(self.parser.parse('math:sin(0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:sin(-0.0e0)').evaluate(), -0.0) - self.assertEqual(self.parser.parse('math:sin(math:pi() div 2)').evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:sin(-math:pi() div 2)').evaluate(), -1.0) - self.assertTrue( - math.isclose(self.parser.parse('math:sin(math:pi())').evaluate(), 0.0, abs_tol=1e-14) - ) + self.assertAlmostEqual(self.parser.parse('math:sin(0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:sin(-0.0e0)').evaluate(), -0.0) + self.assertAlmostEqual(self.parser.parse('math:sin(math:pi() div 2)').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:sin(-math:pi() div 2)').evaluate(), -1.0) + self.assertAlmostEqual(self.parser.parse('math:sin(math:pi())').evaluate(), 0.0, places=13) self.assertTrue(math.isnan(self.parser.parse("math:sin(xs:double('NaN'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:sin(xs:double('INF'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:sin(xs:double('-INF'))").evaluate())) def test_cos_math_function(self): self.assertIsNone(self.parser.parse('math:cos(())').evaluate()) - self.assertEqual(self.parser.parse('math:cos(0)').evaluate(), 1.0) - self.assertEqual(self.parser.parse('math:cos(-0.0e0)').evaluate(), 1.0) - self.assertTrue(math.isclose( - self.parser.parse('math:cos(math:pi() div 2)').evaluate(), 0.0, abs_tol=1e-14 - )) - self.assertTrue(math.isclose( - self.parser.parse('math:cos(-math:pi() div 2)').evaluate(), 0.0, abs_tol=1e-14 - )) - self.assertEqual(self.parser.parse('math:cos(math:pi())').evaluate(), -1.0) + self.assertAlmostEqual(self.parser.parse('math:cos(0)').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:cos(-0.0e0)').evaluate(), 1.0) + self.assertAlmostEqual(self.parser.parse('math:cos(math:pi() div 2)').evaluate(), 0.0, places=13) + self.assertAlmostEqual(self.parser.parse('math:cos(-math:pi() div 2)').evaluate(), 0.0, places=13) + self.assertAlmostEqual(self.parser.parse('math:cos(math:pi())').evaluate(), -1.0) self.assertTrue(math.isnan(self.parser.parse("math:cos(xs:double('NaN'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:cos(xs:double('INF'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:cos(xs:double('-INF'))").evaluate())) def test_tan_math_function(self): self.assertIsNone(self.parser.parse('math:tan(())').evaluate()) - self.assertEqual(self.parser.parse('math:tan(0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:tan(-0.0e0)').evaluate(), -0.0) - self.assertTrue(math.isclose( - self.parser.parse('math:tan(math:pi() div 4)').evaluate(), 1.0, abs_tol=1e-14 - )) - self.assertTrue(math.isclose( - self.parser.parse('math:tan(-math:pi() div 4)').evaluate(), -1.0, abs_tol=1e-14 - )) - self.assertTrue(math.isclose( - self.parser.parse('math:tan(math:pi() div 2)').evaluate(), - 1.633123935319537E16, rel_tol=1e-14 - )) - self.assertTrue(math.isclose( + self.assertAlmostEqual(self.parser.parse('math:tan(0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:tan(-0.0e0)').evaluate(), -0.0) + self.assertAlmostEqual(self.parser.parse('math:tan(math:pi() div 4)').evaluate(), 1.0, places=13) + self.assertAlmostEqual(self.parser.parse('math:tan(-math:pi() div 4)').evaluate(), -1.0, places=13) + self.assertAlmostEqual( + self.parser.parse('math:tan(math:pi() div 2)').evaluate(), 1.633123935319537E16, places=13 + ) + self.assertAlmostEqual( self.parser.parse('math:tan(-math:pi() div 2)').evaluate(), - -1.633123935319537E16, rel_tol=1e-14 - )) - self.assertTrue(math.isclose( - self.parser.parse('math:tan(math:pi())').evaluate(), 0.0, abs_tol=1e-14 - )) + -1.633123935319537E16, places=13 + ) + self.assertAlmostEqual( + self.parser.parse('math:tan(math:pi())').evaluate(), 0.0, places=13 + ) self.assertTrue(math.isnan(self.parser.parse("math:tan(xs:double('NaN'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:tan(xs:double('INF'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:tan(xs:double('-INF'))").evaluate())) def test_asin_math_function(self): self.assertIsNone(self.parser.parse('math:asin(())').evaluate()) - self.assertEqual(self.parser.parse('math:asin(0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:asin(-0.0e0)').evaluate(), -0.0) - self.assertTrue(math.isclose( + self.assertAlmostEqual(self.parser.parse('math:asin(0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:asin(-0.0e0)').evaluate(), -0.0) + self.assertAlmostEqual( self.parser.parse('math:asin(1.0e0)').evaluate(), - 1.5707963267948966e0, rel_tol=1e-14 - )) - self.assertTrue(math.isclose( + 1.5707963267948966e0, places=13 + ) + self.assertAlmostEqual( self.parser.parse('math:asin(-1.0e0)').evaluate(), - -1.5707963267948966e0, rel_tol=1e-14 - )) + -1.5707963267948966e0, places=13 + ) self.assertTrue(math.isnan(self.parser.parse("math:asin(2.0e0)").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:asin(xs:double('NaN'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:asin(xs:double('INF'))").evaluate())) @@ -295,16 +284,16 @@ def test_acos_math_function(self): self.assertIsNone(self.parser.parse('math:acos(())').evaluate()) - self.assertTrue(math.isclose( + self.assertAlmostEqual( self.parser.parse('math:acos(0.0e0)').evaluate(), - 1.5707963267948966e0, rel_tol=1e-14 - )) - self.assertTrue(math.isclose( + 1.5707963267948966e0, places=13 + ) + self.assertAlmostEqual( self.parser.parse('math:acos(-0.0e0)').evaluate(), - 1.5707963267948966e0, rel_tol=1e-14 - )) - self.assertEqual(self.parser.parse('math:acos(1.0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:acos(-1.0e0)').evaluate(), math.pi) + 1.5707963267948966e0, places=13 + ) + self.assertAlmostEqual(self.parser.parse('math:acos(1.0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:acos(-1.0e0)').evaluate(), math.pi) self.assertTrue(math.isnan(self.parser.parse("math:acos(2.0e0)").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:acos(xs:double('NaN'))").evaluate())) self.assertTrue(math.isnan(self.parser.parse("math:acos(xs:double('INF'))").evaluate())) @@ -312,37 +301,37 @@ def test_atan_math_function(self): self.assertIsNone(self.parser.parse('math:atan(())').evaluate()) - self.assertEqual(self.parser.parse('math:atan(0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:atan(-0.0e0)').evaluate(), -0.0) - self.assertTrue(math.isclose( + self.assertAlmostEqual(self.parser.parse('math:atan(0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:atan(-0.0e0)').evaluate(), -0.0) + self.assertAlmostEqual( self.parser.parse('math:atan(1.0e0)').evaluate(), - 0.7853981633974483e0, rel_tol=1e-14 - )) - self.assertTrue(math.isclose( + 0.7853981633974483e0, places=13 + ) + self.assertAlmostEqual( self.parser.parse('math:atan(-1.0e0)').evaluate(), - -0.7853981633974483e0, rel_tol=1e-14 - )) + -0.7853981633974483e0, places=13 + ) self.assertTrue(math.isnan(self.parser.parse("math:atan(xs:double('NaN'))").evaluate())) - self.assertTrue(math.isclose( + self.assertAlmostEqual( self.parser.parse("math:atan(xs:double('INF'))").evaluate(), - 1.5707963267948966e0, rel_tol=1e-5 - )) - self.assertTrue(math.isclose( + 1.5707963267948966e0, places=5 + ) + self.assertAlmostEqual( self.parser.parse("math:atan(xs:double('-INF'))").evaluate(), - -1.5707963267948966e0, rel_tol=1e-5 - )) + -1.5707963267948966e0, places=5 + ) def test_atan2_math_function(self): - self.assertEqual(self.parser.parse('math:atan2(+0.0e0, 0.0e0)').evaluate(), 0.0) - self.assertEqual(self.parser.parse('math:atan2(-0.0e0, 0.0e0)').evaluate(), -0.0) - self.assertEqual(self.parser.parse('math:atan2(+0.0e0, -0.0e0)').evaluate(), math.pi) - self.assertEqual(self.parser.parse('math:atan2(-0.0e0, -0.0e0)').evaluate(), -math.pi) - self.assertEqual(self.parser.parse('math:atan2(-1, 0.0e0)').evaluate(), -math.pi / 2) - self.assertEqual(self.parser.parse('math:atan2(+1, 0.0e0)').evaluate(), math.pi / 2) - self.assertEqual(self.parser.parse('math:atan2(-0.0e0, -1)').evaluate(), -math.pi) - self.assertEqual(self.parser.parse('math:atan2(+0.0e0, -1)').evaluate(), math.pi) - self.assertEqual(self.parser.parse('math:atan2(-0.0e0, +1)').evaluate(), -0.0e0) - self.assertEqual(self.parser.parse('math:atan2(+0.0e0, +1)').evaluate(), 0.0e0) + self.assertAlmostEqual(self.parser.parse('math:atan2(+0.0e0, 0.0e0)').evaluate(), 0.0) + self.assertAlmostEqual(self.parser.parse('math:atan2(-0.0e0, 0.0e0)').evaluate(), -0.0) + self.assertAlmostEqual(self.parser.parse('math:atan2(+0.0e0, -0.0e0)').evaluate(), math.pi) + self.assertAlmostEqual(self.parser.parse('math:atan2(-0.0e0, -0.0e0)').evaluate(), -math.pi) + self.assertAlmostEqual(self.parser.parse('math:atan2(-1, 0.0e0)').evaluate(), -math.pi / 2) + self.assertAlmostEqual(self.parser.parse('math:atan2(+1, 0.0e0)').evaluate(), math.pi / 2) + self.assertAlmostEqual(self.parser.parse('math:atan2(-0.0e0, -1)').evaluate(), -math.pi) + self.assertAlmostEqual(self.parser.parse('math:atan2(+0.0e0, -1)').evaluate(), math.pi) + self.assertAlmostEqual(self.parser.parse('math:atan2(-0.0e0, +1)').evaluate(), -0.0e0) + self.assertAlmostEqual(self.parser.parse('math:atan2(+0.0e0, +1)').evaluate(), 0.0e0) def test_analyze_string_function(self): token = self.parser.parse('fn:analyze-string("The cat sat on the mat.", "unmatchable")') @@ -703,7 +692,7 @@ root = self.etree.XML('<root/>') context = XPathContext(root=root) - self.assertEqual(token(context), 27.0) + self.assertAlmostEqual(token(context), 27.0) token = self.parser.parse("function($a) { $a } (10)") with self.assertRaises(MissingContextError): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/tests/test_xpath_context.py new/elementpath-2.5.3/tests/test_xpath_context.py --- old/elementpath-2.5.0/tests/test_xpath_context.py 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/tests/test_xpath_context.py 2022-05-30 14:52:08.000000000 +0200 @@ -288,6 +288,13 @@ context = XPathContext(root, item=TypedElement(root[2], xsd_type, None)) self.assertListEqual(list(context.iter_siblings('preceding-sibling')), list(root[:2])) + @unittest.skipIf(lxml_etree is None, 'lxml library is not installed') + def test_iter_siblings__issue_44(self): + root = lxml_etree.XML('<root>text 1<!-- comment -->text 2<!-- comment --> text 3</root>') + result = select(root, 'node()[1]/following-sibling::node()') + self.assertListEqual(result, [root[0], 'text 2', root[1], ' text 3']) + self.assertListEqual(result, root.xpath('node()[1]/following-sibling::node()')) + def test_iter_descendants(self): root = ElementTree.XML('<A a1="10" a2="20"><B1/><B2/></A>') attr = AttributeNode('a1', '10') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elementpath-2.5.0/tox.ini new/elementpath-2.5.3/tox.ini --- old/elementpath-2.5.0/tox.ini 2022-03-04 16:15:56.000000000 +0100 +++ new/elementpath-2.5.3/tox.ini 2022-05-30 14:52:08.000000000 +0200 @@ -5,8 +5,8 @@ [tox] envlist = - py{37,38,39,310}, pypy3, xmlschema{190,191,192}, - docs, flake8, mypy-py{37,38,39,310}, mypy-latest, pytest, coverage + py{37,38,39,310}, pypy3, xmlschema{19,110,111}, + docs, flake8, mypy-py{37,38,39,310}, pytest, coverage skip_missing_interpreters = true toxworkdir = {homedir}/.tox/elementpath @@ -16,9 +16,9 @@ xmlschema>=1.9.0 docs: Sphinx coverage: coverage - xmlschema190: xmlschema==1.9.0 - xmlschema191: xmlschema==1.9.1 - xmlschema192: xmlschema==1.9.2 + xmlschema190: xmlschema~=1.9.0 + xmlschema1100: xmlschema~=1.10.0 + xmlschema1110: xmlschema~=1.11.0 commands = python -m unittest whitelist_externals = make @@ -27,6 +27,7 @@ make -C doc html make -C doc latexpdf make -C doc doctest + sphinx-build -n -T -b man doc build/sphinx/man [flake8] max-line-length = 100 @@ -37,19 +38,19 @@ commands = flake8 elementpath -[testenv:mypy-py{38,39,310}] +[testenv:mypy-py37] deps = - mypy==0.931 + mypy==0.950 commands = - mypy --config-file {toxinidir}/mypy.ini elementpath + mypy --show-error-codes --no-warn-redundant-casts --no-warn-unused-ignores --no-warn-return-any --strict elementpath python tests/test_typing.py -[testenv:mypy-latest] +[testenv:mypy-py{38,39,310}] deps = - mypy + mypy==0.950 commands = - pip install --upgrade mypy - mypy --config-file {toxinidir}/mypy.ini elementpath + mypy --no-warn-return-any --strict elementpath + python tests/test_typing.py [testenv:coverage] commands =