1
2
3 """
4 Tests for thread usage in lxml.etree.
5 """
6
7 import unittest, threading, sys, os.path
8
9 this_dir = os.path.dirname(__file__)
10 if this_dir not in sys.path:
11 sys.path.insert(0, this_dir)
12
13 from common_imports import etree, HelperTestCase, BytesIO, _bytes
14
15 try:
16 from Queue import Queue
17 except ImportError:
18 from queue import Queue
19
42
44 XML = self.etree.XML
45 style = XML(_bytes('''\
46 <xsl:stylesheet version="1.0"
47 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
48 <xsl:template match="*">
49 <foo><xsl:copy><xsl:value-of select="/a/b/text()" /></xsl:copy></foo>
50 </xsl:template>
51 </xsl:stylesheet>'''))
52 st = etree.XSLT(style)
53
54 result = []
55
56 def run_thread():
57 root = XML(_bytes('<a><b>B</b><c>C</c></a>'))
58 result.append( st(root) )
59
60 self._run_thread(run_thread)
61 self.assertEquals('''\
62 <?xml version="1.0"?>
63 <foo><a>B</a></foo>
64 ''',
65 str(result[0]))
66
82
83 self._run_thread(run_thread)
84 self.assertEquals(_bytes('<a><b>B</b><c>C</c><foo><a>B</a></foo></a>'),
85 tostring(root))
86
88
89
90 XML = self.etree.XML
91 tostring = self.etree.tostring
92 style = self.etree.XSLT(XML(_bytes('''\
93 <xsl:stylesheet version="1.0"
94 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
95 <xsl:template match="*">
96 <root class="abc">
97 <xsl:copy-of select="@class" />
98 <xsl:attribute name="class">xyz</xsl:attribute>
99 </root>
100 </xsl:template>
101 </xsl:stylesheet>''')))
102
103 result = []
104 def run_thread():
105 root = XML(_bytes('<ROOT class="ABC" />'))
106 result.append( style(root).getroot() )
107
108 self._run_thread(run_thread)
109 self.assertEquals(_bytes('<root class="xyz"/>'),
110 tostring(result[0]))
111
113 XML = self.etree.XML
114 tostring = self.etree.tostring
115 root = XML(_bytes('<a><b>B</b><c>C</c></a>'))
116
117 stylesheets = []
118
119 def run_thread():
120 style = XML(_bytes('''\
121 <xsl:stylesheet
122 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
123 version="1.0">
124 <xsl:output method="xml" />
125 <xsl:template match="/">
126 <div id="test">
127 <xsl:apply-templates/>
128 </div>
129 </xsl:template>
130 </xsl:stylesheet>'''))
131 stylesheets.append( etree.XSLT(style) )
132
133 self._run_thread(run_thread)
134
135 st = stylesheets[0]
136 result = tostring( st(root) )
137
138 self.assertEquals(_bytes('<div id="test">BC</div>'),
139 result)
140
164
165 self.etree.clear_error_log()
166 threads = []
167 for thread_no in range(1, 10):
168 t = threading.Thread(target=parse_error_test,
169 args=(thread_no,))
170 threads.append(t)
171 t.start()
172
173 parse_error_test(0)
174
175 for t in threads:
176 t.join()
177
193
194 def run_parse():
195 thread_root = self.etree.parse(BytesIO(xml)).getroot()
196 result.append(thread_root[0])
197 result.append(thread_root[-1])
198
199 def run_move_main():
200 result.append(fragment[0])
201
202 def run_build():
203 result.append(
204 Element("{myns}foo", attrib={'{test}attr':'val'}))
205 SubElement(result, "{otherns}tasty")
206
207 def run_xslt():
208 style = XML(_bytes('''\
209 <xsl:stylesheet version="1.0"
210 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
211 <xsl:template match="*">
212 <xsl:copy><foo><xsl:value-of select="/a/b/text()" /></foo></xsl:copy>
213 </xsl:template>
214 </xsl:stylesheet>'''))
215 st = etree.XSLT(style)
216 result.append( st(root).getroot() )
217
218 for test in (run_XML, run_parse, run_move_main, run_xslt, run_build):
219 tostring(result)
220 self._run_thread(test)
221
222 self.assertEquals(
223 _bytes('<ns0:root xmlns:ns0="myns" att="someval"><b>B</b>'
224 '<c xmlns="test">C</c><b>B</b><c xmlns="test">C</c><tags/>'
225 '<a><foo>B</foo></a>'
226 '<ns0:foo xmlns:ns1="test" ns1:attr="val"/>'
227 '<ns1:tasty xmlns:ns1="otherns"/></ns0:root>'),
228 tostring(result))
229
230 def strip_first():
231 root = Element("newroot")
232 root.append(result[0])
233
234 while len(result):
235 self._run_thread(strip_first)
236
237 self.assertEquals(
238 _bytes('<ns0:root xmlns:ns0="myns" att="someval"/>'),
239 tostring(result))
240
242 XML = self.etree.XML
243 root = XML(_bytes('<root><a>A</a><b xmlns="test">B</b><c/></root>'))
244 child_count = len(root)
245 def testrun():
246 for i in range(10000):
247 el = root[i%child_count]
248 del el
249 threads = [ threading.Thread(target=testrun)
250 for _ in range(10) ]
251 for thread in threads:
252 thread.start()
253 for thread in threads:
254 thread.join()
255
257 XML = self.etree.XML
258
259 class TestElement(etree.ElementBase):
260 pass
261
262 class MyLookup(etree.CustomElementClassLookup):
263 repeat = range(100)
264 def lookup(self, t, d, ns, name):
265 count = 0
266 for i in self.repeat:
267
268 count += 1
269 return TestElement
270
271 parser = self.etree.XMLParser()
272 parser.set_element_class_lookup(MyLookup())
273
274 root = XML(_bytes('<root><a>A</a><b xmlns="test">B</b><c/></root>'),
275 parser)
276
277 child_count = len(root)
278 def testrun():
279 for i in range(1000):
280 el = root[i%child_count]
281 del el
282 threads = [ threading.Thread(target=testrun)
283 for _ in range(10) ]
284 for thread in threads:
285 thread.start()
286 for thread in threads:
287 thread.join()
288
289
291 """Threading tests based on a thread worker pipeline.
292 """
293 etree = etree
294 item_count = 20
295
296 - class Worker(threading.Thread):
297 - def __init__(self, in_queue, in_count, **kwargs):
298 threading.Thread.__init__(self)
299 self.in_queue = in_queue
300 self.in_count = in_count
301 self.out_queue = Queue(in_count)
302 self.__dict__.update(kwargs)
304 get, put = self.in_queue.get, self.out_queue.put
305 handle = self.handle
306 for _ in range(self.in_count):
307 put(handle(get()))
308
315 first = element[0]
316 element[:] = element[1:]
317 element.append(first)
318 return element
321 element[:] = element[::-1]
322 return element
331
332 xml = _bytes('''\
333 <xsl:stylesheet
334 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
335 version="1.0">
336 <xsl:output method="xml" />
337 <xsl:template match="/">
338 <div id="test">
339 <xsl:apply-templates/>
340 </div>
341 </xsl:template>
342 </xsl:stylesheet>''')
343
345 in_queue = Queue(item_count)
346 start = last = classes[0](in_queue, item_count, **kwargs)
347 start.setDaemon(True)
348 for worker_class in classes[1:]:
349 last = worker_class(last.out_queue, item_count, **kwargs)
350 last.setDaemon(True)
351 last.start()
352 return (in_queue, start, last)
353
355 item_count = self.item_count
356
357 in_queue, start, last = self._build_pipeline(
358 item_count,
359 self.ParseWorker,
360 self.RotateWorker,
361 self.ReverseWorker,
362 self.ParseAndExtendWorker,
363 self.SerialiseWorker,
364 xml = self.xml)
365
366
367 put = start.in_queue.put
368 for _ in range(item_count):
369 put(self.xml)
370
371
372 start.start()
373
374 last.join(60)
375 self.assertEquals(item_count, last.out_queue.qsize())
376
377 get = last.out_queue.get
378 results = [ get() for _ in range(item_count) ]
379
380 comparison = results[0]
381 for i, result in enumerate(results[1:]):
382 self.assertEquals(comparison, result)
383
385 item_count = self.item_count
386 XML = self.etree.XML
387
388 in_queue, start, last = self._build_pipeline(
389 item_count,
390 self.RotateWorker,
391 self.ReverseWorker,
392 self.ParseAndExtendWorker,
393 self.SerialiseWorker,
394 xml = self.xml)
395
396
397 put = start.in_queue.put
398 for _ in range(item_count):
399 put(XML(self.xml))
400
401
402 start.start()
403
404 last.join(60)
405 self.assertEquals(item_count, last.out_queue.qsize())
406
407 get = last.out_queue.get
408 results = [ get() for _ in range(item_count) ]
409
410 comparison = results[0]
411 for i, result in enumerate(results[1:]):
412 self.assertEquals(comparison, result)
413
414
420
421 if __name__ == '__main__':
422 print('to test use test.py %s' % __file__)
423