lxml.tests.test_classlookup

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