diff --git a/elementpath/xpath1/xpath1_parser.py b/elementpath/xpath1/xpath1_parser.py
index eb1159c7..98a05480 100644
--- a/elementpath/xpath1/xpath1_parser.py
+++ b/elementpath/xpath1/xpath1_parser.py
@@ -19,8 +19,8 @@
xsd10_atomic_types, xsd11_atomic_types, ATOMIC_VALUES
from ..xpath_context import XPathSchemaContext
from ..tdop import Parser
-from ..namespaces import XSD_ANY_SIMPLE_TYPE, XML_NAMESPACE, XSD_NAMESPACE, \
- XSD_UNTYPED_ATOMIC, get_namespace, get_expanded_name, split_expanded_name
+from ..namespaces import XSD_ANY_SIMPLE_TYPE, XSD_ANY_ATOMIC_TYPE, XML_NAMESPACE, \
+ XSD_NAMESPACE, XSD_UNTYPED_ATOMIC, get_namespace, get_expanded_name, split_expanded_name
from ..schema_proxy import AbstractSchemaProxy
from ..xpath_token import XPathToken
from ..xpath_nodes import XPathNode, TypedElement, AttributeNode, TypedAttribute, \
@@ -175,7 +175,7 @@ def nud_(self):
self.parser.advance('(')
if nargs is None:
del self[:]
- if self.parser.next_token.symbol == ')':
+ if self.parser.next_token.symbol in (')', '(end)'):
raise self.error(code, 'at least an argument is required')
while True:
self.append(self.parser.expression(5))
@@ -186,7 +186,9 @@ def nud_(self):
return self
elif nargs == 0:
if self.parser.next_token.symbol != ')':
- raise self.error(code, '%s has no arguments' % str(self))
+ if self.parser.next_token.symbol != '(end)':
+ raise self.error(code, '%s has no arguments' % str(self))
+ raise self.parser.next_token.wrong_syntax()
self.parser.advance()
return self
elif isinstance(nargs, (tuple, list)):
@@ -196,7 +198,7 @@ def nud_(self):
k = 0
while k < min_args:
- if self.parser.next_token.symbol == ')':
+ if self.parser.next_token.symbol in (')', '(end)'):
msg = 'Too few arguments: expected at least %s arguments' % min_args
raise self.wrong_nargs(msg if min_args > 1 else msg[:-1])
@@ -215,7 +217,7 @@ def nud_(self):
elif k == 0 and self.parser.next_token.symbol != ')':
self[k:] = self.parser.expression(5),
else:
- break
+ break # pragma: no cover
k += 1
if self.parser.next_token.symbol == ',':
@@ -260,8 +262,12 @@ def is_instance(self, obj, type_qname):
if get_namespace(type_qname) == XSD_NAMESPACE:
if type_qname == XSD_UNTYPED_ATOMIC:
return isinstance(obj, UntypedAtomic)
+ elif type_qname == XSD_ANY_ATOMIC_TYPE:
+ return isinstance(obj, AnyAtomicType)
elif type_qname == XSD_ANY_SIMPLE_TYPE:
- return isinstance(obj, (AnyAtomicType, list))
+ return isinstance(obj, AnyAtomicType) or \
+ isinstance(obj, list) and \
+ all(isinstance(x, AnyAtomicType) for x in obj)
try:
if self.xsd_version == '1.1':
@@ -271,7 +277,11 @@ def is_instance(self, obj, type_qname):
pass
if self.schema is not None:
- return self.schema.is_instance(obj, type_qname)
+ try:
+ return self.schema.is_instance(obj, type_qname)
+ except KeyError:
+ pass
+
raise ElementPathKeyError("unknown type %r" % type_qname)
def is_sequence_type(self, value):
diff --git a/tests/test_xpath1_parser.py b/tests/test_xpath1_parser.py
index 6498f003..aaf958c4 100644
--- a/tests/test_xpath1_parser.py
+++ b/tests/test_xpath1_parser.py
@@ -32,8 +32,14 @@
except ImportError:
lxml_etree = None
+try:
+ import xmlschema
+except ImportError:
+ xmlschema = None
+
from elementpath import *
-from elementpath.namespaces import XSD_NAMESPACE, XPATH_FUNCTIONS_NAMESPACE
+from elementpath.namespaces import XSD_NAMESPACE, XPATH_FUNCTIONS_NAMESPACE, \
+ XSD_ANY_ATOMIC_TYPE, XSD_ANY_SIMPLE_TYPE, XSD_UNTYPED_ATOMIC
try:
from tests import xpath_test_class
@@ -242,6 +248,65 @@ def test_wrong_nargs(self):
self.wrong_type("contains('XPath', 'XP', 20)")
self.wrong_type("boolean(1, 5)")
+ def test_xsd_qname_method(self):
+ qname = self.parser.xsd_qname('string')
+ self.assertEqual(qname, 'xs:string')
+
+ parser = self.parser.__class__(namespaces={'xs': XSD_NAMESPACE})
+ parser.namespaces['xsd'] = parser.namespaces.pop('xs')
+ self.assertEqual(parser.xsd_qname('string'), 'xsd:string')
+
+ parser.namespaces.pop('xsd')
+ with self.assertRaises(NameError) as ctx:
+ parser.xsd_qname('string')
+ self.assertIn('XPST0081', str(ctx.exception))
+
+ def test_is_instance_method(self):
+ self.assertTrue(self.parser.is_instance(datatypes.UntypedAtomic(1),
+ XSD_UNTYPED_ATOMIC))
+ self.assertFalse(self.parser.is_instance(1, XSD_UNTYPED_ATOMIC))
+ self.assertTrue(self.parser.is_instance(1, XSD_ANY_ATOMIC_TYPE))
+ self.assertFalse(self.parser.is_instance([1], XSD_ANY_ATOMIC_TYPE))
+ self.assertTrue(self.parser.is_instance(1, XSD_ANY_SIMPLE_TYPE))
+ self.assertTrue(self.parser.is_instance([1], XSD_ANY_SIMPLE_TYPE))
+
+ self.assertTrue(self.parser.is_instance('foo', '{%s}string' % XSD_NAMESPACE))
+ self.assertFalse(self.parser.is_instance(1, '{%s}string' % XSD_NAMESPACE))
+ self.assertTrue(self.parser.is_instance(1.0, '{%s}double' % XSD_NAMESPACE))
+ self.assertFalse(self.parser.is_instance(1.0, '{%s}float' % XSD_NAMESPACE))
+
+ self.parser._xsd_version = '1.1'
+ try:
+ self.assertTrue(self.parser.is_instance(1.0, '{%s}double' % XSD_NAMESPACE))
+ self.assertFalse(self.parser.is_instance(1.0, '{%s}float' % XSD_NAMESPACE))
+ finally:
+ self.parser._xsd_version = '1.0'
+
+ with self.assertRaises(KeyError):
+ self.parser.is_instance('foo', '{%s}unknown' % XSD_NAMESPACE)
+
+ if xmlschema is not None and self.parser.version > '1.0':
+ schema = xmlschema.XMLSchema("""
+
+
+
+
+ """)
+
+ self.parser.schema = xmlschema.xpath.XMLSchemaProxy(schema)
+ try:
+ self.assertFalse(self.parser.is_instance(1.0, 'myInt'))
+ self.assertTrue(self.parser.is_instance(1, 'myInt'))
+ with self.assertRaises(KeyError):
+ self.parser.is_instance(1.0, 'dType')
+ finally:
+ self.parser.schema = None
+
+ def test_check_variables_method(self):
+ self.assertIsNone(self.parser.check_variables(
+ {'values': [1, 2, -1], 'myaddress': 'info@example.com', 'word': ''}
+ ))
+
# XPath expression tests
def test_node_selection(self):
root = self.etree.XML('')
@@ -736,6 +801,9 @@ def test_boolean_functions(self):
self.check_value("boolean(' ')", True)
self.check_value("boolean('')", False)
+ self.wrong_type("true(1)", 'XPST0017', "'true' function has no arguments")
+ self.wrong_syntax("true(", 'unexpected end of source')
+
if self.parser.version == '1.0':
self.wrong_syntax("boolean(())")
else:
diff --git a/tests/test_xpath2_parser.py b/tests/test_xpath2_parser.py
index e14c579e..9c6a7581 100644
--- a/tests/test_xpath2_parser.py
+++ b/tests/test_xpath2_parser.py
@@ -112,6 +112,7 @@ def test_is_sequence_type_method(self):
self.assertTrue(self.parser.is_sequence_type('item()?'))
self.assertTrue(self.parser.is_sequence_type('xs:untypedAtomic+'))
self.assertFalse(self.parser.is_sequence_type(10))
+ self.assertFalse(self.parser.is_sequence_type(''))
self.assertFalse(self.parser.is_sequence_type('empty-sequence()*'))
self.assertFalse(self.parser.is_sequence_type('unknown'))
self.assertFalse(self.parser.is_sequence_type('unknown?'))
@@ -151,7 +152,7 @@ def test_check_variables_method(self):
{'values': 'xs:decimal+', 'myaddress': 'xs:string', 'word': 'xs:string'})
self.assertIsNone(self.parser.check_variables(
- {'values': 1, 'myaddress': 'info@example.com', 'word': ''}
+ {'values': [1, 2, -1], 'myaddress': 'info@example.com', 'word': ''}
))
with self.assertRaises(NameError) as ctx:
@@ -946,6 +947,8 @@ def test_get_atomic_value(self):
token = self.parser.parse('true()')
+ self.assertEqual(self.parser.get_atomic_value('xs:int'), 1)
+ self.assertEqual(self.parser.get_atomic_value('xs:unknown'), UntypedAtomic('1'))
self.assertEqual(self.parser.get_atomic_value(schema.elements['d'].type),
UntypedAtomic('1'))
@@ -960,6 +963,10 @@ def test_get_atomic_value(self):
with self.assertRaises(AttributeError) as err:
self.parser.get_atomic_value(schema)
+ value = self.parser.get_atomic_value('unknown')
+ self.assertIsInstance(value, UntypedAtomic)
+ self.assertEqual(value, UntypedAtomic(value='1'))
+
value = self.parser.get_atomic_value(schema.elements['a'].type)
self.assertIsInstance(value, UntypedAtomic)
self.assertEqual(value, UntypedAtomic(value='1'))