[Lxml-checkins] r54508 - in lxml/branch/lxml-2.0/src/lxml: . tests
scoder at codespeak.net
scoder at codespeak.net
Tue May 6 23:25:42 CEST 2008
Author: scoder
Date: Tue May 6 23:25:42 2008
New Revision: 54508
Modified:
lxml/branch/lxml-2.0/src/lxml/apihelpers.pxi
lxml/branch/lxml-2.0/src/lxml/lxml.etree.pyx
lxml/branch/lxml-2.0/src/lxml/proxy.pxi
lxml/branch/lxml-2.0/src/lxml/tests/test_threading.py
lxml/branch/lxml-2.0/src/lxml/tree.pxd
Log:
large trunk merge of threading fix
Modified: lxml/branch/lxml-2.0/src/lxml/apihelpers.pxi
==============================================================================
--- lxml/branch/lxml-2.0/src/lxml/apihelpers.pxi (original)
+++ lxml/branch/lxml-2.0/src/lxml/apihelpers.pxi Tue May 6 23:25:42 2008
@@ -643,7 +643,7 @@
_moveTail(c_next, c_node)
if not attemptDeallocation(c_node):
# make namespaces absolute
- moveNodeToDocument(doc, c_node)
+ moveNodeToDocument(doc, c_node.doc, c_node)
return 0
cdef void _moveTail(xmlNode* c_tail, xmlNode* c_target):
@@ -709,6 +709,7 @@
"""
cdef xmlNode* c_orig_neighbour
cdef xmlNode* c_next
+ cdef xmlDoc* c_source_doc
cdef _Element element
cdef Py_ssize_t seqlength, i, c
cdef _node_to_node_function next_element
@@ -792,12 +793,13 @@
for element in elements:
assert element is not None, "Node must not be None"
# move element and tail over
+ c_source_doc = element._c_node.doc
c_next = element._c_node.next
tree.xmlAddPrevSibling(c_node, element._c_node)
_moveTail(c_next, element._c_node)
# integrate element into new document
- moveNodeToDocument(parent._doc, element._c_node)
+ moveNodeToDocument(parent._doc, c_source_doc, element._c_node)
# stop at the end of the slice
if slicelength > 0:
@@ -827,7 +829,9 @@
"""
cdef xmlNode* c_next
cdef xmlNode* c_node
+ cdef xmlDoc* c_source_doc
c_node = child._c_node
+ c_source_doc = c_node.doc
# store possible text node
c_next = c_node.next
# move node itself
@@ -836,7 +840,7 @@
_moveTail(c_next, c_node)
# uh oh, elements may be pointing to different doc when
# parent element has moved; change them too..
- moveNodeToDocument(parent._doc, c_node)
+ moveNodeToDocument(parent._doc, c_source_doc, c_node)
cdef int _prependChild(_Element parent, _Element child) except -1:
"""Prepend a new child to a parent element.
@@ -844,7 +848,9 @@
cdef xmlNode* c_next
cdef xmlNode* c_child
cdef xmlNode* c_node
+ cdef xmlDoc* c_source_doc
c_node = child._c_node
+ c_source_doc = c_node.doc
# store possible text node
c_next = c_node.next
# move node itself
@@ -857,14 +863,16 @@
_moveTail(c_next, c_node)
# uh oh, elements may be pointing to different doc when
# parent element has moved; change them too..
- moveNodeToDocument(parent._doc, c_node)
+ moveNodeToDocument(parent._doc, c_source_doc, c_node)
cdef int _appendSibling(_Element element, _Element sibling) except -1:
"""Append a new child to a parent element.
"""
cdef xmlNode* c_next
cdef xmlNode* c_node
+ cdef xmlDoc* c_source_doc
c_node = sibling._c_node
+ c_source_doc = c_node.doc
# store possible text node
c_next = c_node.next
# move node itself
@@ -872,14 +880,16 @@
_moveTail(c_next, c_node)
# uh oh, elements may be pointing to different doc when
# parent element has moved; change them too..
- moveNodeToDocument(element._doc, c_node)
+ moveNodeToDocument(element._doc, c_source_doc, c_node)
cdef int _prependSibling(_Element element, _Element sibling) except -1:
"""Append a new child to a parent element.
"""
cdef xmlNode* c_next
cdef xmlNode* c_node
+ cdef xmlDoc* c_source_doc
c_node = sibling._c_node
+ c_source_doc = c_node.doc
# store possible text node
c_next = c_node.next
# move node itself
@@ -887,7 +897,7 @@
_moveTail(c_next, c_node)
# uh oh, elements may be pointing to different doc when
# parent element has moved; change them too..
- moveNodeToDocument(element._doc, c_node)
+ moveNodeToDocument(element._doc, c_source_doc, c_node)
cdef int isutf8(char* s):
cdef char c
Modified: lxml/branch/lxml-2.0/src/lxml/lxml.etree.pyx
==============================================================================
--- lxml/branch/lxml-2.0/src/lxml/lxml.etree.pyx (original)
+++ lxml/branch/lxml-2.0/src/lxml/lxml.etree.pyx Tue May 6 23:25:42 2008
@@ -557,6 +557,7 @@
"""
cdef xmlNode* c_node
cdef xmlNode* c_next
+ cdef xmlDoc* c_source_doc
cdef _Element element
cdef bint left_to_right
cdef Py_ssize_t slicelength, step
@@ -578,13 +579,14 @@
c_node = _findChild(self._c_node, x)
if c_node is NULL:
raise IndexError("list index out of range")
+ c_source_doc = element._c_node.doc
c_next = element._c_node.next
_removeText(c_node.next)
tree.xmlReplaceNode(c_node, element._c_node)
_moveTail(c_next, element._c_node)
- moveNodeToDocument(self._doc, element._c_node)
+ moveNodeToDocument(self._doc, c_source_doc, element._c_node)
if not attemptDeallocation(c_node):
- moveNodeToDocument(self._doc, c_node)
+ moveNodeToDocument(self._doc, c_node.doc, c_node)
def __delitem__(self, x):
"""__delitem__(self, x)
@@ -731,14 +733,16 @@
"""
cdef xmlNode* c_node
cdef xmlNode* c_next
+ cdef xmlDoc* c_source_doc
c_node = _findChild(self._c_node, index)
if c_node is NULL:
_appendChild(self, element)
return
+ c_source_doc = c_node.doc
c_next = element._c_node.next
tree.xmlAddPrevSibling(c_node, element._c_node)
_moveTail(c_next, element._c_node)
- moveNodeToDocument(self._doc, element._c_node)
+ moveNodeToDocument(self._doc, c_source_doc, element._c_node)
def remove(self, _Element element not None):
"""remove(self, element)
@@ -756,7 +760,7 @@
tree.xmlUnlinkNode(c_node)
_moveTail(c_next, c_node)
# fix namespace declarations
- moveNodeToDocument(self._doc, c_node)
+ moveNodeToDocument(self._doc, c_node.doc, c_node)
def replace(self, _Element old_element not None,
_Element new_element not None):
@@ -768,18 +772,20 @@
cdef xmlNode* c_old_next
cdef xmlNode* c_new_node
cdef xmlNode* c_new_next
+ cdef xmlDoc* c_source_doc
c_old_node = old_element._c_node
if c_old_node.parent is not self._c_node:
raise ValueError("Element is not a child of this node.")
c_old_next = c_old_node.next
c_new_node = new_element._c_node
c_new_next = c_new_node.next
+ c_source_doc = c_new_node.doc
tree.xmlReplaceNode(c_old_node, c_new_node)
_moveTail(c_new_next, c_new_node)
_moveTail(c_old_next, c_old_node)
- moveNodeToDocument(self._doc, c_new_node)
+ moveNodeToDocument(self._doc, c_source_doc, c_new_node)
# fix namespace declarations
- moveNodeToDocument(self._doc, c_old_node)
+ moveNodeToDocument(self._doc, c_old_node.doc, c_old_node)
# PROPERTIES
property tag:
Modified: lxml/branch/lxml-2.0/src/lxml/proxy.pxi
==============================================================================
--- lxml/branch/lxml-2.0/src/lxml/proxy.pxi (original)
+++ lxml/branch/lxml-2.0/src/lxml/proxy.pxi Tue May 6 23:25:42 2008
@@ -4,7 +4,7 @@
# structure of the respective node to avoid multiple instantiation of
# the Python class
-cdef _Element getProxy(xmlNode* c_node):
+cdef inline _Element getProxy(xmlNode* c_node):
"""Get a proxy for a given node.
"""
#print "getProxy for:", <int>c_node
@@ -13,10 +13,10 @@
else:
return None
-cdef int hasProxy(xmlNode* c_node):
+cdef inline int hasProxy(xmlNode* c_node):
return c_node._private is not NULL
-cdef int _registerProxy(_Element proxy) except -1:
+cdef inline int _registerProxy(_Element proxy) except -1:
"""Register a proxy and type for the node it's proxying for.
"""
cdef xmlNode* c_node
@@ -31,7 +31,7 @@
proxy._gc_doc = <python.PyObject*>proxy._doc
python.Py_INCREF(proxy._doc)
-cdef int _unregisterProxy(_Element proxy) except -1:
+cdef inline int _unregisterProxy(_Element proxy) except -1:
"""Unregister a proxy for the node it's proxying for.
"""
cdef xmlNode* c_node
@@ -40,12 +40,24 @@
c_node._private = NULL
return 0
-cdef void _releaseProxy(_Element proxy):
+cdef inline void _releaseProxy(_Element proxy):
"""An additional DECREF for the document.
"""
python.Py_XDECREF(proxy._gc_doc)
proxy._gc_doc = NULL
+cdef inline void _updateProxyDocument(xmlNode* c_node, _Document doc):
+ """Replace the document reference of a proxy.
+
+ This may deallocate the original document of the proxy!
+ """
+ cdef _Element element = <_Element>c_node._private
+ if element._doc is not doc:
+ python.Py_INCREF(doc)
+ python.Py_DECREF(element._doc)
+ element._doc = doc
+ element._gc_doc = <python.PyObject*>doc
+
################################################################################
# temporarily make a node the root node of its document
@@ -196,7 +208,74 @@
c_new_ns = c_new_ns.next
c_parent = c_parent.parent
-cdef int moveNodeToDocument(_Document doc, xmlNode* c_element) except -1:
+ctypedef struct _nscache:
+ xmlNs** new
+ xmlNs** old
+ cstd.size_t size
+ cstd.size_t last
+
+cdef int _growNsCache(_nscache* c_ns_cache) except -1:
+ cdef xmlNs** c_ns_ptr
+ if c_ns_cache.size == 0:
+ c_ns_cache.size = 20
+ else:
+ c_ns_cache.size *= 2
+ c_ns_ptr = <xmlNs**> cstd.realloc(
+ c_ns_cache.new, c_ns_cache.size * sizeof(xmlNs*))
+ if c_ns_ptr is not NULL:
+ c_ns_cache.new = c_ns_ptr
+ c_ns_ptr = <xmlNs**> cstd.realloc(
+ c_ns_cache.old, c_ns_cache.size * sizeof(xmlNs*))
+ if c_ns_ptr is not NULL:
+ c_ns_cache.old = c_ns_ptr
+ else:
+ cstd.free(c_ns_cache.new)
+ cstd.free(c_ns_cache.old)
+ python.PyErr_NoMemory()
+ return -1
+ return 0
+
+cdef inline int _appendToNsCache(_nscache* c_ns_cache,
+ xmlNs* c_old_ns, xmlNs* c_new_ns) except -1:
+ if c_ns_cache.last >= c_ns_cache.size:
+ _growNsCache(c_ns_cache)
+ c_ns_cache.old[c_ns_cache.last] = c_old_ns
+ c_ns_cache.new[c_ns_cache.last] = c_new_ns
+ c_ns_cache.last += 1
+
+cdef int _stripRedundantNamespaceDeclarations(
+ xmlNode* c_element, _nscache* c_ns_cache, xmlNs** c_del_ns_list) except -1:
+ """Removes namespace declarations from an element that are already
+ defined in its parents. Does not free the xmlNs's, just prepends
+ them to the c_del_ns_list.
+ """
+ cdef xmlNs* c_ns
+ cdef xmlNs* c_ns_next
+ cdef xmlNs** c_nsdef
+ # use a xmlNs** to handle assignments to "c_element.nsDef" correctly
+ c_nsdef = &c_element.nsDef
+ while c_nsdef[0] is not NULL:
+ c_ns = tree.xmlSearchNsByHref(
+ c_element.doc, c_element.parent, c_nsdef[0].href)
+ if c_ns is NULL:
+ # new namespace href => keep and cache the ns declaration
+ _appendToNsCache(c_ns_cache, c_nsdef[0], c_nsdef[0])
+ c_nsdef = &c_nsdef[0].next
+ else:
+ # known namespace href => strip the ns
+ if c_ns is tree.xmlSearchNs(c_element.doc, c_element.parent,
+ c_ns.prefix):
+ # prefix is not shadowed by parents => ns is reusable
+ _appendToNsCache(c_ns_cache, c_nsdef[0], c_ns)
+ # cut out c_nsdef.next and prepend it to garbage chain
+ c_ns_next = c_nsdef[0].next
+ c_nsdef[0].next = c_del_ns_list[0]
+ c_del_ns_list[0] = c_nsdef[0]
+ c_nsdef[0] = c_ns_next
+ return 0
+
+cdef int moveNodeToDocument(_Document doc, xmlDoc* c_source_doc,
+ xmlNode* c_element) except -1:
"""Fix the xmlNs pointers of a node and its subtree that were moved.
Mainly copied from libxml2's xmlReconciliateNs(). Expects libxml2 doc
@@ -213,7 +292,11 @@
prefix). If a namespace is unknown, declare a new one on the
node.
- 3) Set the Document reference to the new Document (if different).
+ 3) Reassign the names of tags and attribute from the dict of the
+ target document *iff* it is different from the dict used in the
+ source subtree.
+
+ 4) Set the Document reference to the new Document (if different).
This is done on backtracking to keep the original Document
alive as long as possible, until all its elements are updated.
@@ -221,96 +304,66 @@
step 1), but freed only after the complete subtree was traversed
and all occurrences were replaced by tree-internal pointers.
"""
- cdef _Element element
- cdef xmlDoc* c_doc
cdef xmlNode* c_start_node
cdef xmlNode* c_node
- cdef xmlNs** c_ns_ptr
- cdef xmlNs** c_ns_new_cache
- cdef xmlNs** c_ns_old_cache
+ cdef char* c_name
+ cdef _nscache c_ns_cache
cdef xmlNs* c_ns
cdef xmlNs* c_ns_next
cdef xmlNs* c_nsdef
- cdef xmlNs* c_new_ns
- cdef xmlNs* c_del_ns
- cdef cstd.size_t i, c_cache_size, c_cache_last
+ cdef xmlNs* c_del_ns_list
+ cdef cstd.size_t i
+ cdef tree.xmlDict* c_dict
if not tree._isElementOrXInclude(c_element):
return 0
- c_doc = c_element.doc
+ # we need to copy the names of tags and attributes iff the element
+ # is based on a different libxml2 tag name dictionary
+ if doc._c_doc.dict is not c_source_doc.dict:
+ c_dict = doc._c_doc.dict
+ else:
+ c_dict = NULL
+
c_start_node = c_element
- c_ns_new_cache = NULL
- c_ns_old_cache = NULL
- c_cache_size = 0
- c_cache_last = 0
- c_del_ns = NULL
+ c_del_ns_list = NULL
+
+ c_ns_cache.new = NULL
+ c_ns_cache.old = NULL
+ c_ns_cache.size = 0
+ c_ns_cache.last = 0
while c_element is not NULL:
# 1) cut out namespaces defined here that are already known by
# the ancestors
- c_nsdef = c_element.nsDef
- if c_nsdef is not NULL:
- # start with second nsdef to keep c_element.nsDef for now
- while c_nsdef.next is not NULL:
- if c_nsdef.next is c_element.ns:
- c_nsdef = c_nsdef.next
- continue
- c_ns = tree.xmlSearchNsByHref(
- c_element.doc, c_element.parent, c_nsdef.next.href)
- if c_ns is NULL:
- c_nsdef = c_nsdef.next
- continue
- # cut out c_nsdef.next and prepend it to garbage chain
- c_ns_next = c_nsdef.next.next
- c_nsdef.next.next = c_del_ns
- c_del_ns = c_nsdef.next
- c_nsdef.next = c_ns_next
- # now handle c_element.nsDef
- c_ns = tree.xmlSearchNsByHref(
- c_element.doc, c_element.parent, c_element.nsDef.href)
- if c_ns is not NULL:
- c_ns_next = c_element.nsDef.next
- c_element.nsDef.next = c_del_ns
- c_del_ns = c_element.nsDef
- c_element.nsDef = c_ns_next
+ if c_element.nsDef is not NULL:
+ _stripRedundantNamespaceDeclarations(
+ c_element, &c_ns_cache, &c_del_ns_list)
- # 2) make sure the namespace of an element and its attributes
- # is declared in this document (i.e. the node or its parents)
+ # 2) make sure the namespaces of an element and its attributes
+ # are declared in this document (i.e. on the node or its parents)
c_node = c_element
while c_node is not NULL:
if c_node.ns is not NULL:
- for i from 0 <= i < c_cache_last:
- if c_node.ns is c_ns_old_cache[i]:
- c_node.ns = c_ns_new_cache[i]
+ for i from 0 <= i < c_ns_cache.last:
+ if c_node.ns is c_ns_cache.old[i]:
+ c_node.ns = c_ns_cache.new[i]
break
else:
# not in cache => find a replacement from this document
- c_new_ns = doc._findOrBuildNodeNs(
+ c_ns = doc._findOrBuildNodeNs(
c_element, c_node.ns.href, c_node.ns.prefix)
- if c_cache_last >= c_cache_size:
- # must resize cache
- if c_cache_size == 0:
- c_cache_size = 20
- else:
- c_cache_size *= 2
- c_ns_ptr = <xmlNs**> cstd.realloc(
- c_ns_new_cache, c_cache_size * sizeof(xmlNs*))
- if c_ns_ptr is not NULL:
- c_ns_new_cache = c_ns_ptr
- c_ns_ptr = <xmlNs**> cstd.realloc(
- c_ns_old_cache, c_cache_size * sizeof(xmlNs*))
- if c_ns_ptr is not NULL:
- c_ns_old_cache = c_ns_ptr
- else:
- cstd.free(c_ns_new_cache)
- cstd.free(c_ns_old_cache)
- python.PyErr_NoMemory()
- return -1
- c_ns_new_cache[c_cache_last] = c_new_ns
- c_ns_old_cache[c_cache_last] = c_node.ns
- c_cache_last += 1
- c_node.ns = c_new_ns
+ _appendToNsCache(&c_ns_cache, c_node.ns, c_ns)
+ c_node.ns = c_ns
+
+ # 3) re-assign names from the target dict
+ if c_dict is not NULL:
+ c_name = tree.xmlDictLookup(c_dict, c_node.name, -1)
+ # c_name can be NULL on memory error, but we don't
+ # handle that here
+ if c_name is not NULL:
+ c_node.name = c_name
+
if c_node is c_element:
# after the element, continue with its attributes
c_node = <xmlNode*>c_element.properties
@@ -326,14 +379,9 @@
if c_node is NULL:
# no children => back off and continue with siblings and parents
- # 3) fix _Document reference (may dealloc the original document!)
+ # 4) fix _Document reference (may dealloc the original document!)
if c_element._private is not NULL:
- element = <_Element>c_element._private
- if element._doc is not doc:
- python.Py_INCREF(doc)
- python.Py_DECREF(element._doc)
- element._doc = doc
- element._gc_doc = <python.PyObject*>doc
+ _updateProxyDocument(c_element, doc)
if c_element is c_start_node:
break # all done
@@ -349,14 +397,9 @@
if c_element is NULL or not tree._isElementOrXInclude(c_element):
break
- # 3) fix _Document reference (may dealloc the original document!)
+ # 4) fix _Document reference (may dealloc the original document!)
if c_element._private is not NULL:
- element = <_Element>c_element._private
- if element._doc is not doc:
- python.Py_INCREF(doc)
- python.Py_DECREF(element._doc)
- element._doc = doc
- element._gc_doc = <python.PyObject*>doc
+ _updateProxyDocument(c_element, doc)
if c_element is c_start_node:
break
@@ -370,13 +413,13 @@
c_element = c_node
# free now unused namespace declarations
- if c_del_ns is not NULL:
- tree.xmlFreeNsList(c_del_ns)
+ if c_del_ns_list is not NULL:
+ tree.xmlFreeNsList(c_del_ns_list)
# cleanup
- if c_ns_new_cache is not NULL:
- cstd.free(c_ns_new_cache)
- if c_ns_old_cache is not NULL:
- cstd.free(c_ns_old_cache)
+ if c_ns_cache.new is not NULL:
+ cstd.free(c_ns_cache.new)
+ if c_ns_cache.old is not NULL:
+ cstd.free(c_ns_cache.old)
return 0
Modified: lxml/branch/lxml-2.0/src/lxml/tests/test_threading.py
==============================================================================
--- lxml/branch/lxml-2.0/src/lxml/tests/test_threading.py (original)
+++ lxml/branch/lxml-2.0/src/lxml/tests/test_threading.py Tue May 6 23:25:42 2008
@@ -6,13 +6,18 @@
import unittest, threading
-from common_imports import etree, HelperTestCase
+from common_imports import etree, HelperTestCase, StringIO
class ThreadingTestCase(HelperTestCase):
"""Threading tests"""
etree = etree
- def test_subtree_copy(self):
+ def _run_thread(self, func):
+ thread = threading.Thread(target=func)
+ thread.start()
+ thread.join()
+
+ def test_subtree_copy_thread(self):
tostring = self.etree.tostring
XML = self.etree.XML
xml = "<root><threadtag/></root>"
@@ -23,12 +28,113 @@
main_root.append(thread_root[0])
del thread_root
- thread = threading.Thread(target=run_thread)
- thread.start()
- thread.join()
-
+ self._run_thread(run_thread)
self.assertEquals(xml, tostring(main_root))
+ def test_main_xslt_in_thread(self):
+ XML = self.etree.XML
+ style = XML('''\
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="*">
+ <foo><xsl:copy><xsl:value-of select="/a/b/text()" /></xsl:copy></foo>
+ </xsl:template>
+</xsl:stylesheet>''')
+ st = etree.XSLT(style)
+
+ result = []
+
+ def run_thread():
+ root = XML('<a><b>B</b><c>C</c></a>')
+ result.append( st(root) )
+
+ self._run_thread(run_thread)
+ self.assertEquals('''\
+<?xml version="1.0"?>
+<foo><a>B</a></foo>
+''',
+ str(result[0]))
+
+ def test_thread_xslt(self):
+ XML = self.etree.XML
+ tostring = self.etree.tostring
+ root = XML('<a><b>B</b><c>C</c></a>')
+
+ def run_thread():
+ style = XML('''\
+ <xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="*">
+ <foo><xsl:copy><xsl:value-of select="/a/b/text()" /></xsl:copy></foo>
+ </xsl:template>
+ </xsl:stylesheet>''')
+ st = etree.XSLT(style)
+ root.append( st(root).getroot() )
+
+ self._run_thread(run_thread)
+ self.assertEquals('<a><b>B</b><c>C</c><foo><a>B</a></foo></a>',
+ tostring(root))
+
+ def test_thread_mix(self):
+ XML = self.etree.XML
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+ tostring = self.etree.tostring
+ xml = '<a><b>B</b><c xmlns="test">C</c></a>'
+ root = XML(xml)
+ fragment = XML("<other><tags/></other>")
+
+ result = self.etree.Element("{myns}root", att = "someval")
+
+ def run_XML():
+ thread_root = XML(xml)
+ result.append(thread_root[0])
+ result.append(thread_root[-1])
+
+ def run_parse():
+ thread_root = self.etree.parse(StringIO(xml)).getroot()
+ result.append(thread_root[0])
+ result.append(thread_root[-1])
+
+ def run_move_main():
+ result.append(fragment[0])
+
+ def run_build():
+ result.append(
+ Element("{myns}foo", attrib={'{test}attr':'val'}))
+ SubElement(result, "{otherns}tasty")
+
+ def run_xslt():
+ style = XML('''\
+ <xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:template match="*">
+ <foo><xsl:copy><xsl:value-of select="/a/b/text()" /></xsl:copy></foo>
+ </xsl:template>
+ </xsl:stylesheet>''')
+ st = etree.XSLT(style)
+ result.append( st(root).getroot()[0] )
+
+ for test in (run_XML, run_parse, run_move_main, run_xslt):
+ tostring(result)
+ self._run_thread(test)
+
+ self.assertEquals(
+ '<ns0:root xmlns:ns0="myns" att="someval"><b>B</b><c xmlns="test">C</c><b>B</b><c xmlns="test">C</c><tags/><a>B</a></ns0:root>',
+ tostring(result))
+
+ def strip_first():
+ root = Element("newroot")
+ root.append(result[0])
+
+ while len(result):
+ self._run_thread(strip_first)
+
+ self.assertEquals(
+ '<ns0:root xmlns:ns0="myns" att="someval"/>',
+ tostring(result))
+
+
def test_suite():
suite = unittest.TestSuite()
suite.addTests([unittest.makeSuite(ThreadingTestCase)])
Modified: lxml/branch/lxml-2.0/src/lxml/tree.pxd
==============================================================================
--- lxml/branch/lxml-2.0/src/lxml/tree.pxd (original)
+++ lxml/branch/lxml-2.0/src/lxml/tree.pxd Tue May 6 23:25:42 2008
@@ -52,12 +52,12 @@
void xmlHashScan(xmlHashTable* table, xmlHashScanner f, void* data) nogil
void* xmlHashLookup(xmlHashTable* table, char* name) nogil
-cdef extern from "libxml/tree.h":
-
- # for some reason need to define this in this section;
+cdef extern from *: # actually "libxml/dict.h"
# libxml/dict.h appears to be broken to include in C
ctypedef struct xmlDict
-
+ cdef char* xmlDictLookup(xmlDict* dict, char* name, int len) nogil
+
+cdef extern from "libxml/tree.h":
ctypedef struct xmlDoc
ctypedef struct xmlAttr
ctypedef struct xmlNotationTable
More information about the lxml-checkins
mailing list