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():
|
for item in context.iter_children_or_self():
|
||||||
if is_attribute_node(item, name) or is_element_node(item, tag):
|
if is_attribute_node(item, name) or is_element_node(item, tag):
|
||||||
path = context.get_path(item)
|
path = context.get_path(item)
|
||||||
|
|
||||||
xsd_component = self.parser.schema.find(path, self.parser.namespaces)
|
xsd_component = self.parser.schema.find(path, self.parser.namespaces)
|
||||||
|
|
||||||
# print(path, xsd_component)
|
|
||||||
|
|
||||||
if xsd_component is not None:
|
if xsd_component is not None:
|
||||||
self.xsd_type = xsd_component.type
|
self.xsd_type = xsd_component.type
|
||||||
else:
|
else:
|
||||||
self.xsd_type = self.parser.schema
|
self.xsd_type = self.parser.schema
|
||||||
|
|
||||||
yield self.get_typed_node(context, item)
|
yield self.get_typed_node(context, item)
|
||||||
else:
|
else:
|
||||||
# XSD typed selection
|
# XSD typed selection
|
||||||
|
|
|
@ -91,6 +91,8 @@ class XPathContext(object):
|
||||||
that are not included in the tree. Uses a LRU cache to minimize parent
|
that are not included in the tree. Uses a LRU cache to minimize parent
|
||||||
map rebuilding for trees processed with an incremental parser.
|
map rebuilding for trees processed with an incremental parser.
|
||||||
"""
|
"""
|
||||||
|
if isinstance(elem, TypedElement):
|
||||||
|
elem = elem[0]
|
||||||
if elem is self.root:
|
if elem is self.root:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -131,11 +131,11 @@ def is_schema_node(obj):
|
||||||
|
|
||||||
|
|
||||||
def is_comment_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):
|
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):
|
def is_document_node(obj):
|
||||||
|
@ -155,8 +155,8 @@ else:
|
||||||
|
|
||||||
|
|
||||||
def is_xpath_node(obj):
|
def is_xpath_node(obj):
|
||||||
return isinstance(obj, tuple) or is_etree_element(obj) or \
|
return isinstance(obj, tuple) or is_etree_element(obj) or is_schema_node(obj) or \
|
||||||
is_document_node(obj) or is_text_node(obj) or is_schema_node(obj)
|
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 .compat import string_base_type, unicode_type
|
||||||
from .exceptions import xpath_error
|
from .exceptions import xpath_error
|
||||||
from .namespaces import XQT_ERRORS_NAMESPACE
|
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_etree_element, is_attribute_node, elem_iter_strings, is_text_node, \
|
||||||
is_namespace_node, is_comment_node, is_processing_instruction_node, \
|
is_namespace_node, is_comment_node, is_processing_instruction_node, \
|
||||||
is_element_node, is_document_node, is_xpath_node, is_schema_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):
|
for result in self.select(context):
|
||||||
if isinstance(result, TypedElement):
|
if isinstance(result, TypedElement):
|
||||||
yield result[0]
|
yield result[0]
|
||||||
elif isinstance(result, (AttributeNode, TypedAttribute)):
|
elif isinstance(result, AttributeNode):
|
||||||
yield result[1]
|
yield result[1]
|
||||||
|
elif isinstance(result, TypedAttribute):
|
||||||
|
yield result[0][1] if hasattr(result[0][1], 'type') else result[1]
|
||||||
else:
|
else:
|
||||||
yield result
|
yield result
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ if __name__ == '__main__':
|
||||||
except ImportError:
|
except ImportError:
|
||||||
# Python 2 fallback
|
# Python 2 fallback
|
||||||
from test_exceptions import ExceptionsTest
|
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_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
|
||||||
from test_xpath_nodes import XPathNodesTest
|
from test_xpath_nodes import XPathNodesTest
|
||||||
from test_xpath_token import XPathTokenTest
|
from test_xpath_token import XPathTokenTest
|
||||||
|
|
|
@ -658,12 +658,18 @@ class XPath2ParserTest(test_xpath1_parser.XPath1ParserTest):
|
||||||
' <B1><p2:C xmlns:p2="ns2"/></B1><B2/>'
|
' <B1><p2:C xmlns:p2="ns2"/></B1><B2/>'
|
||||||
' <p0:B3><eg:C1 xmlns:eg="http://www.example.com/ns/"/><C2/></p0:B3>'
|
' <p0:B3><eg:C1 xmlns:eg="http://www.example.com/ns/"/><C2/></p0:B3>'
|
||||||
'</p1:A>')
|
'</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:
|
if self.etree is lxml_etree:
|
||||||
prefixes = {'p0', 'p1'}
|
self.assertIn('p0', prefixes)
|
||||||
|
self.assertIn('p1', prefixes)
|
||||||
|
self.assertNotIn('p2', prefixes)
|
||||||
else:
|
else:
|
||||||
prefixes = {'p0', 'p2', 'fn', 'xlink', 'err', 'vc', 'xslt', '', 'hfp'}
|
self.assertIn('p0', prefixes)
|
||||||
prefixes |= {x for x in self.etree._namespace_map.values()}
|
self.assertNotIn('p1', prefixes)
|
||||||
self.check_selector("fn:in-scope-prefixes(.)", root, prefixes, namespaces={'p0': 'ns0', 'p2': 'ns2'})
|
self.assertIn('p2', prefixes)
|
||||||
|
|
||||||
def test_string_constructors(self):
|
def test_string_constructors(self):
|
||||||
self.check_value("xs:string(5.0)", '5.0')
|
self.check_value("xs:string(5.0)", '5.0')
|
||||||
|
|
|
@ -13,6 +13,7 @@ import unittest
|
||||||
import xml.etree.ElementTree as ElementTree
|
import xml.etree.ElementTree as ElementTree
|
||||||
|
|
||||||
from elementpath import *
|
from elementpath import *
|
||||||
|
from elementpath.compat import PY3
|
||||||
|
|
||||||
|
|
||||||
class XPathContextTest(unittest.TestCase):
|
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]
|
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>')
|
root = ElementTree.XML('<A><B1><C1/></B1><B2/><B3><C1/><C2 max="10"/></B3></A>')
|
||||||
|
|
||||||
context = XPathContext(root)
|
context = XPathContext(root)
|
||||||
|
|
|
@ -14,6 +14,7 @@ import unittest
|
||||||
import io
|
import io
|
||||||
import math
|
import math
|
||||||
import xml.etree.ElementTree as ElementTree
|
import xml.etree.ElementTree as ElementTree
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
from elementpath.namespaces import XSD_NAMESPACE
|
from elementpath.namespaces import XSD_NAMESPACE
|
||||||
from elementpath.xpath_nodes import AttributeNode, TypedAttribute, TypedElement, NamespaceNode
|
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))
|
context = XPathContext(elem, item=TypedAttribute(AttributeNode('max', '30'), 30))
|
||||||
self.assertListEqual(list(token.select_results(context)), [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)
|
context = XPathContext(elem, item=10)
|
||||||
self.assertListEqual(list(token.select_results(context)), [10])
|
self.assertListEqual(list(token.select_results(context)), [10])
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue