diff --git a/elementpath/datatypes.py b/elementpath/datatypes.py index 99b4ad0..cb7d318 100644 --- a/elementpath/datatypes.py +++ b/elementpath/datatypes.py @@ -1050,7 +1050,7 @@ QNAME_PATTERN = re.compile( ) HEX_BINARY_PATTERN = re.compile(r'^[0-9a-fA-F]+$') NOT_BASE64_BINARY_PATTERN = re.compile(r'[^0-9a-zA-z+/= \t\n]') -LANGUAGE_CODE_PATTERN = re.compile(r'^([a-zA-Z]{2}|[iI]-[a-zA-Z]+|[xX]-[a-zA-Z]{1,8})(-[a-zA-Z]{1,8})*$') +LANGUAGE_CODE_PATTERN = re.compile(r'^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$') WRONG_ESCAPE_PATTERN = re.compile(r'%(?![a-eA-E\d]{2})') diff --git a/elementpath/xpath_token.py b/elementpath/xpath_token.py index 1caa038..c60e2f4 100644 --- a/elementpath/xpath_token.py +++ b/elementpath/xpath_token.py @@ -275,14 +275,16 @@ class XPathToken(Token): results = list(self.select(context)) if len(results) == 1: res = results[0] - if isinstance(res, tuple) or is_etree_element(res) or is_document_node(res): + if isinstance(res, (bool, int, float, Decimal)): + return res + elif isinstance(res, tuple) or is_etree_element(res) or is_document_node(res): + return results + elif is_schema_node(res): return results elif self.symbol in ('text', 'node'): return results elif self.label in ('function', 'literal'): return res - elif isinstance(res, bool): # Tests and comparisons - return res else: return results else: @@ -483,13 +485,15 @@ class XPathToken(Token): of the node. Used for schema-based dynamic evaluation of XPath expressions. """ try: - if obj.type.is_simple(): + if obj.type.is_simple() or obj.type.has_simple_content(): # In case of schema element or attribute use a the sample value # of the primitive type primitive_type = self.parser.schema.get_primitive_type(obj.type) return XSD_BUILTIN_TYPES[primitive_type.local_name].value elif obj.type.local_name == 'anyType': return XSD_BUILTIN_TYPES['anyType'].value + else: + return UntypedAtomic('') except AttributeError: raise self.wrong_type("the argument %r is not a node of an XSD schema" % obj) diff --git a/tests/test_xpath1_parser.py b/tests/test_xpath1_parser.py index 7816700..e8f374e 100644 --- a/tests/test_xpath1_parser.py +++ b/tests/test_xpath1_parser.py @@ -839,13 +839,13 @@ class XPath1ParserTest(unittest.TestCase): root = self.etree.XML(XML_DATA_TEST) if self.parser.version == '1.0': self.check_value("'9' + 5.0", 14) - self.check_selector("/values/a + 2", root, [5.4]) + self.check_selector("/values/a + 2", root, 5.4) self.check_value("/values/b + 2", float('nan'), context=XPathContext(root)) else: self.check_selector("/values/a + 2", root, TypeError) self.check_value("/values/b + 2", TypeError, context=XPathContext(root)) - self.check_selector("/values/d + 3", root, [47]) + self.check_selector("/values/d + 3", root, 47) def test_numerical_mod_operator(self): self.check_value("11 mod 3", 2) @@ -854,13 +854,13 @@ class XPath1ParserTest(unittest.TestCase): root = self.etree.XML(XML_DATA_TEST) if self.parser.version == '1.0': - self.check_selector("/values/a mod 2", root, [1.4]) + self.check_selector("/values/a mod 2", root, 1.4) self.check_value("/values/b mod 2", float('nan'), context=XPathContext(root)) else: self.check_selector("/values/a mod 2", root, TypeError) self.check_value("/values/b mod 2", TypeError, context=XPathContext(root)) - self.check_selector("/values/d mod 3", root, [2]) + self.check_selector("/values/d mod 3", root, 2) def test_number_function(self): root = self.etree.XML('15') @@ -1145,13 +1145,14 @@ class LxmlXPath1ParserTest(XPath1ParserTest): else: results = select(root, path, namespaces, self.parser.__class__, **kwargs) variables = kwargs.get('variables', {}) + if namespaces and '' in namespaces: + namespaces = {k: v for k, v in namespaces.items() if k} + if isinstance(expected, set): - if namespaces and '' not in namespaces: - self.assertEqual(set(root.xpath(path, namespaces=namespaces, **variables)), expected) + self.assertEqual(set(root.xpath(path, namespaces=namespaces, **variables)), expected) self.assertEqual(set(results), expected) elif not callable(expected): - if namespaces and '' not in namespaces: - self.assertEqual(root.xpath(path, namespaces=namespaces, **variables), expected) + self.assertEqual(root.xpath(path, namespaces=namespaces, **variables), expected) self.assertEqual(results, expected) elif isinstance(expected, type): self.assertTrue(isinstance(results, expected)) diff --git a/tests/test_xpath2_parser.py b/tests/test_xpath2_parser.py index bbcc82a..5ac4faa 100644 --- a/tests/test_xpath2_parser.py +++ b/tests/test_xpath2_parser.py @@ -657,7 +657,9 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest): self.check_value('xs:language(" en ")', "en") self.check_value('xs:language(" en-GB ")', "en-GB") self.check_value('xs:language("it-IT")', "it-IT") - self.wrong_value('xs:language("hello-world")') + self.check_value('xs:language("i-klingon")', 'i-klingon') # IANA-registered language + self.check_value('xs:language("x-another-language-code")', 'x-another-language-code') + self.wrong_value('xs:language("MoreThan8")') self.check_value('xs:language(())', []) self.check_value('xs:NMTOKEN(" :menĂ¹.09-_ ")', ":menĂ¹.09-_")