diff --git a/elementpath/xpath1_parser.py b/elementpath/xpath1_parser.py index 215f954..a1e5a3c 100644 --- a/elementpath/xpath1_parser.py +++ b/elementpath/xpath1_parser.py @@ -257,7 +257,11 @@ def select(self, context=None): xsd_type = self.match_xsd_type(item, name) if xsd_type is not None: primitive_type = self.parser.schema.get_primitive_type(xsd_type) - value = XSD_BUILTIN_TYPES[primitive_type.local_name].value + try: + value = XSD_BUILTIN_TYPES[primitive_type.local_name or 'anyType'].value + except KeyError: + value = XSD_BUILTIN_TYPES['anyType'].value + if isinstance(item, AttributeNode): yield TypedAttribute(item, value) else: @@ -676,7 +680,6 @@ def select(self, context=None): yield result else: items = [] - context2 = context.copy() left_results = list(self[0].select(context)) context.size = len(left_results) for context.position, context.item in enumerate(left_results): @@ -749,7 +752,10 @@ def led(self, left): @method('[') def select(self, context=None): - if context is not None: + if isinstance(context, XPathSchemaContext): + for item in self[0].select(context): + yield item + elif context is not None: for position, item in enumerate(self[0].select(context), start=1): predicate = list(self[1].select(context.copy())) if len(predicate) == 1 and isinstance(predicate[0], NumericTypeProxy): @@ -825,14 +831,20 @@ def select(self, context=None): else: return - for elem in context.iter_parent(axis=self.symbol): - follows = False - for child in context.iter_children_or_self(elem, child_axis=True): - if follows: + for parent in context.iter_parent(axis=self.symbol): + if isinstance(context, XPathSchemaContext): + for _ in context.iter_children_or_self(parent, child_axis=True): for result in self[0].select(context): yield result - elif item is child: - follows = True + + else: + follows = False + for child in context.iter_children_or_self(parent, child_axis=True): + if follows: + for result in self[0].select(context): + yield result + elif item is child: + follows = True @method(axis('following')) diff --git a/elementpath/xpath2_functions.py b/elementpath/xpath2_functions.py index e80a801..6e1cbe8 100644 --- a/elementpath/xpath2_functions.py +++ b/elementpath/xpath2_functions.py @@ -19,6 +19,7 @@ import time import re import locale import unicodedata +import xml.etree.ElementTree as ElementTree from .compat import PY3, string_base_type, unicode_chr, urlparse, urljoin, urllib_quote, unicode_type from .datatypes import QNAME_PATTERN, DateTime10, Date10, Time, Timezone, Duration, DayTimeDuration @@ -182,11 +183,21 @@ def select(self, context=None): elem = self.get_argument(context) if not is_element_node(elem): raise self.error('FORG0006', 'argument %r is not a node' % elem) - for e in elem.iter(): - tag_ns = get_namespace(e.tag) - for pfx, uri in self.parser.namespaces.items(): - if uri == tag_ns: - yield pfx + + if isinstance(context, XPathSchemaContext): + # For schema context returns prefixes of static namespaces + for prefix in self.parser.namespaces: + yield prefix + elif hasattr(elem, 'nsmap'): + # For lxml returns Element's prefixes + for prefix in elem.nsmap: + yield prefix or '' + else: + # For ElementTree returns module registered prefixes + prefixes = {x for x in self.parser.namespaces} + prefixes.update(x for x in ElementTree._namespace_map.values()) + for prefix in prefixes: + yield prefix @method(function('resolve-QName', nargs=2)) diff --git a/elementpath/xpath_helpers.py b/elementpath/xpath_helpers.py deleted file mode 100644 index d2b214c..0000000 --- a/elementpath/xpath_helpers.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright (c), 2018-2019, 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 -# distribution, or http://opensource.org/licenses/MIT. -# -# @author Davide Brunato -# -from .exceptions import xpath_error -from .xpath_nodes import is_element_node - - -def boolean_value(obj, token=None): - """ - The effective boolean value, as computed by fn:boolean(). - Moved to token class but kept for backward compatibility. - """ - if isinstance(obj, list): - if not obj: - return False - elif isinstance(obj[0], tuple) or is_element_node(obj[0]): - return True - elif len(obj) == 1: - return bool(obj[0]) - else: - raise xpath_error( - code='FORG0006', token=token, prefix=getattr(token, 'error_prefix', 'err'), - message="Effective boolean value is not defined for a sequence of two or " - "more items not starting with an XPath node.", - ) - elif isinstance(obj, tuple) or is_element_node(obj): - raise xpath_error( - code='FORG0006', token=token, prefix=getattr(token, 'error_prefix', 'err'), - message="Effective boolean value is not defined for {}.".format(obj) - ) - return bool(obj) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2a368e8..9f74c2f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -3,6 +3,6 @@ setuptools tox coverage lxml -xmlschema~=1.0.13 +xmlschema~=1.0.14 Sphinx -e . diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 36e08fe..15f0376 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -23,7 +23,6 @@ from elementpath.xpath_nodes import AttributeNode, NamespaceNode, is_etree_eleme node_base_uri, node_document_uri, node_children, node_is_id, node_is_idrefs, \ node_nilled, node_kind, node_name from elementpath.xpath_token import ordinal -from elementpath.xpath_helpers import boolean_value from elementpath.xpath1_parser import XPath1Parser @@ -247,20 +246,6 @@ class XPathTokenHelpersTest(unittest.TestCase): self.assertEqual(ordinal(23), '23rd') self.assertEqual(ordinal(34), '34th') - def test_boolean_value_function(self): - elem = ElementTree.Element('A') - - self.assertFalse(boolean_value([])) - self.assertTrue(boolean_value([elem])) - self.assertFalse(boolean_value([0])) - self.assertTrue(boolean_value([1])) - with self.assertRaises(TypeError): - boolean_value([1, 1]) - with self.assertRaises(TypeError): - boolean_value(elem) - self.assertFalse(boolean_value(0)) - self.assertTrue(boolean_value(1)) - def test_get_argument_method(self): token = self.parser.symbol_table['true'](self.parser) diff --git a/tests/test_xpath1_parser.py b/tests/test_xpath1_parser.py index 8df9f5e..ed444db 100644 --- a/tests/test_xpath1_parser.py +++ b/tests/test_xpath1_parser.py @@ -217,6 +217,17 @@ class XPath1ParserTest(unittest.TestCase): with self.assertRaises(TypeError): token.boolean_value(elem) + self.assertFalse(token.boolean_value([])) + self.assertTrue(token.boolean_value([elem])) + self.assertFalse(token.boolean_value([0])) + self.assertTrue(token.boolean_value([1])) + with self.assertRaises(TypeError): + token.boolean_value([1, 1]) + with self.assertRaises(TypeError): + token.boolean_value(elem) + self.assertFalse(token.boolean_value(0)) + self.assertTrue(token.boolean_value(1)) + def test_data_value_function(self): token = self.parser.parse('true()') self.assertIsNone(token.data_value(None)) diff --git a/tests/test_xpath2_parser.py b/tests/test_xpath2_parser.py index 937822e..28522c4 100644 --- a/tests/test_xpath2_parser.py +++ b/tests/test_xpath2_parser.py @@ -552,6 +552,12 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest): ) self.assertIsNone(parser.parse('fn:resolve-uri(())').evaluate(context)) + def test_predicate(self): + super(XPath2ParserTest, self).test_predicate() + root = self.etree.XML('') + self.check_selector("/(A/*/*)[1]", root, [root[0][0]]) + self.check_selector("/A/*/*[1]", root, [root[0][0], root[1][0]]) + def test_sequence_general_functions(self): # Test cases from https://www.w3.org/TR/xquery-operators/#general-seq-funcs self.check_value('fn:empty(("hello", "world"))', False) @@ -647,7 +653,16 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest): self.check_value("fn:namespace-uri-for-prefix('eg', .)", 'http://www.example.com/ns/', context=context) self.check_selector("fn:namespace-uri-for-prefix('p3', .)", root, NameError, namespaces={'p3': ''}) - self.check_selector("fn:in-scope-prefixes(.)", root, ['p2', 'p0'], namespaces={'p0': 'ns0', 'p2': 'ns2'}) + def test_in_scope_prefixes_function(self): + root = self.etree.XML('' + ' ' + ' ' + '') + if self.etree is lxml_etree: + prefixes = {'p0', 'p1'} + else: + prefixes = {'p0', 'p2', 'fn', 'xlink', 'err'} | {x for x in self.etree._namespace_map.values()} + self.check_selector("fn:in-scope-prefixes(.)", root, prefixes, namespaces={'p0': 'ns0', 'p2': 'ns2'}) def test_string_constructors(self): self.check_value("xs:string(5.0)", '5.0') diff --git a/tox.ini b/tox.ini index 23116f0..d979945 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ toxworkdir = {homedir}/.tox/elementpath [testenv] deps = lxml - xmlschema~=1.0.13 + xmlschema~=1.0.14 docs: Sphinx flake8: flake8 coverage: coverage @@ -24,7 +24,7 @@ commands = python tests/test_elementpath.py [testenv:py38] deps = lxml==4.3.5 - xmlschema~=1.0.13 + xmlschema~=1.0.14 commands = python -m unittest [testenv:docs]