1   
  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)  
 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   
 28      """Basic tests for element proxy behaviour. 
 29      """ 
 30      etree = etree 
 31   
 36   
 43   
 53   
 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   
 79   
 90   
 98   
 106   
107   
133   
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   
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   
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   
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   
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   
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   
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   
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   
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:  
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   
378          class Lookup(etree.CustomElementClassLookup): 
379               def __init__(self): 
380                    
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   
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