Fix typed selection
- Fix XPathToken.select_results() for element selection - Fix XPathContext.get_parent() for typed elements
This commit is contained in:
parent
9c4b942700
commit
ad41660f9e
|
@ -280,14 +280,13 @@ def select(self, context=None):
|
|||
for item in context.iter_children_or_self():
|
||||
if is_attribute_node(item, name) or is_element_node(item, tag):
|
||||
path = context.get_path(item)
|
||||
|
||||
xsd_component = self.parser.schema.find(path, self.parser.namespaces)
|
||||
|
||||
# print(path, xsd_component)
|
||||
|
||||
if xsd_component is not None:
|
||||
self.xsd_type = xsd_component.type
|
||||
else:
|
||||
self.xsd_type = self.parser.schema
|
||||
|
||||
yield self.get_typed_node(context, item)
|
||||
else:
|
||||
# XSD typed selection
|
||||
|
|
|
@ -91,6 +91,8 @@ class XPathContext(object):
|
|||
that are not included in the tree. Uses a LRU cache to minimize parent
|
||||
map rebuilding for trees processed with an incremental parser.
|
||||
"""
|
||||
if isinstance(elem, TypedElement):
|
||||
elem = elem[0]
|
||||
if elem is self.root:
|
||||
return
|
||||
|
||||
|
|
|
@ -131,11 +131,11 @@ def is_schema_node(obj):
|
|||
|
||||
|
||||
def is_comment_node(obj):
|
||||
return is_etree_element(obj) and callable(obj.tag) and obj.tag.__name__ == 'Comment'
|
||||
return hasattr(obj, 'tag') and callable(obj.tag) and obj.tag.__name__ == 'Comment'
|
||||
|
||||
|
||||
def is_processing_instruction_node(obj):
|
||||
return is_etree_element(obj) and callable(obj.tag) and obj.tag.__name__ == 'ProcessingInstruction'
|
||||
return hasattr(obj, 'tag') and callable(obj.tag) and obj.tag.__name__ == 'ProcessingInstruction'
|
||||
|
||||
|
||||
def is_document_node(obj):
|
||||
|
@ -155,8 +155,8 @@ else:
|
|||
|
||||
|
||||
def is_xpath_node(obj):
|
||||
return isinstance(obj, tuple) or is_etree_element(obj) or \
|
||||
is_document_node(obj) or is_text_node(obj) or is_schema_node(obj)
|
||||
return isinstance(obj, tuple) or is_etree_element(obj) or is_schema_node(obj) or \
|
||||
is_document_node(obj) or is_text_node(obj)
|
||||
|
||||
|
||||
###
|
||||
|
|
|
@ -27,7 +27,7 @@ from decimal import Decimal
|
|||
from .compat import string_base_type, unicode_type
|
||||
from .exceptions import xpath_error
|
||||
from .namespaces import XQT_ERRORS_NAMESPACE
|
||||
from .xpath_nodes import AttributeNode, NamespaceNode, TypedAttribute, TypedElement, \
|
||||
from .xpath_nodes import AttributeNode, TypedAttribute, TypedElement, \
|
||||
is_etree_element, is_attribute_node, elem_iter_strings, is_text_node, \
|
||||
is_namespace_node, is_comment_node, is_processing_instruction_node, \
|
||||
is_element_node, is_document_node, is_xpath_node, is_schema_node
|
||||
|
@ -291,8 +291,10 @@ class XPathToken(Token):
|
|||
for result in self.select(context):
|
||||
if isinstance(result, TypedElement):
|
||||
yield result[0]
|
||||
elif isinstance(result, (AttributeNode, TypedAttribute)):
|
||||
elif isinstance(result, AttributeNode):
|
||||
yield result[1]
|
||||
elif isinstance(result, TypedAttribute):
|
||||
yield result[0][1] if hasattr(result[0][1], 'type') else result[1]
|
||||
else:
|
||||
yield result
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ if __name__ == '__main__':
|
|||
except ImportError:
|
||||
# Python 2 fallback
|
||||
from test_exceptions import ExceptionsTest
|
||||
from test_namespaces import NamespacessTest
|
||||
from test_namespaces import NamespacesTest
|
||||
from test_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
|
||||
from test_xpath_nodes import XPathNodesTest
|
||||
from test_xpath_token import XPathTokenTest
|
||||
|
|
|
@ -658,12 +658,18 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest):
|
|||
' <B1><p2:C xmlns:p2="ns2"/></B1><B2/>'
|
||||
' <p0:B3><eg:C1 xmlns:eg="http://www.example.com/ns/"/><C2/></p0:B3>'
|
||||
'</p1:A>')
|
||||
|
||||
namespaces = {'p0': 'ns0', 'p2': 'ns2'}
|
||||
prefixes = select(root, "fn:in-scope-prefixes(.)", namespaces, parser=self.parser.__class__)
|
||||
|
||||
if self.etree is lxml_etree:
|
||||
prefixes = {'p0', 'p1'}
|
||||
self.assertIn('p0', prefixes)
|
||||
self.assertIn('p1', prefixes)
|
||||
self.assertNotIn('p2', prefixes)
|
||||
else:
|
||||
prefixes = {'p0', 'p2', 'fn', 'xlink', 'err', 'vc', 'xslt', '', 'hfp'}
|
||||
prefixes |= {x for x in self.etree._namespace_map.values()}
|
||||
self.check_selector("fn:in-scope-prefixes(.)", root, prefixes, namespaces={'p0': 'ns0', 'p2': 'ns2'})
|
||||
self.assertIn('p0', prefixes)
|
||||
self.assertNotIn('p1', prefixes)
|
||||
self.assertIn('p2', prefixes)
|
||||
|
||||
def test_string_constructors(self):
|
||||
self.check_value("xs:string(5.0)", '5.0')
|
||||
|
|
|
@ -13,6 +13,7 @@ import unittest
|
|||
import xml.etree.ElementTree as ElementTree
|
||||
|
||||
from elementpath import *
|
||||
from elementpath.compat import PY3
|
||||
|
||||
|
||||
class XPathContextTest(unittest.TestCase):
|
||||
|
@ -48,7 +49,35 @@ class XPathContextTest(unittest.TestCase):
|
|||
root[2]: root, root[2][0]: root[2], root[2][1]: root[2]
|
||||
})
|
||||
|
||||
def test_path(self):
|
||||
def test_get_parent(self):
|
||||
root = ElementTree.XML('<A><B1><C1/></B1><B2/><B3><C1/><C2 max="10"/></B3></A>')
|
||||
|
||||
context = XPathContext(root)
|
||||
|
||||
self.assertIsNone(context._parent_map)
|
||||
self.assertIsNone(context.get_parent(root))
|
||||
|
||||
self.assertIsNone(context._parent_map)
|
||||
self.assertEqual(context.get_parent(root[0]), root)
|
||||
self.assertIsInstance(context._parent_map, dict)
|
||||
parent_map_id = id(context._parent_map)
|
||||
|
||||
self.assertEqual(context.get_parent(root[1]), root)
|
||||
self.assertEqual(context.get_parent(root[2]), root)
|
||||
self.assertEqual(context.get_parent(root[2][1]), root[2])
|
||||
|
||||
self.assertEqual(context.get_parent(TypedElement(root[2][1], None)), root[2])
|
||||
self.assertEqual(id(context._parent_map), parent_map_id)
|
||||
|
||||
self.assertIsNone(context.get_parent(AttributeNode('max', '10')))
|
||||
self.assertNotEqual(id(context._parent_map), parent_map_id)
|
||||
|
||||
parent_map_id = id(context._parent_map)
|
||||
self.assertIsNone(context.get_parent(AttributeNode('max', '10')))
|
||||
if PY3:
|
||||
self.assertEqual(id(context._parent_map), parent_map_id) # LRU cache prevents parent map rebuild
|
||||
|
||||
def test_get_path(self):
|
||||
root = ElementTree.XML('<A><B1><C1/></B1><B2/><B3><C1/><C2 max="10"/></B3></A>')
|
||||
|
||||
context = XPathContext(root)
|
||||
|
|
|
@ -14,6 +14,7 @@ import unittest
|
|||
import io
|
||||
import math
|
||||
import xml.etree.ElementTree as ElementTree
|
||||
from collections import namedtuple
|
||||
|
||||
from elementpath.namespaces import XSD_NAMESPACE
|
||||
from elementpath.xpath_nodes import AttributeNode, TypedAttribute, TypedElement, NamespaceNode
|
||||
|
@ -61,6 +62,10 @@ class XPathTokenTest(unittest.TestCase):
|
|||
context = XPathContext(elem, item=TypedAttribute(AttributeNode('max', '30'), 30))
|
||||
self.assertListEqual(list(token.select_results(context)), [30])
|
||||
|
||||
attribute = namedtuple('XsdAttribute', 'name type')('max', 'xs:string')
|
||||
context = XPathContext(elem, item=TypedAttribute(AttributeNode('max', attribute), 30))
|
||||
self.assertListEqual(list(token.select_results(context)), [attribute])
|
||||
|
||||
context = XPathContext(elem, item=10)
|
||||
self.assertListEqual(list(token.select_results(context)), [10])
|
||||
|
||||
|
|
Loading…
Reference in New Issue