Skip to content

Commit

Permalink
Fix sequence type check for 'treat as' operator
Browse files Browse the repository at this point in the history
  • Loading branch information
brunato committed Jan 28, 2021
1 parent 31b5ab4 commit 1c9b073
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 9 deletions.
14 changes: 5 additions & 9 deletions elementpath/xpath2/xpath2_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -650,17 +650,13 @@ def led(self, left):
try:
self[:] = left, self.parser.expression(rbp=self.rbp)
except ElementPathTypeError as err:
try:
raise err.token.wrong_syntax(err.message) from None
except AttributeError:
raise self.parser.next_token.wrong_syntax() from None
message = getattr(err, 'message', str(err))
raise self.error('XPST0003', message) from None

next_symbol = self.parser.next_token.symbol
if self[1].symbol != 'empty-sequence' and next_symbol in ('?', '*', '+'):
self[2:] = self.parser.symbol_table[next_symbol](self.parser), # Add nullary token
self.parser.advance()
elif next_symbol in {'('}:
raise self.error('XPST0051')
return self


Expand Down Expand Up @@ -715,9 +711,9 @@ def evaluate(self, context=None):
raise self.wrong_sequence_type()
elif self[1].label in ('kind test', 'sequence type'):
for position, item in enumerate(self[0].select(context)):
if self[1].evaluate(context) is None:
if context is not None and not isinstance(context, XPathSchemaContext):
raise self.wrong_sequence_type()
result = self[1].evaluate(context)
if isinstance(result, list) and not result:
raise self.wrong_sequence_type()
elif position and (occurs is None or occurs == '?'):
raise self.wrong_sequence_type("more than one item in sequence")
castable_expr.append(item)
Expand Down
25 changes: 25 additions & 0 deletions tests/test_xpath2_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -878,6 +878,25 @@ def test_instance_of_expression(self):
self.check_value("5 instance of empty-sequence()", False)
self.check_value("() instance of empty-sequence()", True)

self.wrong_syntax("5 instance of unknown()", 'XPST0003', "unknown function 'unknown'")
self.wrong_syntax("1e3 instance of empty-sequence()(",
'XPST0003', "unexpected '(' operator")

# Test dynamic evaluation error on prefixed name
parser = XPath2Parser()
token = parser.parse('5 instance of xs:decimal')
parser.namespaces.pop('xs')
with self.assertRaises(NameError) as ctx:
token.evaluate()
self.assertIn('XPST0081', str(ctx.exception))

# From W3C XQuery/XPath tests
context = XPathContext(element)
self.check_value("not(1 instance of node())", True, context)
self.check_value("(1, 2, 3, 4, 5) instance of item()+", True, context)
self.check_value("(1, 2, 3, 4, 5) instance of item()", False, context)
self.wrong_name("3 instance of void")

def test_treat_as_expression(self):
element = self.etree.Element('schema')
context = XPathContext(element)
Expand All @@ -895,6 +914,12 @@ def test_treat_as_expression(self):
self.check_value("5 treat as empty-sequence()", ElementPathTypeError)
self.check_value("() treat as empty-sequence()", [])

# From W3C XQuery/XPath tests
self.check_value("3 treat as item()+", [3], context)
self.wrong_type("3 treat as node()+", 'XPDY0050', context=context)
self.check_value("(1, 2, 3) treat as item()+", [1, 2, 3], context)
self.wrong_type("(1, 2, 3) treat as item()", 'XPDY0050', context=context)

def test_castable_expression(self):
self.check_value("5 castable as xs:integer", True)
self.check_value("'5' castable as xs:integer", True)
Expand Down

0 comments on commit 1c9b073

Please sign in to comment.