# -*- coding: utf-8 -*- """ Tests for different Element class lookup mechanisms. """ from __future__ import absolute_import import unittest, gc from .common_imports import etree, HelperTestCase, _bytes, BytesIO xml_str = _bytes('''\ 0 1 2 ''') class ProxyTestCase(HelperTestCase): """Basic tests for element proxy behaviour. """ etree = etree def test_proxy_reuse(self): root = etree.XML('') b = root.find('b') self.assertTrue(b is root[0]) def test_proxy_reuse_after_gc(self): root = etree.XML('') b = root.find('b') self.assertTrue(self.etree.iselement(b)) gc.collect() self.assertTrue(b is root[0]) def test_proxy_reuse_after_del_root(self): root = etree.XML('') b = root.find('b') self.assertTrue(self.etree.iselement(b)) c = b.find('c') self.assertTrue(self.etree.iselement(c)) del root gc.collect() self.assertTrue(b[0] is c) def test_proxy_hashing(self): root = etree.XML('') old_elements = set(root.iter()) elements = root.iter() del root gc.collect() missing = len(old_elements) self.assertEqual(3, missing) for new in elements: for old in old_elements: if old == new: self.assertTrue(old is new) missing -= 1 break else: self.assertTrue(False, "element '%s' is missing" % new.tag) self.assertEqual(0, missing) def test_element_base(self): el = self.etree.ElementBase() self.assertEqual('ElementBase', el.tag) root = self.etree.ElementBase() root.append(el) self.assertEqual('ElementBase', root[0].tag) def test_element_base_children(self): el = self.etree.ElementBase(etree.ElementBase()) self.assertEqual('ElementBase', el.tag) self.assertEqual(1, len(el)) self.assertEqual('ElementBase', el[0].tag) root = self.etree.ElementBase() root.append(el) self.assertEqual('ElementBase', root[0].tag) self.assertEqual('ElementBase', root[0][0].tag) def test_comment_base(self): el = self.etree.CommentBase('some text') self.assertEqual(self.etree.Comment, el.tag) self.assertEqual('some text', el.text) root = self.etree.Element('root') root.append(el) self.assertEqual('some text', root[0].text) def test_pi_base(self): el = self.etree.PIBase('the target', 'some text') self.assertEqual(self.etree.ProcessingInstruction, el.tag) self.assertEqual('some text', el.text) root = self.etree.Element('root') root.append(el) self.assertEqual('some text', root[0].text) class ClassLookupTestCase(HelperTestCase): """Test cases for different Element class lookup mechanisms. """ etree = etree def tearDown(self): etree.set_element_class_lookup() super(ClassLookupTestCase, self).tearDown() def test_namespace_lookup(self): class TestElement(etree.ElementBase): FIND_ME = "namespace class" lookup = etree.ElementNamespaceClassLookup() etree.set_element_class_lookup(lookup) ns = lookup.get_namespace("myNS") ns[None] = TestElement root = etree.XML(xml_str) self.assertEqual(root.FIND_ME, TestElement.FIND_ME) self.assertEqual(root[0].FIND_ME, TestElement.FIND_ME) self.assertFalse(hasattr(root[0][-1], 'FIND_ME')) def test_default_class_lookup(self): class TestElement(etree.ElementBase): FIND_ME = "default element" class TestComment(etree.CommentBase): FIND_ME = "default comment" class TestPI(etree.PIBase): FIND_ME = "default pi" parser = etree.XMLParser() lookup = etree.ElementDefaultClassLookup( element=TestElement, comment=TestComment, pi=TestPI) parser.set_element_class_lookup(lookup) root = etree.XML(_bytes(""" """), parser) self.assertEqual("default element", root.FIND_ME) self.assertEqual("default pi", root[0].FIND_ME) self.assertEqual("default comment", root[1].FIND_ME) def test_default_class_lookup_pull_parser(self): class TestElement(etree.ElementBase): FIND_ME = "default element" class TestComment(etree.CommentBase): FIND_ME = "default comment" class TestPI(etree.PIBase): FIND_ME = "default pi" parser = etree.XMLPullParser(events=('start', 'end', 'comment', 'pi')) lookup = etree.ElementDefaultClassLookup( element=TestElement, comment=TestComment, pi=TestPI) parser.set_element_class_lookup(lookup) events_seen = [] def add_events(events): for ev, el in events: events_seen.append((ev, el.FIND_ME)) parser.feed(""" """) add_events(parser.read_events()) parser.feed("") add_events(parser.read_events()) parser.feed("") root = parser.close() add_events(parser.read_events()) self.assertEqual([ ('start', "default element"), ('pi', "default pi"), ('comment', "default comment"), ('end', "default element"), ], events_seen) self.assertEqual("default element", root.FIND_ME) self.assertEqual("default pi", root[0].FIND_ME) self.assertEqual("default comment", root[1].FIND_ME) def test_evil_class_lookup(self): class MyLookup(etree.CustomElementClassLookup): def lookup(self, t, d, ns, name): if name == 'none': return None elif name == 'obj': return object() else: return etree.ElementBase parser = etree.XMLParser() parser.set_element_class_lookup(MyLookup()) root = etree.XML(_bytes(''), parser) self.assertEqual('none', root.tag) self.assertRaises( TypeError, etree.XML, _bytes(""), parser) root = etree.XML(_bytes(''), parser) self.assertEqual('root', root.tag) def test_class_lookup_type_mismatch(self): class MyLookup(etree.CustomElementClassLookup): def lookup(self, t, d, ns, name): if t == 'element': if name == 'root': return etree.ElementBase return etree.CommentBase elif t == 'comment': return etree.PIBase elif t == 'PI': return etree.EntityBase elif t == 'entity': return etree.ElementBase else: raise ValueError('got type %s' % t) parser = etree.XMLParser(resolve_entities=False) parser.set_element_class_lookup(MyLookup()) root = etree.XML(_bytes(''), parser) self.assertEqual('root', root.tag) self.assertEqual(etree.ElementBase, type(root)) root = etree.XML(_bytes(""), parser) self.assertRaises(TypeError, root.__getitem__, 0) root = etree.XML(_bytes(""), parser) self.assertRaises(TypeError, root.__getitem__, 0) root = etree.XML(_bytes(""), parser) self.assertRaises(TypeError, root.__getitem__, 0) root = etree.XML( _bytes(']>' '&myent;'), parser) self.assertRaises(TypeError, root.__getitem__, 0) root = etree.XML(_bytes(''), parser) self.assertEqual('root', root[0].tag) def test_attribute_based_lookup(self): class TestElement(etree.ElementBase): FIND_ME = "attribute_based" class_dict = {"A1" : TestElement} lookup = etree.AttributeBasedElementClassLookup( "a1", class_dict) etree.set_element_class_lookup(lookup) root = etree.XML(xml_str) self.assertFalse(hasattr(root, 'FIND_ME')) self.assertEqual(root[0].FIND_ME, TestElement.FIND_ME) self.assertFalse(hasattr(root[0][0], 'FIND_ME')) def test_custom_lookup(self): class TestElement(etree.ElementBase): FIND_ME = "custom" class MyLookup(etree.CustomElementClassLookup): def lookup(self, t, d, ns, name): if name == 'c1': return TestElement etree.set_element_class_lookup( MyLookup() ) root = etree.XML(xml_str) self.assertFalse(hasattr(root, 'FIND_ME')) self.assertEqual(root[0].FIND_ME, TestElement.FIND_ME) self.assertFalse(hasattr(root[0][1], 'FIND_ME')) def test_custom_lookup_ns_fallback(self): class TestElement1(etree.ElementBase): FIND_ME = "custom" class TestElement2(etree.ElementBase): FIND_ME = "nsclasses" class MyLookup(etree.CustomElementClassLookup): def lookup(self, t, d, ns, name): if name == 'c1': return TestElement1 lookup = etree.ElementNamespaceClassLookup( MyLookup() ) etree.set_element_class_lookup(lookup) ns = lookup.get_namespace("otherNS") ns[None] = TestElement2 root = etree.XML(xml_str) self.assertFalse(hasattr(root, 'FIND_ME')) self.assertEqual(root[0].FIND_ME, TestElement1.FIND_ME) self.assertFalse(hasattr(root[0][1], 'FIND_ME')) self.assertEqual(root[0][-1].FIND_ME, TestElement2.FIND_ME) def test_parser_based_lookup(self): class TestElement(etree.ElementBase): FIND_ME = "parser_based" lookup = etree.ParserBasedElementClassLookup() etree.set_element_class_lookup(lookup) class MyLookup(etree.CustomElementClassLookup): def lookup(self, t, d, ns, name): return TestElement parser = etree.XMLParser() parser.set_element_class_lookup( MyLookup() ) root = etree.parse(BytesIO(xml_str), parser).getroot() self.assertEqual(root.FIND_ME, TestElement.FIND_ME) self.assertEqual(root[0].FIND_ME, TestElement.FIND_ME) root = etree.parse(BytesIO(xml_str)).getroot() self.assertFalse(hasattr(root, 'FIND_ME')) self.assertFalse(hasattr(root[0], 'FIND_ME')) def test_class_lookup_reentry(self): XML = self.etree.XML class TestElement(etree.ElementBase): FIND_ME = "here" root = None class MyLookup(etree.CustomElementClassLookup): el = None def lookup(self, t, d, ns, name): if root is not None: # not in the parser if self.el is None and name == "a": self.el = [] self.el.append(root.find(name)) return TestElement parser = self.etree.XMLParser() parser.set_element_class_lookup(MyLookup()) root = XML(_bytes('AB'), parser) a = root[0] self.assertEqual(a.tag, "a") self.assertEqual(root[0].tag, "a") del a self.assertEqual(root[0].tag, "a") def test_lookup_without_fallback(self): class Lookup(etree.CustomElementClassLookup): def __init__(self): # no super call here, so no fallback is set pass def lookup(self, node_type, document, namespace, name): return Foo class Foo(etree.ElementBase): def custom(self): return "test" parser = self.etree.XMLParser() parser.set_element_class_lookup( Lookup() ) root = etree.XML('', parser) self.assertEqual("test", root.custom()) def test_suite(): suite = unittest.TestSuite() suite.addTests([unittest.makeSuite(ProxyTestCase)]) suite.addTests([unittest.makeSuite(ClassLookupTestCase)]) return suite if __name__ == '__main__': print('to test use test.py %s' % __file__)