Package lxml :: Package tests :: Module test_classlookup

[hide private]
[frames] | no frames]

# Source Code for Module lxml.tests.test_classlookup

      1  # -*- coding: utf-8 -*-
      2
      3  """
      4  Tests for different Element class lookup mechanisms.
      5  """
      6
      7
      8  import unittest, os.path, sys, gc
      9
     10  this_dir = os.path.dirname(__file__)
     11  if this_dir not in sys.path:
     12      sys.path.insert(0, this_dir) # needed for Py3
     13
     14  from common_imports import etree, HelperTestCase, SillyFileLike, fileInTestDir
     15  from common_imports import canonicalize, _bytes, _str, BytesIO, StringIO
     16
     17  xml_str = _bytes('''\
     18  <root xmlns="myNS" xmlns:other="otherNS">
     19    <c1 a1="A1" a2="A2" other:a3="A3">
     20      <c2 a1="C2">0</c2>
     21      <c2>1</c2>
     22      <other:c2>2</other:c2>
     23    </c1>
     24  </root>''')
     25
     26
     27 -class ProxyTestCase(HelperTestCase):
     28      """Basic tests for element proxy behaviour.
     29      """
     30      etree = etree
     31
     32 -    def test_proxy_reuse(self):
     33          root = etree.XML('<a><b><c/></b></a>')
     34          b = root.find('b')
     35          self.assertTrue(b is root[0])
     36
     37 -    def test_proxy_reuse_after_gc(self):
     38          root = etree.XML('<a><b><c/></b></a>')
     39          b = root.find('b')
     40          self.assertTrue(self.etree.iselement(b))
     41          gc.collect()
     42          self.assertTrue(b is root[0])
     43
     44 -    def test_proxy_reuse_after_del_root(self):
     45          root = etree.XML('<a><b><c/></b></a>')
     46          b = root.find('b')
     47          self.assertTrue(self.etree.iselement(b))
     48          c = b.find('c')
     49          self.assertTrue(self.etree.iselement(c))
     50          del root
     51          gc.collect()
     52          self.assertTrue(b[0] is c)
     53
     54 -    def test_proxy_hashing(self):
     55          root = etree.XML('<a><b><c/></b></a>')
     56          old_elements = set(root.iter())
     57          elements = root.iter()
     58          del root
     59          gc.collect()
     60
     61          missing = len(old_elements)
     62          self.assertEqual(3, missing)
     63          for new in elements:
     64              for old in old_elements:
     65                  if old == new:
     66                      self.assertTrue(old is new)
     67                      missing -= 1
     68                      break
     69              else:
     70                  self.assertTrue(False, "element '%s' is missing" % new.tag)
     71          self.assertEqual(0, missing)
     72
     73 -    def test_element_base(self):
     74          el = self.etree.ElementBase()
     75          self.assertEqual('ElementBase', el.tag)
     76          root = self.etree.ElementBase()
     77          root.append(el)
     78          self.assertEqual('ElementBase', root[0].tag)
     79
     80 -    def test_element_base_children(self):
     81          el = self.etree.ElementBase(etree.ElementBase())
     82          self.assertEqual('ElementBase', el.tag)
     83          self.assertEqual(1, len(el))
     84          self.assertEqual('ElementBase', el[0].tag)
     85
     86          root = self.etree.ElementBase()
     87          root.append(el)
     88          self.assertEqual('ElementBase', root[0].tag)
     89          self.assertEqual('ElementBase', root[0][0].tag)
     90
     91 -    def test_comment_base(self):
     92          el = self.etree.CommentBase('some text')
     93          self.assertEqual(self.etree.Comment, el.tag)
     94          self.assertEqual('some text', el.text)
     95          root = self.etree.Element('root')
     96          root.append(el)
     97          self.assertEqual('some text', root[0].text)
     98
     99 -    def test_pi_base(self):
    100          el = self.etree.PIBase('the target', 'some text')
    101          self.assertEqual(self.etree.ProcessingInstruction, el.tag)
    102          self.assertEqual('some text', el.text)
    103          root = self.etree.Element('root')
    104          root.append(el)
    105          self.assertEqual('some text', root[0].text)
    106
    107
    108 -class ClassLookupTestCase(HelperTestCase):
    109      """Test cases for different Element class lookup mechanisms.
    110      """
    111      etree = etree
    112
    113 -    def tearDown(self):
    114          etree.set_element_class_lookup()
    115          super(ClassLookupTestCase, self).tearDown()
    116
    117 -    def test_namespace_lookup(self):
    118          class TestElement(etree.ElementBase):
    119              FIND_ME = "namespace class"
    120
    121          lookup = etree.ElementNamespaceClassLookup()
    122          etree.set_element_class_lookup(lookup)
    123
    124          ns = lookup.get_namespace("myNS")
    125          ns[None] = TestElement
    126
    127          root = etree.XML(xml_str)
    128          self.assertEqual(root.FIND_ME,
    129                            TestElement.FIND_ME)
    130          self.assertEqual(root[0].FIND_ME,
    131                            TestElement.FIND_ME)
    132          self.assertFalse(hasattr(root[0][-1], 'FIND_ME'))
    133
    134 -    def test_default_class_lookup(self):
    135          class TestElement(etree.ElementBase):
    136              FIND_ME = "default element"
    137          class TestComment(etree.CommentBase):
    138              FIND_ME = "default comment"
    139          class TestPI(etree.PIBase):
    140              FIND_ME = "default pi"
    141
    142          parser = etree.XMLParser()
    143
    144          lookup = etree.ElementDefaultClassLookup(
    145              element=TestElement, comment=TestComment, pi=TestPI)
    146          parser.set_element_class_lookup(lookup)
    147
    148          root = etree.XML(_bytes("""<?xml version='1.0'?>
    149          <root>
    150            <?myPI?>
    151            <!-- hi -->
    152          </root>
    153          """), parser)
    154
    155          self.assertEqual("default element", root.FIND_ME)
    156          self.assertEqual("default pi", root[0].FIND_ME)
    157          self.assertEqual("default comment", root[1].FIND_ME)
    158
    159 -    def test_evil_class_lookup(self):
    160          class MyLookup(etree.CustomElementClassLookup):
    161              def lookup(self, t, d, ns, name):
    162                  if name == 'none':
    163                      return None
    164                  elif name == 'obj':
    165                      return object()
    166                  else:
    167                      return etree.ElementBase
    168
    169          parser = etree.XMLParser()
    170          parser.set_element_class_lookup(MyLookup())
    171
    172          root = etree.XML(_bytes('<none/>'), parser)
    173          self.assertEqual('none', root.tag)
    174
    175          self.assertRaises(
    176              TypeError,
    177              etree.XML, _bytes("<obj />"), parser)
    178
    179          root = etree.XML(_bytes('<root/>'), parser)
    180          self.assertEqual('root', root.tag)
    181
    182 -    def test_class_lookup_type_mismatch(self):
    183          class MyLookup(etree.CustomElementClassLookup):
    184              def lookup(self, t, d, ns, name):
    185                  if t == 'element':
    186                      if name == 'root':
    187                          return etree.ElementBase
    188                      return etree.CommentBase
    189                  elif t == 'comment':
    190                      return etree.PIBase
    191                  elif t == 'PI':
    192                      return etree.EntityBase
    193                  elif t == 'entity':
    194                      return etree.ElementBase
    195                  else:
    196                      raise ValueError('got type %s' % t)
    197
    198          parser = etree.XMLParser(resolve_entities=False)
    199          parser.set_element_class_lookup(MyLookup())
    200
    201          root = etree.XML(_bytes('<root></root>'), parser)
    202          self.assertEqual('root', root.tag)
    203          self.assertEqual(etree.ElementBase, type(root))
    204
    205          root = etree.XML(_bytes("<root><test/></root>"), parser)
    206          self.assertRaises(TypeError, root.__getitem__, 0)
    207
    208          root = etree.XML(_bytes("<root><!-- test --></root>"), parser)
    209          self.assertRaises(TypeError, root.__getitem__, 0)
    210
    211          root = etree.XML(_bytes("<root><?test?></root>"), parser)
    212          self.assertRaises(TypeError, root.__getitem__, 0)
    213
    214          root = etree.XML(
    215              _bytes('<!DOCTYPE root [<!ENTITY myent "ent">]>'
    216                     '<root>&myent;</root>'),
    217              parser)
    218          self.assertRaises(TypeError, root.__getitem__, 0)
    219
    220          root = etree.XML(_bytes('<root><root/></root>'), parser)
    221          self.assertEqual('root', root[0].tag)
    222
    223 -    def test_attribute_based_lookup(self):
    224          class TestElement(etree.ElementBase):
    225              FIND_ME = "attribute_based"
    226
    227          class_dict = {"A1" : TestElement}
    228
    229          lookup = etree.AttributeBasedElementClassLookup(
    230              "a1", class_dict)
    231          etree.set_element_class_lookup(lookup)
    232
    233          root = etree.XML(xml_str)
    234          self.assertFalse(hasattr(root, 'FIND_ME'))
    235          self.assertEqual(root[0].FIND_ME,
    236                            TestElement.FIND_ME)
    237          self.assertFalse(hasattr(root[0][0], 'FIND_ME'))
    238
    239 -    def test_custom_lookup(self):
    240          class TestElement(etree.ElementBase):
    241              FIND_ME = "custom"
    242
    243          class MyLookup(etree.CustomElementClassLookup):
    244              def lookup(self, t, d, ns, name):
    245                  if name == 'c1':
    246                      return TestElement
    247
    248          etree.set_element_class_lookup( MyLookup() )
    249
    250          root = etree.XML(xml_str)
    251          self.assertFalse(hasattr(root, 'FIND_ME'))
    252          self.assertEqual(root[0].FIND_ME,
    253                            TestElement.FIND_ME)
    254          self.assertFalse(hasattr(root[0][1], 'FIND_ME'))
    255
    256 -    def test_custom_lookup_ns_fallback(self):
    257          class TestElement1(etree.ElementBase):
    258              FIND_ME = "custom"
    259
    260          class TestElement2(etree.ElementBase):
    261              FIND_ME = "nsclasses"
    262
    263          class MyLookup(etree.CustomElementClassLookup):
    264              def lookup(self, t, d, ns, name):
    265                  if name == 'c1':
    266                      return TestElement1
    267
    268          lookup = etree.ElementNamespaceClassLookup( MyLookup() )
    269          etree.set_element_class_lookup(lookup)
    270
    271          ns = lookup.get_namespace("otherNS")
    272          ns[None] = TestElement2
    273
    274          root = etree.XML(xml_str)
    275          self.assertFalse(hasattr(root, 'FIND_ME'))
    276          self.assertEqual(root[0].FIND_ME,
    277                            TestElement1.FIND_ME)
    278          self.assertFalse(hasattr(root[0][1], 'FIND_ME'))
    279          self.assertEqual(root[0][-1].FIND_ME,
    280                            TestElement2.FIND_ME)
    281
    282 -    def test_parser_based_lookup(self):
    283          class TestElement(etree.ElementBase):
    284              FIND_ME = "parser_based"
    285
    286          lookup = etree.ParserBasedElementClassLookup()
    287          etree.set_element_class_lookup(lookup)
    288
    289          class MyLookup(etree.CustomElementClassLookup):
    290              def lookup(self, t, d, ns, name):
    291                  return TestElement
    292
    293          parser = etree.XMLParser()
    294          parser.set_element_class_lookup( MyLookup() )
    295
    296          root = etree.parse(BytesIO(xml_str), parser).getroot()
    297          self.assertEqual(root.FIND_ME,
    298                            TestElement.FIND_ME)
    299          self.assertEqual(root[0].FIND_ME,
    300                            TestElement.FIND_ME)
    301
    302          root = etree.parse(BytesIO(xml_str)).getroot()
    303          self.assertFalse(hasattr(root, 'FIND_ME'))
    304          self.assertFalse(hasattr(root[0], 'FIND_ME'))
    305
    306 -    def test_class_lookup_reentry(self):
    307          XML = self.etree.XML
    308
    309          class TestElement(etree.ElementBase):
    310              FIND_ME = "here"
    311
    312          root = None
    313          class MyLookup(etree.CustomElementClassLookup):
    314              el = None
    315              def lookup(self, t, d, ns, name):
    316                  if root is not None: # not in the parser
    317                      if self.el is None and name == "a":
    318                          self.el = []
    319                          self.el.append(root.find(name))
    320                  return TestElement
    321
    322          parser = self.etree.XMLParser()
    323          parser.set_element_class_lookup(MyLookup())
    324
    325          root = XML(_bytes('<root><a>A</a><b xmlns="test">B</b></root>'),
    326                     parser)
    327
    328          a = root[0]
    329          self.assertEqual(a.tag, "a")
    330          self.assertEqual(root[0].tag, "a")
    331          del a
    332          self.assertEqual(root[0].tag, "a")
    333
    334 -    def test_lookup_without_fallback(self):
    335          class Lookup(etree.CustomElementClassLookup):
    336               def __init__(self):
    337                   # no super call here, so no fallback is set
    338                   pass
    339
    340               def lookup(self, node_type, document, namespace, name):
    341                   return Foo
    342
    343          class Foo(etree.ElementBase):
    344               def custom(self):
    345                   return "test"
    346
    347          parser = self.etree.XMLParser()
    348          parser.set_element_class_lookup( Lookup() )
    349
    350          root = etree.XML('<foo/>', parser)
    351
    352          self.assertEqual("test", root.custom())
    353
    354
    355 -def test_suite():
    356      suite = unittest.TestSuite()
    357      suite.addTests([unittest.makeSuite(ProxyTestCase)])
    358      suite.addTests([unittest.makeSuite(ClassLookupTestCase)])
    359      return suite
    360
    361  if __name__ == '__main__':
    362      print('to test use test.py %s' % __file__)
    363