Adapt XPathContext to typed iterations

renamed:    tests/test_context.py -> tests/test_xpath_context.py
This commit is contained in:
Davide Brunato 2019-09-30 11:17:04 +02:00
parent 3cd4c95272
commit 72a99dc39f
6 changed files with 98 additions and 29 deletions

View File

@ -29,9 +29,9 @@ copyright = '2018-2019, SISSA (International School for Advanced Studies)'
author = 'Davide Brunato'
# The short X.Y version
version = '1.2'
version = '1.3'
# The full version, including alpha/beta/rc tags
release = '1.2.2'
release = '1.3.0'
# -- General configuration ---------------------------------------------------

View File

@ -8,7 +8,7 @@
#
# @author Davide Brunato <brunato@sissa.it>
#
__version__ = '1.2.2'
__version__ = '1.3.0'
__author__ = "Davide Brunato"
__contact__ = "brunato@sissa.it"
__copyright__ = "Copyright 2018-2019, SISSA"
@ -23,7 +23,7 @@ from .exceptions import ElementPathError, MissingContextError, \
from . import datatypes
from .tdop_parser import Token, Parser
from .xpath_context import XPathContext, XPathSchemaContext
from .xpath_nodes import AttributeNode, NamespaceNode
from .xpath_nodes import AttributeNode, TypedAttribute, TypedElement, NamespaceNode
from .xpath_token import XPathToken
from .xpath1_parser import XPath1Parser
from .xpath2_constructors import XPath2Parser

View File

@ -125,17 +125,16 @@ class XPathContext(object):
self.axis = 'child'
if item is not None:
self.item = item
self.item = item[0] if isinstance(item, TypedElement) else item
elif isinstance(self.item, TypedElement):
self.item = self.item[0]
if self.item is None:
self.size, self.position = 1, 0
self.item = self._root.getroot() if is_document_node(self._root) else self._root
yield self.item
elif is_element_node(self.item):
if isinstance(self.item, TypedElement):
elem = self.item.elem
else:
elem = self.item
elif is_etree_element(self.item):
elem = self.item
if elem.text is not None:
self.item = elem.text
yield self.item
@ -148,26 +147,25 @@ class XPathContext(object):
def iter_preceding(self):
status = self.item, self.size, self.position, self.axis
elem = self.item
if not is_etree_element(elem):
item = e = self.item[0] if isinstance(self.item, TypedElement) else self.item
if not is_etree_element(item):
return
self.axis = 'preceding'
ancestors = []
ancestors = []
while True:
try:
parent = self.parent_map[elem]
parent = self.parent_map[e]
except KeyError:
break
else:
ancestors.append(parent)
elem = parent
e = parent
elem = self.item
for e in self.root.iter():
if e is elem:
if e is item:
break
if e not in ancestors:
elif e not in ancestors:
self.item = e
yield e
@ -176,12 +174,17 @@ class XPathContext(object):
def iter_parent(self, axis=None):
status = self.item, self.size, self.position, self.axis
self.axis = axis
try:
self.item = self.parent_map[self.item]
if isinstance(self.item, TypedElement):
self.item = self.parent_map[self.item[0]]
else:
self.item = self.parent_map[self.item]
except KeyError:
pass
else:
yield self.item
self.item, self.size, self.position, self.axis = status
def iter_descendants(self, item=None, axis=None):
@ -189,13 +192,15 @@ class XPathContext(object):
self.axis = axis
if item is not None:
self.item = item
self.item = item[0] if isinstance(item, TypedElement) else item
elif isinstance(self.item, TypedElement):
self.item = self.item[0]
if self.item is None:
self.size, self.position = 1, 0
yield self._root
self.item = self._root.getroot() if is_document_node(self._root) else self._root
elif not is_etree_element(self.item):
elif not is_element_node(self.item):
return
for descendant in self._iter_descendants():
@ -220,9 +225,9 @@ class XPathContext(object):
self.axis = axis
if item is not None:
self.item = item
if not is_etree_element(self.item):
return
self.item = item[0] if isinstance(item, TypedElement) else item
elif isinstance(self.item, TypedElement):
self.item = self.item[0]
while True:
try:
@ -243,6 +248,8 @@ class XPathContext(object):
self.size, self.position = 1, 0
yield self._root
self.item = self._root.getroot() if is_document_node(self._root) else self._root
elif isinstance(self.item, TypedElement):
self.item = self.item[0]
elif not is_etree_element(self.item):
return
@ -265,8 +272,13 @@ class XPathContext(object):
elif isinstance(item, AttributeNode):
# Match XSD decoded attributes
for attr in filter(lambda x: isinstance(x, TypedAttribute), results):
if attr[0] in results:
if attr[0] == item:
yield attr[1] if is_root else attr
elif is_etree_element(item):
# Match XSD decoded elements
for elem in filter(lambda x: isinstance(x, TypedElement), results):
if elem[0] is item:
yield elem[1] if is_root else elem
self.item, self.size, self.position = status

View File

@ -15,7 +15,7 @@ with open("README.rst") as readme:
setup(
name='elementpath',
version='1.2.2',
version='1.3.0',
packages=['elementpath'],
author='Davide Brunato',
author_email='brunato@sissa.it',

View File

@ -26,7 +26,7 @@ if __name__ == '__main__':
try:
from tests.test_helpers import ExceptionHelpersTest, NamespaceHelpersTest, NodeHelpersTest
from tests.test_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
from tests.test_context import XPathContextTest
from tests.test_xpath_context import XPathContextTest
from tests.test_xpath1_parser import XPath1ParserTest, LxmlXPath1ParserTest
from tests.test_xpath2_parser import XPath2ParserTest, LxmlXPath2ParserTest
from tests.test_schema_proxy import XPath2ParserXMLSchemaTest, LxmlXPath2ParserXMLSchemaTest
@ -36,7 +36,7 @@ if __name__ == '__main__':
# Python 2 fallback
from test_helpers import ExceptionHelpersTest, NamespaceHelpersTest, NodeHelpersTest
from test_datatypes import UntypedAtomicTest, DateTimeTypesTest, DurationTypesTest, TimezoneTypeTest
from test_context import XPathContextTest
from test_xpath_context import XPathContextTest
from test_xpath1_parser import XPath1ParserTest, LxmlXPath1ParserTest
from test_xpath2_parser import XPath2ParserTest, LxmlXPath2ParserTest
from test_schema_proxy import XPath2ParserXMLSchemaTest, LxmlXPath2ParserXMLSchemaTest

View File

@ -32,12 +32,21 @@ class XPathContextTest(unittest.TestCase):
context = XPathContext(root)
self.assertEqual(context.parent_map, {root[0]: root, root[1]: root})
context = XPathContext(root, item=TypedElement(root, ''))
self.assertEqual(context.parent_map, {root[0]: root, root[1]: root})
root = ElementTree.XML('<A><B1><C1/></B1><B2/><B3><C1/><C2/></B3></A>')
context = XPathContext(root)
self.assertEqual(context.parent_map, {
root[0]: root, root[0][0]: root[0], root[1]: root,
root[2]: root, root[2][0]: root[2], root[2][1]: root[2]
})
context = XPathContext(root, item=TypedElement(root, None))
self.assertEqual(context.parent_map, {
root[0]: root, root[0][0]: root[0], root[1]: root,
root[2]: root, root[2][0]: root[2], root[2][1]: root[2]
})
def test_iter_attributes(self):
root = ElementTree.XML('<A a1="10" a2="20"/>')
@ -46,7 +55,11 @@ class XPathContextTest(unittest.TestCase):
sorted(list(context.iter_attributes()), key=lambda x: x[0]),
[AttributeNode(name='a1', value='10'), AttributeNode(name='a2', value='20')]
)
context = XPathContext(root, item=TypedElement(root, ''))
self.assertListEqual(
sorted(list(context.iter_attributes()), key=lambda x: x[0]),
[AttributeNode(name='a1', value='10'), AttributeNode(name='a2', value='20')]
)
context.item = None
self.assertListEqual(list(context.iter_attributes()), [])
@ -55,12 +68,31 @@ class XPathContextTest(unittest.TestCase):
context = XPathContext(root, item=None)
self.assertListEqual(list(context.iter_parent()), [])
context = XPathContext(root)
self.assertListEqual(list(context.iter_parent()), [])
context = XPathContext(root, item=TypedElement(root, ''))
self.assertListEqual(list(context.iter_parent()), [])
root = ElementTree.XML('<A><B1><C1/></B1><B2/><B3><C1/><C2/></B3></A>')
context = XPathContext(root, item=None)
self.assertListEqual(list(context.iter_parent()), [])
context = XPathContext(root, item=root[2][0])
self.assertListEqual(list(context.iter_parent()), [root[2]])
context = XPathContext(root, item=TypedElement(root[2][0], None))
self.assertListEqual(list(context.iter_parent()), [root[2]])
def test_iter_descendants(self):
root = ElementTree.XML('<A a1="10" a2="20"><B1/><B2/></A>')
attr = AttributeNode('a1', '10')
self.assertListEqual(list(XPathContext(root).iter_descendants()), [root, root[0], root[1]])
self.assertListEqual(list(XPathContext(root, item=attr).iter_descendants()), [])
context = XPathContext(root, item=TypedElement(root, ''))
self.assertListEqual(list(context.iter_descendants()), [root, root[0], root[1]])
def test_iter_ancestors(self):
root = ElementTree.XML('<A a1="10" a2="20"><B1/><B2/></A>')
attr = AttributeNode('a1', '10')
@ -69,15 +101,40 @@ class XPathContextTest(unittest.TestCase):
self.assertListEqual(list(XPathContext(root).iter_ancestors(item=root[1])), [root])
self.assertListEqual(list(XPathContext(root, item=attr).iter_ancestors()), [])
context = XPathContext(root, item=TypedElement(root[1], None))
self.assertListEqual(list(context.iter_ancestors()), [root])
def test_iter(self):
root = ElementTree.XML('<A><B1><C1/></B1><B2/><B3><C1/><C2/></B3></A>')
context = XPathContext(root)
self.assertListEqual(list(context.iter()), list(root.iter()))
context.item = None
self.assertListEqual(list(context.iter()), [root] + list(root.iter()))
context.item = AttributeNode('a1', '10')
self.assertListEqual(list(context.iter()), [])
context = XPathContext(root, item=TypedElement(root, None))
self.assertListEqual(list(context.iter()), list(root.iter()))
def test_iter_results(self):
root = ElementTree.XML('<A><B1><C1/></B1><B2/><B3><C1/><C2/></B3></A>')
results = [root[2], root[0][0]]
context = XPathContext(root)
self.assertListEqual(list(context.iter_results(results)), [root[0][0], root[2]])
context = XPathContext(root, item=TypedElement(root, None))
self.assertListEqual(list(context.iter_results(results)), [root[0][0], root[2]])
results = [root[2], TypedElement(root[0][0], None)]
context = XPathContext(root)
self.assertListEqual(list(context.iter_results(results)), [TypedElement(root[0][0], None), root[2]])
context = XPathContext(root, item=TypedElement(root, None))
self.assertListEqual(list(context.iter_results(results)), [TypedElement(root[0][0], None), root[2]])
if __name__ == '__main__':
unittest.main()