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_default_class_lookup_pull_parser(self):
    160          class TestElement(etree.ElementBase):
    161              FIND_ME = "default element"
    162          class TestComment(etree.CommentBase):
    163              FIND_ME = "default comment"
    164          class TestPI(etree.PIBase):
    165              FIND_ME = "default pi"
    166
    167          parser = etree.XMLPullParser(events=('start', 'end', 'comment', 'pi'))
    168          lookup = etree.ElementDefaultClassLookup(
    169              element=TestElement, comment=TestComment, pi=TestPI)
    170          parser.set_element_class_lookup(lookup)
    171
    172          events_seen = []
    173
    174          def add_events(events):
    175              for ev, el in events:
    176                  events_seen.append((ev, el.FIND_ME))
    177
    178          parser.feed("""<?xml version='1.0'?>
    179          <root>
    180            <?myPI?>
    181          """)
    182          add_events(parser.read_events())
    183
    184          parser.feed("<!-- hi -->")
    185          add_events(parser.read_events())
    186
    187          parser.feed("</root>")
    188          root = parser.close()
    189          add_events(parser.read_events())
    190
    191          self.assertEqual([
    192              ('start',   "default element"),
    193              ('pi',      "default pi"),
    194              ('comment', "default comment"),
    195              ('end',     "default element"),
    196          ], events_seen)
    197
    198          self.assertEqual("default element", root.FIND_ME)
    199          self.assertEqual("default pi", root[0].FIND_ME)
    200          self.assertEqual("default comment", root[1].FIND_ME)
    201
    202 -    def test_evil_class_lookup(self):
    203          class MyLookup(etree.CustomElementClassLookup):
    204              def lookup(self, t, d, ns, name):
    205                  if name == 'none':
    206                      return None
    207                  elif name == 'obj':
    208                      return object()
    209                  else:
    210                      return etree.ElementBase
    211
    212          parser = etree.XMLParser()
    213          parser.set_element_class_lookup(MyLookup())
    214
    215          root = etree.XML(_bytes('<none/>'), parser)
    216          self.assertEqual('none', root.tag)
    217
    218          self.assertRaises(
    219              TypeError,
    220              etree.XML, _bytes("<obj />"), parser)
    221
    222          root = etree.XML(_bytes('<root/>'), parser)
    223          self.assertEqual('root', root.tag)
    224
    225 -    def test_class_lookup_type_mismatch(self):
    226          class MyLookup(etree.CustomElementClassLookup):
    227              def lookup(self, t, d, ns, name):
    228                  if t == 'element':
    229                      if name == 'root':
    230                          return etree.ElementBase
    231                      return etree.CommentBase
    232                  elif t == 'comment':
    233                      return etree.PIBase
    234                  elif t == 'PI':
    235                      return etree.EntityBase
    236                  elif t == 'entity':
    237                      return etree.ElementBase
    238                  else:
    239                      raise ValueError('got type %s' % t)
    240
    241          parser = etree.XMLParser(resolve_entities=False)
    242          parser.set_element_class_lookup(MyLookup())
    243
    244          root = etree.XML(_bytes('<root></root>'), parser)
    245          self.assertEqual('root', root.tag)
    246          self.assertEqual(etree.ElementBase, type(root))
    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(_bytes("<root><?test?></root>"), parser)
    255          self.assertRaises(TypeError, root.__getitem__, 0)
    256
    257          root = etree.XML(
    258              _bytes('<!DOCTYPE root [<!ENTITY myent "ent">]>'
    259                     '<root>&myent;</root>'),
    260              parser)
    261          self.assertRaises(TypeError, root.__getitem__, 0)
    262
    263          root = etree.XML(_bytes('<root><root/></root>'), parser)
    264          self.assertEqual('root', root[0].tag)
    265
    266 -    def test_attribute_based_lookup(self):
    267          class TestElement(etree.ElementBase):
    268              FIND_ME = "attribute_based"
    269
    270          class_dict = {"A1" : TestElement}
    271
    272          lookup = etree.AttributeBasedElementClassLookup(
    273              "a1", class_dict)
    274          etree.set_element_class_lookup(lookup)
    275
    276          root = etree.XML(xml_str)
    277          self.assertFalse(hasattr(root, 'FIND_ME'))
    278          self.assertEqual(root[0].FIND_ME,
    279                            TestElement.FIND_ME)
    280          self.assertFalse(hasattr(root[0][0], 'FIND_ME'))
    281
    282 -    def test_custom_lookup(self):
    283          class TestElement(etree.ElementBase):
    284              FIND_ME = "custom"
    285
    286          class MyLookup(etree.CustomElementClassLookup):
    287              def lookup(self, t, d, ns, name):
    288                  if name == 'c1':
    289                      return TestElement
    290
    291          etree.set_element_class_lookup( MyLookup() )
    292
    293          root = etree.XML(xml_str)
    294          self.assertFalse(hasattr(root, 'FIND_ME'))
    295          self.assertEqual(root[0].FIND_ME,
    296                            TestElement.FIND_ME)
    297          self.assertFalse(hasattr(root[0][1], 'FIND_ME'))
    298
    299 -    def test_custom_lookup_ns_fallback(self):
    300          class TestElement1(etree.ElementBase):
    301              FIND_ME = "custom"
    302
    303          class TestElement2(etree.ElementBase):
    304              FIND_ME = "nsclasses"
    305
    306          class MyLookup(etree.CustomElementClassLookup):
    307              def lookup(self, t, d, ns, name):
    308                  if name == 'c1':
    309                      return TestElement1
    310
    311          lookup = etree.ElementNamespaceClassLookup( MyLookup() )
    312          etree.set_element_class_lookup(lookup)
    313
    314          ns = lookup.get_namespace("otherNS")
    315          ns[None] = TestElement2
    316
    317          root = etree.XML(xml_str)
    318          self.assertFalse(hasattr(root, 'FIND_ME'))
    319          self.assertEqual(root[0].FIND_ME,
    320                            TestElement1.FIND_ME)
    321          self.assertFalse(hasattr(root[0][1], 'FIND_ME'))
    322          self.assertEqual(root[0][-1].FIND_ME,
    323                            TestElement2.FIND_ME)
    324
    325 -    def test_parser_based_lookup(self):
    326          class TestElement(etree.ElementBase):
    327              FIND_ME = "parser_based"
    328
    329          lookup = etree.ParserBasedElementClassLookup()
    330          etree.set_element_class_lookup(lookup)
    331
    332          class MyLookup(etree.CustomElementClassLookup):
    333              def lookup(self, t, d, ns, name):
    334                  return TestElement
    335
    336          parser = etree.XMLParser()
    337          parser.set_element_class_lookup( MyLookup() )
    338
    339          root = etree.parse(BytesIO(xml_str), parser).getroot()
    340          self.assertEqual(root.FIND_ME,
    341                            TestElement.FIND_ME)
    342          self.assertEqual(root[0].FIND_ME,
    343                            TestElement.FIND_ME)
    344
    345          root = etree.parse(BytesIO(xml_str)).getroot()
    346          self.assertFalse(hasattr(root, 'FIND_ME'))
    347          self.assertFalse(hasattr(root[0], 'FIND_ME'))
    348
    349 -    def test_class_lookup_reentry(self):
    350          XML = self.etree.XML
    351
    352          class TestElement(etree.ElementBase):
    353              FIND_ME = "here"
    354
    355          root = None
    356          class MyLookup(etree.CustomElementClassLookup):
    357              el = None
    358              def lookup(self, t, d, ns, name):
    359                  if root is not None: # not in the parser
    360                      if self.el is None and name == "a":
    361                          self.el = []
    362                          self.el.append(root.find(name))
    363                  return TestElement
    364
    365          parser = self.etree.XMLParser()
    366          parser.set_element_class_lookup(MyLookup())
    367
    368          root = XML(_bytes('<root><a>A</a><b xmlns="test">B</b></root>'),
    369                     parser)
    370
    371          a = root[0]
    372          self.assertEqual(a.tag, "a")
    373          self.assertEqual(root[0].tag, "a")
    374          del a
    375          self.assertEqual(root[0].tag, "a")
    376
    377 -    def test_lookup_without_fallback(self):
    378          class Lookup(etree.CustomElementClassLookup):
    379               def __init__(self):
    380                   # no super call here, so no fallback is set
    381                   pass
    382
    383               def lookup(self, node_type, document, namespace, name):
    384                   return Foo
    385
    386          class Foo(etree.ElementBase):
    387               def custom(self):
    388                   return "test"
    389
    390          parser = self.etree.XMLParser()
    391          parser.set_element_class_lookup( Lookup() )
    392
    393          root = etree.XML('<foo/>', parser)
    394
    395          self.assertEqual("test", root.custom())
    396
    397
    398 -def test_suite():
    399      suite = unittest.TestSuite()
    400      suite.addTests([unittest.makeSuite(ProxyTestCase)])
    401      suite.addTests([unittest.makeSuite(ClassLookupTestCase)])
    402      return suite
    403
    404  if __name__ == '__main__':
    405      print('to test use test.py %s' % __file__)
    406