diff --git a/elementpath/xpath2/xpath2_parser.py b/elementpath/xpath2/xpath2_parser.py
index db6e96d0..16955969 100644
--- a/elementpath/xpath2/xpath2_parser.py
+++ b/elementpath/xpath2/xpath2_parser.py
@@ -512,7 +512,7 @@ def select(self, context=None):
raise self.missing_context()
s1, s2 = set(self[0].select(copy(context))), set(self[1].select(copy(context)))
- if any(not is_xpath_node(x) for x in s1) or any(not is_xpath_node(x) for x in s1):
+ if any(not is_xpath_node(x) for x in s1) or any(not is_xpath_node(x) for x in s2):
raise self.error('XPTY0004', 'only XPath nodes are allowed')
if self.symbol == 'except':
@@ -568,10 +568,9 @@ def nud(self):
if tk[0].value == variable[0].value:
raise tk.error('XPST0008', 'loop variable in its range expression')
- if self.parser.next_token.symbol == ',':
- self.parser.advance()
- else:
+ if self.parser.next_token.symbol != ',':
break
+ self.parser.advance()
self.parser.advance('satisfies')
self.append(self.parser.expression(5))
@@ -616,10 +615,9 @@ def nud(self):
if tk[0].value == variable[0].value:
raise tk.error('XPST0008', 'loop variable in its range expression')
- if self.parser.next_token.symbol == ',':
- self.parser.advance()
- else:
+ if self.parser.next_token.symbol != ',':
break
+ self.parser.advance()
self.parser.advance('return')
self.append(self.parser.expression(5))
diff --git a/tests/test_xpath2_parser.py b/tests/test_xpath2_parser.py
index bb6f6934..a12fab37 100644
--- a/tests/test_xpath2_parser.py
+++ b/tests/test_xpath2_parser.py
@@ -146,6 +146,23 @@ def test_match_sequence_type_method(self):
self.assertFalse(self.parser.match_sequence_type('1', 'xs:unknown'))
self.assertFalse(self.parser.match_sequence_type('1', 'tns0:string'))
+ def test_variable_reference(self):
+ root = self.etree.XML('')
+
+ context = XPathContext(root=root, variables={'var1': root[0]})
+ self.check_value('$var1', root[0], context=context)
+
+ context = XPathContext(root=root, variables={'tns:var1': root[0]})
+ self.check_raise('$tns:var1', NameError, 'XPST0081', context=context)
+
+ # Test dynamic evaluation error
+ parser = XPath2Parser(namespaces={'tns': 'http://xpath.test/ns'})
+ token = parser.parse('$tns:var1')
+ parser.namespaces.pop('tns')
+ with self.assertRaises(NameError) as ctx:
+ token.evaluate(context)
+ self.assertIn('XPST0081', str(ctx.exception))
+
def test_check_variables_method(self):
self.parser.variable_types.update(
(k, get_sequence_type(v)) for k, v in self.variables.items()
@@ -292,6 +309,14 @@ def test_quantifier_expressions(self):
self.check_value('some $x in (1, 2, "cat") satisfies $x * 2 = 4', True, context)
self.check_value('every $x in (1, 2, "cat") satisfies $x * 2 = 4', False, context)
+ # From W3C XQuery/XPath tests
+ context = XPathContext(root=self.etree.XML(''),
+ variables={'result': [43, 44, 45]})
+
+ self.check_value('some $i in $result satisfies $i = 44', True, context)
+ self.check_value('every $i in $result satisfies $i = 44', False, context)
+ self.check_raise('some $foo in (1, $foo) satisfies 1', NameError, 'XPST0008')
+
def test_for_expressions(self):
# Cases from XPath 2.0 examples
context = XPathContext(root=self.etree.XML(''))
@@ -346,6 +371,13 @@ def test_for_expressions(self):
root[2][0], root[2][2], root[2][0], root[2][3], root[2][0]]
)
+ # From W3C XQuery/XPath tests
+ context = XPathContext(root=self.etree.XML(''),
+ variables={'result': [43, 44, 45]})
+
+ self.check_value('for $i in $result return $i + 10', [53, 54, 55], context)
+ self.check_raise('for $foo in (1, $foo) return 1', NameError, 'XPST0008')
+
def test_idiv_operator(self):
self.check_value("5 idiv 2", 2)
self.check_value("-3.5 idiv -2", 1)
@@ -744,6 +776,17 @@ def test_union_intersect_except_operators(self):
self.check_select('$seq1 except $seq2', [], context=context)
self.check_select('$seq2 except $seq3', root[:1], context=context)
+ self.wrong_type('1 intersect 1', 'XPTY0004',
+ 'only XPath nodes are allowed', context=context)
+ self.wrong_type('1 except $seq1', 'XPTY0004',
+ 'only XPath nodes are allowed', context=context)
+ self.wrong_type('1 union $seq1', 'XPTY0004',
+ 'only XPath nodes are allowed', context=context)
+ self.wrong_type('$seq1 intersect 1', 'XPTY0004',
+ 'only XPath nodes are allowed', context=context)
+ self.wrong_type('$seq1 union 1', 'XPTY0004',
+ 'only XPath nodes are allowed', context=context)
+
def test_node_comparison_operators(self):
# Test cases from https://www.w3.org/TR/xpath20/#id-node-comparisons
root = self.etree.XML('''