diff --git a/doc/conf.py b/doc/conf.py index 086794a..ca8fe2e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -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 --------------------------------------------------- diff --git a/elementpath/__init__.py b/elementpath/__init__.py index 52fe050..b690657 100644 --- a/elementpath/__init__.py +++ b/elementpath/__init__.py @@ -8,7 +8,7 @@ # # @author Davide Brunato # -__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 diff --git a/elementpath/xpath_context.py b/elementpath/xpath_context.py index 0ea1612..f1f3a27 100644 --- a/elementpath/xpath_context.py +++ b/elementpath/xpath_context.py @@ -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 diff --git a/setup.py b/setup.py index c7cd379..6bcc84b 100644 --- a/setup.py +++ b/setup.py @@ -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', diff --git a/tests/test_elementpath.py b/tests/test_elementpath.py index 6368b75..c101dc8 100644 --- a/tests/test_elementpath.py +++ b/tests/test_elementpath.py @@ -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 diff --git a/tests/test_context.py b/tests/test_xpath_context.py similarity index 55% rename from tests/test_context.py rename to tests/test_xpath_context.py index 1522e9d..83b2260 100644 --- a/tests/test_context.py +++ b/tests/test_xpath_context.py @@ -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('') + 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('') @@ -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('') + 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('') 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('') 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('') 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('') + + 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()