Index: MANIFEST.in
===================================================================
--- MANIFEST.in	(Revision 0)
+++ MANIFEST.in	(Revision 19068)
@@ -0,0 +1,3 @@
+include setup.py *.txt lxmldistutils.py
+recursive-include src *.pyx *.pxd
+recursive-include doc *.txt
Index: src/lxml/tests/test_etree.py
===================================================================
--- src/lxml/tests/test_etree.py	(Revision 18892)
+++ src/lxml/tests/test_etree.py	(Arbeitskopie)
@@ -1544,7 +1544,25 @@
         e = etree.Element('foo')
         e.set('bar', 'Bar')
         self.assertEquals(False, bool(e))
-        
+
+    def test_multiple_elementrees(self):
+        etree = self.etree
+
+        a = etree.Element('a')
+        b = etree.SubElement(a, 'b')
+
+        t = etree.ElementTree(a)
+        self.assertEquals(self._rootstring(t), '<a><b/></a>')
+
+        t1 = etree.ElementTree(a)
+        self.assertEquals(self._rootstring(t1), '<a><b/></a>')
+        self.assertEquals(self._rootstring(t),  '<a><b/></a>')
+
+        t2 = etree.ElementTree(b)
+        self.assertEquals(self._rootstring(t2), '<b/>')
+        self.assertEquals(self._rootstring(t1), '<a><b/></a>')
+        self.assertEquals(self._rootstring(t),  '<a><b/></a>')
+
     def _writeElement(self, element, encoding='us-ascii'):
         """Write out element for comparison.
         """
@@ -1579,6 +1597,9 @@
         self.assertEquals(expected, self._writeElement(element, encoding))
         self.assertEquals(expected, self._writeElementFile(element, encoding))
         
+    def _rootstring(self, tree):
+        return self.etree.tostring(tree.getroot()).replace(' ', '').replace('\n', '')
+
     def _check_element_tree(self, tree):
         self._check_element(tree.getroot())
         
@@ -1721,6 +1742,9 @@
         f = StringIO(text)
         return etree.parse(f)
     
+    def _rootstring(self, tree):
+        return etree.tostring(tree.getroot()).replace(' ', '').replace('\n', '')
+
 class ETreeOnlyTestCase(HelperTestCase):
     """Tests only for etree, not ElementTree"""
     etree = etree
@@ -1808,6 +1832,27 @@
             None,
             a[0].prefix)
 
+    def test_getparent(self):
+        Element = self.etree.Element
+        SubElement = self.etree.SubElement
+
+        a = Element('a')
+        b = SubElement(a, 'b')
+        c = SubElement(a, 'c')
+        d = SubElement(b, 'd')
+        self.assertEquals(
+            None,
+            a.getparent())
+        self.assertEquals(
+            a,
+            b.getparent())
+        self.assertEquals(
+            b.getparent(),
+            c.getparent())
+        self.assertEquals(
+            b,
+            d.getparent())
+
     def test_namespaces(self):
         etree = self.etree
 
@@ -1892,6 +1937,64 @@
         return canonicalize(data)
 
 
+class ETreeNamespaceClassesTestCase(HelperTestCase):
+    class default_class(etree.ElementBase):
+        pass
+    class maeh_class(etree.ElementBase):
+        def maeh(self):
+            return u'maeh'
+    class bluff_class(etree.ElementBase):
+        def bluff(self):
+            return u'bluff'
+
+    def test_registry(self):
+        self.assertRaises(KeyError, etree.unregister_namespace_classes, u'ns01')
+        etree.register_namespace_classes(u'ns01', {u'maeh'  : self.maeh_class})
+        etree.unregister_namespace_classes(u'ns01')
+        self.assertRaises(KeyError, etree.unregister_namespace_classes, u'ns01')
+
+        etree.register_namespace_classes(u'ns02', {u'maeh'  : self.maeh_class})
+        etree.register_namespace_classes(u'ns03', {u'bluff' : self.bluff_class})
+        etree.unregister_namespace_classes(u'ns02')
+        etree.unregister_namespace_classes(u'ns03')
+
+    def test_ns_classes(self):
+        bluff_dict = {u'bluff' : self.bluff_class}
+        maeh_dict  = {u'maeh'  : self.maeh_class}
+
+        etree.register_namespace_classes(u'ns10', bluff_dict)
+
+        tree = self.parse(u'<bluff xmlns="ns10"><ns11:maeh xmlns:ns11="ns11"/></bluff>')
+
+        el = tree.getroot()
+        self.assert_(isinstance(el, etree.ElementBase))
+        self.assert_(hasattr(el, 'bluff'))
+        self.assertFalse(hasattr(el[0], 'maeh'))
+        self.assertFalse(hasattr(el[0], 'bluff'))
+        self.assertEquals(el.bluff(), u'bluff')
+        del el
+
+        etree.register_namespace_classes(u'ns11', maeh_dict)
+        el = tree.getroot()
+        self.assert_(hasattr(el, 'bluff'))
+        self.assert_(hasattr(el[0], 'maeh'))
+        self.assertEquals(el.bluff(), u'bluff')
+        self.assertEquals(el[0].maeh(), u'maeh')
+        del el
+
+        etree.unregister_namespace_classes(u'ns10')
+
+        tree = self.parse(u'<bluff xmlns="ns10"><ns11:maeh xmlns:ns11="ns11"/></bluff>')
+        el = tree.getroot()
+        self.assertFalse(hasattr(el, 'bluff'))
+        self.assertFalse(hasattr(el, 'maeh'))
+        self.assertFalse(hasattr(el[0], 'bluff'))
+        self.assert_(hasattr(el[0], 'maeh'))
+
+        etree.unregister_namespace_classes(u'ns11')
+
+
+
 class ETreeXSLTTestCase(HelperTestCase):
     """XPath tests etree"""
         
@@ -2057,6 +2160,42 @@
             '<doc><foo>Bar</foo><foo>Baz</foo></doc>',
             etree.tostring(result.getroot()))
         
+    def test_multiple_elementrees(self):
+        tree = self.parse('<a><b>B</b><c>C</c></a>')
+        style = self.parse('''\
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:template match="a"><A><xsl:apply-templates/></A></xsl:template>
+  <xsl:template match="b"><B><xsl:apply-templates/></B></xsl:template>
+  <xsl:template match="c"><C><xsl:apply-templates/></C></xsl:template>
+</xsl:stylesheet>''')
+
+        self.assertEquals(self._rootstring(tree),
+                          '<a><b>B</b><c>C</c></a>')
+        result = tree.xslt(style)
+        self.assertEquals(self._rootstring(tree),
+                          '<a><b>B</b><c>C</c></a>')
+        self.assertEquals(self._rootstring(result),
+                          '<A><B>B</B><C>C</C></A>')
+
+        b_tree = etree.ElementTree(tree.getroot()[0])
+        self.assertEquals(self._rootstring(b_tree),
+                          '<b>B</b>')
+        result = b_tree.xslt(style)
+        self.assertEquals(self._rootstring(tree),
+                          '<a><b>B</b><c>C</c></a>')
+        self.assertEquals(self._rootstring(result),
+                          '<B>B</B>')
+
+        c_tree = etree.ElementTree(tree.getroot()[1])
+        self.assertEquals(self._rootstring(c_tree),
+                          '<c>C</c>')
+        result = c_tree.xslt(style)
+        self.assertEquals(self._rootstring(tree),
+                          '<a><b>B</b><c>C</c></a>')
+        self.assertEquals(self._rootstring(result),
+                          '<C>C</C>')
+
 class ETreeRelaxNGTestCase(HelperTestCase):
     def test_relaxng(self):
         tree_valid = self.parse('<a><b></b></a>')
@@ -2106,6 +2245,35 @@
         self.assert_(tree_valid.relaxng(schema))
         self.assert_(not tree_invalid.relaxng(schema))
 
+    def test_multiple_elementrees(self):
+        tree = self.parse('<a><b>B</b><c>C</c></a>')
+        schema = etree.RelaxNG( self.parse('''\
+<element name="a" xmlns="http://relaxng.org/ns/structure/1.0">
+  <element name="b">
+    <text />
+  </element>
+  <element name="c">
+    <text />
+  </element>
+</element>
+''') )
+        self.assert_(schema.validate(tree))
+        self.assert_(schema.validate(tree))
+
+        schema = etree.RelaxNG( self.parse('''\
+<element name="b" xmlns="http://relaxng.org/ns/structure/1.0">
+  <text />
+</element>
+''') )
+        c_tree = etree.ElementTree(tree.getroot()[1])
+        self.assertEqual(self._rootstring(c_tree), '<c>C</c>')
+        self.assert_(not schema.validate(c_tree))
+
+        b_tree = etree.ElementTree(tree.getroot()[0])
+        self.assertEqual(self._rootstring(b_tree), '<b>B</b>')
+        self.assert_(schema.validate(b_tree))
+
+
 class ETreeXMLSchemaTestCase(HelperTestCase):
     def test_xmlschema(self):
         tree_valid = self.parse('<a><b></b></a>')
@@ -2180,6 +2348,80 @@
         self.assertEquals('<a><b></b></a>',
                           s)
 
+class ETreeSaxTestCase(HelperTestCase):
+    def _saxify_unsaxify(self, saxifiable):
+        handler = etree.SaxTreeBuilder()
+        saxifiable.saxify(handler)
+        return handler.etree
+        
+    def _saxify_serialize(self, tree):
+        new_tree = self._saxify_unsaxify(tree)
+        f = StringIO()
+        new_tree.write(f)
+        return f.getvalue()
+
+    def test_etree_sax_simple(self):
+        tree = self.parse('<a>ab<b/>ba</a>')
+        xml_out = self._saxify_serialize(tree)
+        self.assertEquals('<a>ab<b/>ba</a>',
+                          xml_out)
+
+    def test_etree_sax_double(self):
+        tree = self.parse('<a>ab<b>bb</b>ba</a>')
+        xml_out = self._saxify_serialize(tree)
+        self.assertEquals('<a>ab<b>bb</b>ba</a>',
+                          xml_out)
+
+    def test_etree_sax_ns1(self):
+        tree = self.parse('<a xmlns="bla">ab<b>bb</b>ba</a>')
+        new_tree = self._saxify_unsaxify(tree)
+        root = new_tree.getroot()
+        self.assertEqual(root.tag,
+                         '{bla}a')
+        self.assertEqual(root[0].tag,
+                         '{bla}b')
+
+    def test_etree_sax_ns2(self):
+        tree = self.parse('<a xmlns="blaA">ab<b:b xmlns:b="blaB">bb</b:b>ba</a>')
+        new_tree = self._saxify_unsaxify(tree)
+        root = new_tree.getroot()
+        self.assertEqual(root.tag,
+                         '{blaA}a')
+        self.assertEqual(root[0].tag,
+                         '{blaB}b')
+
+    def test_element_sax(self):
+        tree = self.parse('<a><b/></a>')
+        a = tree.getroot()
+        b = a[0]
+
+        xml_out = self._saxify_serialize(a)
+        self.assertEquals('<a><b/></a>',
+                          xml_out)
+
+        xml_out = self._saxify_serialize(b)
+        self.assertEquals('<b/>',
+                          xml_out)
+
+    def test_element_sax_ns(self):
+        tree = self.parse('<a:a xmlns:a="blaA"><b/></a:a>')
+        a = tree.getroot()
+        b = a[0]
+
+        new_tree = self._saxify_unsaxify(a)
+        root = new_tree.getroot()
+        self.assertEqual(root.tag,
+                         '{blaA}a')
+        self.assertEqual(root[0].tag,
+                         'b')
+
+        new_tree = self._saxify_unsaxify(b)
+        root = new_tree.getroot()
+        self.assertEqual(root.tag,
+                         'b')
+        self.assertEqual(len(root),
+                         0)
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTests([unittest.makeSuite(ETreeTestCase)])
@@ -2193,6 +2435,8 @@
     suite.addTests([unittest.makeSuite(ETreeXMLSchemaTestCase)])
     suite.addTests([unittest.makeSuite(ETreeXIncludeTestCase)])
     suite.addTests([unittest.makeSuite(ETreeC14NTestCase)])
+    suite.addTests([unittest.makeSuite(ETreeSaxTestCase)])
+    suite.addTests([unittest.makeSuite(ETreeNamespaceClassesTestCase)])
     suite.addTests(
         [doctest.DocFileSuite('../../../doc/api.txt')])
     suite.addTests(
Index: src/lxml/tests/test_xpathevaluator.py
===================================================================
--- src/lxml/tests/test_xpathevaluator.py	(Revision 18892)
+++ src/lxml/tests/test_xpathevaluator.py	(Arbeitskopie)
@@ -146,6 +146,76 @@
         self.assertEquals('Hoi', r[0].text)
         self.assertEquals('Dag', r[1].text)
 
+    def test_xpath_variables(self):
+        x = self.parse('<a attr="true"/>')
+        e = etree.XPathEvaluator(x)
+
+        expr = "/a[@attr=$aval]"
+        r = e.evaluate(expr, aval=1)
+        self.assertEquals(0, len(r))
+
+        r = e.evaluate(expr, aval="true")
+        self.assertEquals(1, len(r))
+        self.assertEquals("true", r[0].get('attr'))
+
+        r = e.evaluate(expr, aval=True)
+        self.assertEquals(1, len(r))
+        self.assertEquals("true", r[0].get('attr'))
+
+    def test_xpath_compile_doc(self):
+        x = self.parse('<a attr="true"/>')
+
+        expr = etree.XPath("/a[@attr != 'true']")
+        r = expr.evaluate(x)
+        self.assertEquals(0, len(r))
+
+        expr = etree.XPath("/a[@attr = 'true']")
+        r = expr.evaluate(x)
+        self.assertEquals(1, len(r))
+
+        expr = etree.XPath( expr.path )
+        r = expr.evaluate(x)
+        self.assertEquals(1, len(r))
+
+    def test_xpath_compile_element(self):
+        x = self.parse('<a><b/><c/></a>')
+        root = x.getroot()
+
+        expr = etree.XPath("./b")
+        r = expr.evaluate(root)
+        self.assertEquals(1, len(r))
+        self.assertEquals('b', r[0].tag)
+
+        expr = etree.XPath("./*")
+        r = expr.evaluate(root)
+        self.assertEquals(2, len(r))
+
+    def test_xpath_compile_vars(self):
+        x = self.parse('<a attr="true"/>')
+
+        expr = etree.XPath("/a[@attr=$aval]")
+        r = expr.evaluate(x, aval=False)
+        self.assertEquals(0, len(r))
+
+        r = expr.evaluate(x, aval=True)
+        self.assertEquals(1, len(r))
+
+    def test_xpath_compile_ns(self):
+        x = self.parse('<a><b xmlns="nsa"/><b xmlns="nsb"/></a>')
+
+        expr = etree.XPath("/a/{nsa}b")
+        r = expr.evaluate(x)
+        self.assertEquals(1, len(r))
+        self.assertEquals('{nsa}b', r[0].tag)
+
+        expr = etree.XPath("/a/{nsb}b")
+        r = expr.evaluate(x)
+        self.assertEquals(1, len(r))
+        self.assertEquals('{nsb}b', r[0].tag)
+
+    def test_xpath_compile_error(self):
+        self.assertRaises(SyntaxError, etree.XPath, '\\fad')
+
 SAMPLE_XML = etree.parse(StringIO("""
 <body>
   <tag>text</tag>
Index: src/lxml/etree.pyx
===================================================================
--- src/lxml/etree.pyx	(Revision 18892)
+++ src/lxml/etree.pyx	(Arbeitskopie)
@@ -9,7 +9,7 @@
 cimport xinclude
 cimport c14n
 cimport cstd
-import types
+import re, types
 
 from xmlparser cimport xmlParserCtxt, xmlDict
 import _elementpath
@@ -28,6 +28,11 @@
 PROXY_ATTRIB_ITER = 2
 PROXY_ELEMENT_ITER = 3
 
+cdef object _RE_STRINGS
+cdef object _RE_NAMESPACES
+_RE_STRINGS    = re.compile('("[^"]*")|(\'[^\']*\')')
+_RE_NAMESPACES = re.compile('{([^}]+)}')
+
 # the rules
 # any libxml C argument/variable is prefixed with c_
 # any non-public function/class is prefixed with an underscore
@@ -90,14 +95,14 @@
 class C14NError(Error):
     pass
 
-cdef class _DocumentBase:
-    """Base class to reference a libxml document.
+cdef class _Document:
+    """Internal base class to reference a libxml document.
 
     When instances of this class are garbage collected, the libxml
     document is cleaned up.
     """
+    cdef xmlDoc* _c_doc
     cdef int _ns_counter
-    cdef xmlDoc* _c_doc
     
     def __dealloc__(self):
         # if there are no more references to the document, it is safe
@@ -108,6 +113,49 @@
         #print self._c_doc.dict is theParser._c_dict
         tree.xmlFreeDoc(self._c_doc)
 
+    def buildNewPrefix(self):
+        ns = "ns%d" % self._ns_counter
+        self._ns_counter = self._ns_counter + 1
+        return ns
+
+    def getroot(self):
+        cdef xmlNode* c_node
+        c_node = tree.xmlDocGetRootElement(self._c_doc)
+        if c_node is NULL:
+            return None
+        return _elementFactory(self, c_node)
+
+cdef _Document _parseDocument(source, parser):
+    cdef xmlDoc* c_doc
+
+    # XXX ignore parser !!
+
+    # XXX simplistic (c)StringIO support
+    if hasattr(source, 'getvalue'):
+        c_doc = theParser.parseDoc(source.getvalue())
+    else:
+        filename = _getFilenameForFile(source)
+        # Support for unamed file-like object (eg urlgrabber.urlopen)
+        if not filename and hasattr(source, 'read'):
+            c_doc = theParser.parseDoc(source.read())
+        # Otherwise parse the file directly from the filesystem
+        else:
+            if filename is None:
+                filename = source
+            # open filename
+            c_doc = theParser.parseDocFromFile(filename)
+    if c_doc is NULL:
+        return None
+    else:
+        return _documentFactory(c_doc)
+
+cdef _Document _documentFactory(xmlDoc* c_doc):
+    cdef _Document result
+    result = _Document()
+    result._c_doc = c_doc
+    result._ns_counter = 0
+    return result
+
 # to help with debugging
 cdef void displayNode(xmlNode* c_node, indent):
     cdef xmlNode* c_child
@@ -120,10 +168,10 @@
 cdef class _NodeBase:
     """Base class to reference a document object and a libxml node.
 
-    By pointing to an ElementTree instance, a reference is kept to
-    _ElementTree as long as there is some pointer to a node in it.
+    By pointing to a Document instance, a reference is kept to
+    _Document as long as there is some pointer to a node in it.
     """
-    cdef _DocumentBase _doc
+    cdef _Document _doc
     cdef xmlNode* _c_node
     cdef int _proxy_type
     
@@ -134,6 +182,11 @@
             unregisterProxy(self, self._proxy_type)
         attemptDeallocation(self._c_node)
 
+    def _init(self):
+        """Called after object initialisation. Subclasses may override
+        this if they recursively call _init() in the superclasses.
+        """
+
     cdef xmlNs* _getNs(self, char* href):
         """Get or create namespace structure.
         """
@@ -149,79 +202,38 @@
             return c_ns
         # create ns if existing ns cannot be found
         # try to simulate ElementTree's namespace prefix creation
-        prefix = 'ns%s' % self._doc._ns_counter
-        self._doc._ns_counter = self._doc._ns_counter + 1
+        prefix = self._doc.buildNewPrefix()
         c_ns = tree.xmlNewNs(c_node, href, prefix)
         return c_ns
 
-cdef class _ElementTree(_DocumentBase):
+cdef class _ElementTree:
+    cdef _Document _doc
+    cdef _NodeBase _context_node
+    cdef object    _namespace_classes
 
     def parse(self, source, parser=None):
         """Updates self with the content of source and returns its root
         """
-        # XXX ignore parser for now
-        cdef xmlDoc* c_doc
-        cdef xmlNode* c_node
-        
-        # XXX simplistic (c)StringIO support
-        if hasattr(source, 'getvalue'):
-            c_doc = theParser.parseDoc(source.getvalue())
-        else:
-            filename = _getFilenameForFile(source)
-            # Support for unamed file-like object (eg urlgrabber.urlopen)
-            if not filename and hasattr(source, 'read'):
-                c_doc = theParser.parseDoc(source.read())
-            # Otherwise parse the file directly from the filesystem
-            else:
-                if filename is None:
-                    filename = source
-                # open filename
-                c_doc = theParser.parseDocFromFile(filename)
-        if self._c_doc is not NULL:
-            c_node = tree.xmlDocGetRootElement(self._c_doc)
-            if (c_node is not NULL and
-                not hasProxy(c_node) and
-                canDeallocateChildren(c_node)):
-                tree.xmlFreeDoc(self._c_doc)
-        self._c_doc = c_doc
-        return self.getroot()
+        self._doc          = _parseDocument(source, parser)
+        self._context_node = self._doc.getroot()
+        return self._context_node
     
     def getroot(self):
-        cdef xmlNode* c_node
-        c_node = tree.xmlDocGetRootElement(self._c_doc)
-        if c_node is NULL:
-            return None
-        return _elementFactory(self, c_node)
+        return self._context_node
     
     def write(self, file, encoding='us-ascii'):
-        cdef tree.xmlSaveCtxt* save_ctxt
-        cdef char* mem
-        cdef int size
-        
-        # recognize a diversity of ways to spell this in Python
-        if encoding in ('UTF-8', 'utf8', 'UTF8', 'utf-8'):
-            encoding = 'UTF-8'
-
         if not hasattr(file, 'write'):
             # file is a filename, we want a file object
             file = open(file, 'wb')
 
-        tree.xmlDocDumpMemoryEnc(self._c_doc, &mem, &size, encoding)
-        m = mem
+        m = tostring(self._context_node, encoding)
         # XXX this is purely for ElementTree compatibility..
         if encoding == 'UTF-8' or encoding == 'us-ascii':
-            # strip off XML prologue..
-            i = m.find('\n')
-            if i != -1:
-                m = m[i + 1:]
-            # strip off ending \n..
-            m = m[:-1]
-        if encoding == 'UTF-8':
-            file.write(m)
-        else:
-            file.write(funicode(m).encode(encoding))
-        tree.xmlFree(mem)
-            
+            m = _stripXmlPI(m)
+            if m[-1:] == '\n':
+                m = m[:-1]
+        file.write(m)
+
     def getiterator(self, tag=None):
         root = self.getroot()
         if root is None:
@@ -250,7 +262,7 @@
         return root.findall(path)
     
     # extensions to ElementTree API
-    def xpath(self, path, namespaces=None):
+    def xpath(self, _path, _namespaces=None, **variables):
         """XPath evaluate in context of document.
 
         namespaces is an optional dictionary with prefix to namespace URI
@@ -265,7 +277,7 @@
         against the same document, it is more efficient to use
         XPathEvaluator directly.
         """
-        return XPathDocumentEvaluator(self, namespaces).evaluate(path)
+        return XPathDocumentEvaluator(self._doc, _namespaces).evaluate(_path, **variables)
 
     def xslt(self, xslt, **kw):
         """Transform this document using other document.
@@ -322,29 +334,43 @@
         # at all. The XInclude nodes appear to be still being in the same
         # parent and same document, but they must not be connected to the
         # tree..
-        result = xinclude.xmlXIncludeProcess(self._c_doc)
+        result = xinclude.xmlXIncludeProcessTree(self._context_node._c_node)
         if result == -1:
             raise XIncludeError, "XInclude processing failed"
         
+    def saxify(self, content_handler):
+        "Output sax events for this tree to a SAX content handler."
+        _saxify(self._context_node, content_handler)
+
     def write_c14n(self, file):
         """C14N write of document. Always writes UTF-8.
         """
+        cdef xmlDoc* c_base_doc
+        cdef xmlDoc* c_doc
         cdef char* data
         cdef int bytes
-        bytes = c14n.xmlC14NDocDumpMemory(self._c_doc,
-                                          NULL, 0, NULL, 1, &data)
+        c_base_doc = self._doc._c_doc
+
+        c_doc = _fakeRootDoc(c_base_doc, self._context_node._c_node)
+        bytes = c14n.xmlC14NDocDumpMemory(c_doc, NULL, 0, NULL, 1, &data)
+        _destroyFakeDoc(c_base_doc, c_doc)
+
         if bytes < 0:
             raise C14NError, "C18N failed"
         if not hasattr(file, 'write'):
             file = open(file, 'wb')
         file.write(data)
         tree.xmlFree(data)
-    
-cdef _ElementTree _elementTreeFactory(xmlDoc* c_doc):
+
+
+cdef _ElementTree _elementTreeFactory(_Document doc, _NodeBase context_node):
     cdef _ElementTree result
     result = _ElementTree()
-    result._ns_counter = 0
-    result._c_doc = c_doc
+    result._doc = doc
+    if context_node is None and doc is not None:
+        context_node = doc.getroot()
+    result._context_node = context_node
+    result._namespace_classes = {}
     return result
 
 cdef class _Element(_NodeBase):
@@ -410,10 +436,10 @@
         cdef xmlNode* c_node
         cdef xmlDoc* c_doc
         c_doc = theParser.newDoc()
-        etree = _elementTreeFactory(c_doc)
+        doc = _documentFactory(c_doc)
         c_node = tree.xmlDocCopyNode(self._c_node, c_doc, 1)
         tree.xmlDocSetRootElement(c_doc, c_node)
-        return _elementFactory(etree, c_node)
+        return _elementFactory(doc, c_node)
         
     def set(self, key, value):
         self.attrib[key] = value
@@ -569,7 +595,7 @@
                 result.append(_elementFactory(self._doc, c_node))
                 c = c + 1
             c_node = c_node.next
-        return result        
+        return result
             
     def __len__(self):
         cdef int c
@@ -643,6 +669,14 @@
             c_node = c_node.next
         return result
 
+    def getparent(self):
+        cdef xmlNode* c_node
+        c_node = self._c_node.parent
+        if c_node is not NULL:
+            if _isElement(c_node):
+                return _elementFactory(self._doc, c_node)
+        return None
+
     def getiterator(self, tag=None):
         result = []
         if tag == "*":
@@ -659,6 +693,11 @@
     def makeelement(self, tag, attrib):
         return Element(tag, attrib)
 
+    def saxify(self, content_handler):
+        """Output sax events for this element and its subtree to a SAX
+        content handler."""
+        _saxify(self, content_handler)
+
     def find(self, path):
         return _elementpath.find(self, path)
 
@@ -668,26 +707,33 @@
     def findall(self, path):
         return _elementpath.findall(self, path)
 
-    def xpath(self, path, namespaces=None):
-        return XPathElementEvaluator(self, namespaces).evaluate(path)
+    def xpath(self, _path, _namespaces=None, **variables):
+        return XPathElementEvaluator(self, _namespaces).evaluate(_path, **variables)
 
-cdef _Element _elementFactory(_ElementTree etree, xmlNode* c_node):
+cdef _Element _elementFactory(_Document doc, xmlNode* c_node):
     cdef _Element result
+    cdef char* c_ns_href
     result = getProxy(c_node, PROXY_ELEMENT)
     if result is not None:
         return result
     if c_node is NULL:
         return None
     if c_node.type == tree.XML_ELEMENT_NODE:
-        result = _Element()
+        if c_node.ns == NULL:
+            c_ns_href = NULL
+        else:
+            c_ns_href = c_node.ns.href
+        element_class = _find_element_class(c_ns_href, c_node.name)
+        result = element_class()
     elif c_node.type == tree.XML_COMMENT_NODE:
         result = _Comment()
     else:
         assert 0, "Unknown node type"
-    result._doc = etree
+    result._doc = doc
     result._c_node = c_node
     result._proxy_type = PROXY_ELEMENT
     registerProxy(result, PROXY_ELEMENT)
+    result._init()
     return result
 
 cdef class _Comment(_Element):
@@ -731,7 +777,7 @@
     def items(self):
         return []
     
-cdef _Comment _commentFactory(_ElementTree etree, xmlNode* c_node):
+cdef _Comment _commentFactory(_Document doc, xmlNode* c_node):
     cdef _Comment result
     result = getProxy(c_node, PROXY_ELEMENT)
     if result is not None:
@@ -739,7 +785,7 @@
     if c_node is NULL:
         return None
     result = _Comment()
-    result._doc = etree
+    result._doc = doc
     result._c_node = c_node
     result._proxy_type = PROXY_ELEMENT
     registerProxy(result, PROXY_ELEMENT)
@@ -872,13 +918,13 @@
             result = tree.xmlGetNsProp(self._c_node, tag, ns)
         return result is not NULL
   
-cdef _Attrib _attribFactory(_ElementTree etree, xmlNode* c_node):
+cdef _Attrib _attribFactory(_Document doc, xmlNode* c_node):
     cdef _Attrib result
     result = getProxy(c_node, PROXY_ATTRIB)
     if result is not None:
         return result
     result = _Attrib()
-    result._doc = etree
+    result._doc = doc
     result._c_node = c_node
     result._proxy_type = PROXY_ATTRIB
     registerProxy(result, PROXY_ATTRIB)
@@ -899,14 +945,14 @@
         registerProxy(self, PROXY_ATTRIB_ITER)
         return funicode(c_node.name)
     
-cdef _AttribIterator _attribIteratorFactory(_ElementTree etree,
+cdef _AttribIterator _attribIteratorFactory(_Document doc,
                                             xmlNode* c_node):
     cdef _AttribIterator result
     result = getProxy(c_node, PROXY_ATTRIB_ITER)
     if result is not None:
         return result
     result = _AttribIterator()
-    result._doc = etree
+    result._doc = doc
     result._c_node = c_node
     result._proxy_type = PROXY_ATTRIB_ITER
     registerProxy(result, PROXY_ATTRIB_ITER)
@@ -927,14 +973,14 @@
         registerProxy(self, PROXY_ELEMENT_ITER)
         return _elementFactory(self._doc, c_node)
 
-cdef _ElementIterator _elementIteratorFactory(_ElementTree etree,
+cdef _ElementIterator _elementIteratorFactory(_Document doc,
                                               xmlNode* c_node):
     cdef _ElementIterator result
     result = getProxy(c_node, PROXY_ELEMENT_ITER)
     if result is not None:
         return result
     result = _ElementIterator()
-    result._doc = etree
+    result._doc = doc
     result._c_node = c_node
     result._proxy_type = PROXY_ELEMENT_ITER
     registerProxy(result, PROXY_ELEMENT_ITER)
@@ -959,31 +1005,72 @@
     c_node = tree.xmlNewDocComment(c_doc, text)
     return c_node
 
+
+# module-level API for namespace implementations
+
+class ElementBase(_Element):
+    """All classes in namespace implementations must inherit from
+    this one.  Note that subclasses *must not* override __init__."""
+    pass
+
+def register_namespace_classes(namespace, class_dict):
+    """Register a namespace implementation.
+
+    The namespace URI will be associated with a class dictionary.
+    Whenever elements from this namespace are created or read from the
+    XML tree, the dictionary is consulted for their local name to
+    build the class that implements them. Classes associated with the
+    None value represent the default class for the entire namespace."""
+    if namespace:
+        ns_utf = namespace.encode('UTF-8')
+    else:
+        ns_utf = None
+    __NAMESPACE_CLASSES[ns_utf] = _build_namespace_impl_dict(class_dict)
+
+def unregister_namespace_classes(namespace):
+    """Unregister a namespace implementation.
+
+    See register_namespace_classes(). Note that this does not assure
+    by itself that the previously registered classes will no longer be
+    used. As long as references to any of their elements are held
+    somewhere in your program, accessing these elements may result in
+    reuse of this object. Only when new elements are created, they
+    will use the registered classes as of the time of their creation."""
+    if namespace:
+        ns_utf = namespace.encode('UTF-8')
+    else:
+        ns_utf = None
+    del __NAMESPACE_CLASSES[ns_utf]
+
+
 # module-level API for ElementTree
 
 def Element(tag, attrib=None, nsmap=None, **extra):
-    cdef xmlNode* c_node
-    cdef _ElementTree etree
-    etree = ElementTree()
-    c_node = _createElement(etree._c_doc, tag, attrib, extra)
-    tree.xmlDocSetRootElement(etree._c_doc, c_node)
+    cdef _Document doc
+    cdef _Element  result
+    cdef xmlNode*  c_node
+    cdef xmlDoc*   c_doc
+    c_doc = theParser.newDoc()
+    c_node = _createElement(c_doc, tag, attrib, extra)
+    tree.xmlDocSetRootElement(c_doc, c_node)
     # add namespaces to node if necessary
-    _addNamespaces(etree._c_doc, c_node, nsmap)
+    _addNamespaces(c_doc, c_node, nsmap)
     # XXX hack for namespaces
-    result = _elementFactory(etree, c_node)
+    doc = _documentFactory(c_doc)
+    result = _elementFactory(doc, c_node)
     result.tag = tag
     return result
 
 def Comment(text=None):
-    cdef xmlNode* c_node
-    cdef _ElementTree etree
+    cdef _Document doc
+    cdef xmlNode*  c_node
     if text is None:
         text = ''
     text = ' %s ' % text.encode('UTF-8')
-    etree = ElementTree()
-    c_node = _createComment(etree._c_doc, text)
-    tree.xmlAddChild(<xmlNode*>etree._c_doc, c_node)
-    return _commentFactory(etree, c_node)
+    doc = _documentFactory( theParser.newDoc() )
+    c_node = _createComment(doc._c_doc, text)
+    tree.xmlAddChild(<xmlNode*>doc._c_doc, c_node)
+    return _commentFactory(doc, c_node)
 
 def SubElement(_Element parent, tag, attrib=None, nsmap=None, **extra):
     cdef xmlNode* c_node
@@ -998,13 +1085,17 @@
     return element
 
 def ElementTree(_Element element=None, file=None):
-    cdef xmlDoc* c_doc
     cdef xmlNode* c_next
     cdef xmlNode* c_node
     cdef xmlNode* c_node_copy
     cdef _ElementTree etree
-    
-    if file is not None:
+    cdef _Document doc
+    cdef _NodeBase node
+
+    if element is not None:
+        doc  = element._doc
+        node = element
+    elif file is not None:
         if isinstance(file, str) or isinstance(file, unicode):
             f = open(file, 'r')
             data = f.read()
@@ -1012,27 +1103,29 @@
         else:
             # XXX read XML into memory not the fastest way to do this
             data = file.read()
-        c_doc = theParser.parseDoc(data)
+        doc = _documentFactory( theParser.parseDoc(data) )
+        node = doc.getroot()
     else:
-        c_doc = theParser.newDoc()
+        doc = _documentFactory( theParser.newDoc() )
+        node = None
 
-    etree = _elementTreeFactory(c_doc)
+    etree = _elementTreeFactory(doc, node)
 
-    # XXX what if element and file are both not None?
-    if element is not None:
-        c_next = element._c_node.next
-        tree.xmlDocSetRootElement(etree._c_doc, element._c_node)
-        _moveTail(c_next, element._c_node)
-        changeDocumentBelow(element, etree)
+##     # XXX what if element and file are both not None?
+##     if element is not None:
+##         c_next = element._c_node.next
+##         tree.xmlDocSetRootElement(etree._c_doc, element._c_node)
+##         _moveTail(c_next, element._c_node)
+##         changeDocumentBelow(element, etree)
     
     return etree
 
 def XML(text):
     cdef xmlDoc* c_doc
     if isinstance(text, unicode):
-        text = text.encode('UTF-8')
+        text = _stripXmlPI(text.encode('UTF-8'))
     c_doc = theParser.parseDoc(text)
-    return _elementTreeFactory(c_doc).getroot()
+    return _documentFactory(c_doc).getroot()
 
 fromstring = XML
 
@@ -1044,7 +1137,7 @@
     _dumpToFile(sys.stdout, elem._doc._c_doc, elem._c_node)
 
 def tostring(_NodeBase element, encoding='us-ascii'):
-    cdef _DocumentBase doc
+    cdef _Document doc
     cdef tree.xmlOutputBuffer* c_buffer
     cdef tree.xmlCharEncodingHandler* enchandler
     cdef char* enc
@@ -1056,10 +1149,6 @@
     if encoding in ('utf8', 'UTF8', 'utf-8'):
         encoding = 'UTF-8'
     doc = element._doc
-    if element is element._doc.getroot():
-        f = StringIO()
-        doc.write(f, encoding)
-        return f.getvalue()
     enc = encoding
     # it is necessary to *and* find the encoding handler *and* use
     # encoding during output
@@ -1079,10 +1168,9 @@
 def parse(source, parser=None):
     """Return an ElementTree object loaded with source elements
     """
-    cdef _ElementTree tree
-    tree = _elementTreeFactory(NULL)
-    tree.parse(source, parser=parser)
-    return tree
+    cdef _Document doc
+    doc = _parseDocument(source, parser)
+    return ElementTree(doc.getroot())
 
 cdef _addNamespaces(xmlDoc* c_doc, xmlNode* c_node, object nsmap):
     cdef xmlNs* c_ns
@@ -1097,22 +1185,113 @@
             else:
                 tree.xmlNewNs(c_node, href, NULL)
         
-cdef class XPathDocumentEvaluator:
+
+cdef class XPathEvaluatorBase:
+    cdef object _extension_functions
+    cdef object _extensions
+    cdef object _namespaces
+    cdef object _exc_info
+
+    def __init__(self, namespaces, extensions):
+        self._extension_functions = {}
+        self._extensions = extensions
+        self._namespaces = namespaces
+
+    cdef _registerNamespace(self, xpath.xmlXPathContext* xpathCtxt, prefix, uri):
+        """Register a namespace with the XPath context.
+        """
+        s_prefix = prefix.encode('UTF8')
+        s_uri = uri.encode('UTF8')
+        # XXX should check be done to verify namespace doesn't already exist?
+        ns_register_status = xpath.xmlXPathRegisterNs(
+            xpathCtxt, s_prefix, s_uri)
+        if ns_register_status != 0:
+            # XXX doesn't seem to be possible to trigger this
+            # from Python
+            raise XPathNamespaceError, (
+                "Unable to register namespaces with prefix "
+                "%s and uri %s" % (prefix, uri))
+
+    cdef _registerExtensions(self, xpath.xmlXPathContext* xpathCtxt, extensions):
+        for extension in extensions:
+            self._extension_functions.update(extension)
+            for (ns_uri, name), function in extension.items():
+                if ns_uri is not None:
+                    xpath.xmlXPathRegisterFuncNS(
+                        xpathCtxt, name, ns_uri, _xpathCallback)
+                else:
+                    xpath.xmlXPathRegisterFunc(
+                        xpathCtxt, name, _xpathCallback)
+
+    cdef _registerVariables(self, xpath.xmlXPathContext* xpathCtxt, variable_dict):
+        for name, value in variable_dict.items():
+            name_utf = name.encode('UTF-8')
+            xpath.xmlXPathRegisterVariable(xpathCtxt, name_utf,
+                                           _wrapXPathObject(value))
+
+    cdef _unregisterVariables(self, xpath.xmlXPathContext* xpathCtxt, variable_names):
+        cdef xpath.xmlXPathObject* xpathVarValue
+        for name in variable_names:
+            name_utf = name.encode('UTF-8')
+            xpathVarValue = xpath.xmlXPathVariableLookup(xpathCtxt, name_utf)
+            if xpathVarValue is not NULL:
+                xpath.xmlXPathRegisterVariable(xpathCtxt, name_utf, NULL)
+                xpath.xmlXPathFreeObject(xpathVarValue)
+
+    cdef _nsextract_path(self, path_utf):
+        namespaces = {}
+        # replace {namespaces} by prefixes
+        stripped_path = _RE_STRINGS.sub('', path_utf) # remove string literals
+        namespace_uris = []
+        i = 1
+        for namespace in _RE_NAMESPACES.findall(stripped_path):
+            if namespace not in namespace_uris:
+                namespace_uris.append(namespace)
+                namespaces["p%02d" % i] = namespace
+                i = i+1
+        for prefix, namespace in namespaces.items():
+            ns_str     = "{%s}" % namespace
+            prefix_str = prefix + ':'
+            path_utf = path_utf.replace(ns_str, prefix_str)
+        return path_utf, namespaces
+
+    cdef _prepare_eval(self):
+        self._exc_info = None
+
+    cdef object _handle_result(self, xpath.xmlXPathObject* xpathObj, _Document doc):
+        if self._exc_info is not None:
+            type, value, traceback = self._exc_info
+            self._exc_info = None
+            raise type, value, traceback
+        if xpathObj is NULL:
+            raise XPathSyntaxError, "Error in xpath expression."
+        try:
+            result = _unwrapXPathObject(xpathObj, doc)
+        except XPathResultError:
+            xpath.xmlXPathFreeObject(xpathObj)
+            raise
+        xpath.xmlXPathFreeObject(xpathObj)
+        return result
+
+
+cdef class XPathDocumentEvaluator(XPathEvaluatorBase):
     """Create an XPath evaluator for a document.
     """
     cdef xpath.xmlXPathContext* _c_ctxt
-    cdef _ElementTree _doc
-    cdef object _extension_functions
-    cdef object _exc_info
-    cdef object _namespaces
-    cdef object _extensions
+    cdef _Document _doc
     cdef object _temp_elements
     cdef object _temp_docs
     
-    def __init__(self, _ElementTree doc,
+    def __init__(self, etree,
                  namespaces=None, extensions=None):
         cdef xpath.xmlXPathContext* xpathCtxt
         cdef int ns_register_status
+        cdef _Document doc
+
+        if isinstance(etree, _Document):
+            doc = <_Document>etree # for internal use only!
+        else:
+            doc = (<_ElementTree>etree)._doc
         
         xpathCtxt = xpath.xmlXPathNewContext(doc._c_doc)
         if xpathCtxt is NULL:
@@ -1122,40 +1301,21 @@
         self._doc = doc
         self._c_ctxt = xpathCtxt
         self._c_ctxt.userData = <void*>self
-        self._namespaces = namespaces
-        self._extensions = extensions
         
+        XPathEvaluatorBase.__init__(self, namespaces, extensions)
+
         if namespaces is not None:
             self.registerNamespaces(namespaces)
-        self._extension_functions = {}
         if extensions is not None:
-            for extension in extensions:
-                self._extension_functions.update(extension)
-                for (ns_uri, name), function in extension.items():
-                    if ns_uri is not None:
-                        xpath.xmlXPathRegisterFuncNS(
-                            xpathCtxt, name, ns_uri, _xpathCallback)
-                    else:
-                        xpath.xmlXPathRegisterFunc(
-                            xpathCtxt, name, _xpathCallback)
-                        
+            self._registerExtensions(xpathCtxt, extensions)
+
     def __dealloc__(self):
         xpath.xmlXPathFreeContext(self._c_ctxt)
     
     def registerNamespace(self, prefix, uri):
         """Register a namespace with the XPath context.
         """
-        s_prefix = prefix.encode('UTF8')
-        s_uri = uri.encode('UTF8')
-        # XXX should check be done to verify namespace doesn't already exist?
-        ns_register_status = xpath.xmlXPathRegisterNs(
-            self._c_ctxt, s_prefix, s_uri)
-        if ns_register_status != 0:
-            # XXX doesn't seem to be possible to trigger this
-            # from Python
-            raise XPathNamespaceError, (
-                "Unable to register namespaces with prefix "
-                "%s and uri %s" % (prefix, uri))
+        self._registerNamespace(self._c_ctxt, prefix, uri)
 
     def registerNamespaces(self, namespaces):
         """Register a prefix -> uri dict.
@@ -1163,36 +1323,31 @@
         for prefix, uri in namespaces.items():
             self.registerNamespace(prefix, uri)
     
-    def evaluate(self, path):
-        return self._evaluate(path, NULL)
+    def evaluate(self, _path, **variables):
+        """Evaluate an XPath expression on the document.  Variables
+        may be given as keyword arguments. Note that namespaces are
+        currently not supported for variables."""
+        return self._evaluate(_path, NULL, variables)
 
-    cdef object _evaluate(self, path, xmlNode* c_ctxt_node):
-        cdef xpath.xmlXPathObject* xpathObj
+    cdef object _evaluate(self, path, xmlNode* c_ctxt_node, variable_dict):
+        cdef xpath.xmlXPathContext* xpathCtxt
+        cdef xpath.xmlXPathObject*  xpathObj
         cdef xmlNode* c_node
         
+        xpathCtxt = self._c_ctxt
         # if element context is requested; unfortunately need to modify ctxt
-        self._c_ctxt.node = c_ctxt_node
+        xpathCtxt.node = c_ctxt_node
 
+        self._registerVariables(xpathCtxt, variable_dict)
+
         path = path.encode('UTF-8')
-        self._exc_info = None
+        self._prepare_eval()
         self._release()
-        xpathObj = xpath.xmlXPathEvalExpression(path, self._c_ctxt)
-        if self._exc_info is not None:
-            type, value, traceback = self._exc_info
-            self._exc_info = None
-            raise type, value, traceback
-        if xpathObj is NULL:
-            raise XPathSyntaxError, "Error in xpath expression."
-        try:
-            result = _unwrapXPathObject(xpathObj, self._doc)
-        except XPathResultError:
-            #self._release()
-            xpath.xmlXPathFreeObject(xpathObj)
-            raise
-        xpath.xmlXPathFreeObject(xpathObj)
-        # release temporarily held python stuff
-        #self._release()
-        return result
+        xpathObj = xpath.xmlXPathEvalExpression(path, xpathCtxt)
+
+        self._unregisterVariables(xpathCtxt, variable_dict)
+
+        return self._handle_result(xpathObj, self._doc)
         
     #def clone(self):
     #    # XXX pretty expensive so calling this from callback is probably
@@ -1233,14 +1388,17 @@
             self, element._doc, namespaces, extensions)
         self._element = element
         
-    def evaluate(self, path):
-        return self._evaluate(path, self._element._c_node)
+    def evaluate(self, _path, **variables):
+        """Evaluate an XPath expression on the element.  Variables may
+        be given as keyword arguments. Note that namespaces are
+        currently not supported for variables."""
+        return self._evaluate(_path, self._element._c_node, variables)
 
-def XPathEvaluator(doc_or_element, namespaces=None, extensions=None):
-    if isinstance(doc_or_element, _DocumentBase):
-        return XPathDocumentEvaluator(doc_or_element, namespaces, extensions)
+def XPathEvaluator(etree_or_element, namespaces=None, extensions=None):
+    if isinstance(etree_or_element, _ElementTree):
+        return XPathDocumentEvaluator(etree_or_element, namespaces, extensions)
     else:
-        return XPathElementEvaluator(doc_or_element, namespaces, extensions)
+        return XPathElementEvaluator(etree_or_element, namespaces, extensions)
     
 def Extension(module, function_mapping, ns_uri=None):
     result = {}
@@ -1248,6 +1406,59 @@
         result[(ns_uri, xpath_name)] = getattr(module, function_name)
     return result
 
+cdef class XPath(XPathEvaluatorBase):
+    cdef xpath.xmlXPathCompExpr* _xpath
+    cdef object _prefix_map
+    cdef readonly object path
+
+    def __init__(self, path, namespaces=None, extensions=None):
+        self.path = path
+        path = path.encode('UTF-8')
+        if namespaces is None:
+            path, namespaces = self._nsextract_path(path)
+        XPathEvaluatorBase.__init__(self, namespaces, extensions)
+        self._xpath = xpath.xmlXPathCompile(path)
+        if self._xpath is NULL:
+            raise XPathSyntaxError, "Error in xpath expression."
+
+    def evaluate(self, _etree_or_element, **variables):
+        cdef xpath.xmlXPathContext* xpathCtxt
+        cdef xpath.xmlXPathObject*  xpathObj
+        cdef _Document document
+        cdef _NodeBase element
+
+        if isinstance(_etree_or_element, _ElementTree):
+            document = (<_ElementTree>_etree_or_element)._doc
+            element  = <_NodeBase>(document.getroot())
+        elif isinstance(_etree_or_element, _NodeBase):
+            element  = <_NodeBase>_etree_or_element
+            document = element._doc
+        else:
+            raise ValueError, "Invalid argument, neither document nor element."
+
+        xpathCtxt = xpath.xmlXPathNewContext(document._c_doc)
+        xpathCtxt.node = element._c_node
+        
+        for prefix, uri in self._namespaces.items():
+            self._registerNamespace(xpathCtxt, prefix, uri)
+        if self._extensions is not None:
+            self._registerExtensions(xpathCtxt, self._extensions)
+
+        self._registerVariables(xpathCtxt, variables)
+
+        self._prepare_eval()
+        xpathObj = xpath.xmlXPathCompiledEval(self._xpath, xpathCtxt)
+
+        self._unregisterVariables(xpathCtxt, variables)
+
+        xpath.xmlXPathFreeContext(xpathCtxt)
+
+        return self._handle_result(xpathObj, document)
+
+    def __dealloc__(self):
+        if self._xpath is not NULL:
+            xpath.xmlXPathFreeCompExpr(self._xpath)
+
 cdef xpath.xmlXPathObject* _wrapXPathObject(object obj) except NULL:
     cdef xpath.xmlNodeSet* resultSet
     cdef _NodeBase node
@@ -1277,7 +1488,7 @@
     return NULL
 
 cdef object _unwrapXPathObject(xpath.xmlXPathObject* xpathObj,
-                               _ElementTree doc):
+                               _Document doc):
     if xpathObj.type == xpath.XPATH_UNDEFINED:
         raise XPathResultError, "Undefined xpath result"
     elif xpathObj.type == xpath.XPATH_NODESET:
@@ -1301,7 +1512,7 @@
     else:
         raise XPathResultError, "Unknown xpath result %s" % str(xpathObj.type)
 
-cdef object _createNodeSetResult(_ElementTree doc,
+cdef object _createNodeSetResult(_Document doc,
                                  xpath.xmlXPathObject* xpathObj):
     cdef xmlNode* c_node
     cdef char* s
@@ -1334,7 +1545,7 @@
 
 cdef void _xpathCallback(xpath.xmlXPathParserContext* ctxt, int nargs):
     cdef xpath.xmlXPathContext* rctxt
-    cdef _ElementTree doc
+    cdef _Document doc
     cdef xpath.xmlXPathObject* obj
     cdef XPathDocumentEvaluator evaluator
     
@@ -1380,11 +1591,13 @@
     """
     cdef xslt.xsltStylesheet* _c_style
     
-    def __init__(self, _ElementTree doc):
+    def __init__(self, _ElementTree etree):
         # make a copy of the document as stylesheet needs to assume it
         # doesn't change
         cdef xslt.xsltStylesheet* c_style
         cdef xmlDoc* c_doc
+        cdef _Document doc
+        doc = etree._doc
         c_doc = tree.xmlCopyDoc(doc._c_doc, 1)
         # XXX work around bug in xmlCopyDoc (fix is upcoming in new release
         # of libxml2)
@@ -1401,8 +1614,10 @@
         # this cleans up copy of doc as well
         xslt.xsltFreeStylesheet(self._c_style)
         
-    def apply(self, _ElementTree doc, **kw):
+    def apply(self, _ElementTree etree, **kw):
+        cdef _Document result_doc
         cdef xmlDoc* c_result
+        cdef xmlDoc* c_doc
         cdef char** params
         cdef int i
         cdef int j
@@ -1427,7 +1642,11 @@
             params[i] = NULL
         else:
             params = NULL
-        c_result = xslt.xsltApplyStylesheet(self._c_style, doc._c_doc, params)
+
+        c_doc = _fakeRootDoc(etree._doc._c_doc, etree._context_node._c_node)
+        c_result = xslt.xsltApplyStylesheet(self._c_style, c_doc, params)
+        _destroyFakeDoc(etree._doc._c_doc, c_doc)
+
         if params is not NULL:
             # deallocate space for parameters again
             cstd.free(params)
@@ -1436,15 +1655,21 @@
         # XXX should set special flag to indicate this is XSLT result
         # so that xsltSaveResultTo* functional can be used during
         # serialize?
-        return _elementTreeFactory(c_result)
+        result_doc = _documentFactory(c_result)
+        return _elementTreeFactory(result_doc, None)
 
-    def tostring(self, _ElementTree doc):
+    def tostring(self, _ElementTree etree):
         """Save result doc to string using stylesheet as guidance.
         """
+        cdef xmlDoc* c_doc
         cdef char* s
         cdef int l
         cdef int r
-        r = xslt.xsltSaveResultToString(&s, &l, doc._c_doc, self._c_style)
+        # XXX is this actually necessary? what is xsltSaveResultToString doing??
+        c_doc = _fakeRootDoc(etree._doc._c_doc, etree._context_node._c_node)
+        r = xslt.xsltSaveResultToString(&s, &l, c_doc, self._c_style)
+        _destroyFakeDoc(etree._doc._c_doc, c_doc)
+
         if r == -1:
             raise XSLTSaveError, "Error saving stylesheet result to string"
         result = funicode(s)
@@ -1457,11 +1682,12 @@
     """
     cdef relaxng.xmlRelaxNG* _c_schema
     
-    def __init__(self, _ElementTree tree=None, file=None):
+    def __init__(self, _ElementTree etree=None, file=None):
+        cdef _Document doc
         cdef relaxng.xmlRelaxNGParserCtxt* parser_ctxt
-                    
-        if tree is not None:
-            parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(tree._c_doc)
+
+        if etree is not None:
+            parser_ctxt = relaxng.xmlRelaxNGNewDocParserCtxt(etree._doc._c_doc)
         elif file is not None:
             filename = _getFilenameForFile(file)
             if filename is None:
@@ -1481,14 +1707,19 @@
     def __dealloc__(self):
         relaxng.xmlRelaxNGFree(self._c_schema)
         
-    def validate(self, _ElementTree doc):
+    def validate(self, _ElementTree etree):
         """Validate doc using Relax NG.
 
         Returns true if document is valid, false if not."""
+        cdef xmlDoc* c_doc
         cdef relaxng.xmlRelaxNGValidCtxt* valid_ctxt
         cdef int ret
         valid_ctxt = relaxng.xmlRelaxNGNewValidCtxt(self._c_schema)
-        ret = relaxng.xmlRelaxNGValidateDoc(valid_ctxt, doc._c_doc)
+
+        c_doc = _fakeRootDoc(etree._doc._c_doc, etree._context_node._c_node)
+        ret = relaxng.xmlRelaxNGValidateDoc(valid_ctxt, c_doc)
+        _destroyFakeDoc(etree._doc._c_doc, c_doc)
+
         relaxng.xmlRelaxNGFreeValidCtxt(valid_ctxt)
         if ret == -1:
             raise RelaxNGValidateError, "Internal error in Relax NG validation"
@@ -1499,29 +1730,36 @@
     """
     cdef xmlschema.xmlSchema* _c_schema
     
-    def __init__(self, _ElementTree tree):
+    def __init__(self, _ElementTree etree):
+        cdef _Document doc
         cdef xmlschema.xmlSchemaParserCtxt* parser_ctxt
-        parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(tree._c_doc)
+        doc = etree._doc
+        parser_ctxt = xmlschema.xmlSchemaNewDocParserCtxt(doc._c_doc)
         if parser_ctxt is NULL:
-            raise XMLSchemaParseError, "Document is not valid XML Schema"
+            raise XMLSchemaParseError, "Document is accepted as XML Schema"
         self._c_schema = xmlschema.xmlSchemaParse(parser_ctxt)
         if self._c_schema is NULL:
             xmlschema.xmlSchemaFreeParserCtxt(parser_ctxt)
-            raise XMLSchemaParseError, "Document is not valid XML Schema"
+            raise XMLSchemaParseError, "Document is not parsable as XML Schema"
         xmlschema.xmlSchemaFreeParserCtxt(parser_ctxt)
         
     def __dealloc__(self):
         xmlschema.xmlSchemaFree(self._c_schema)
 
-    def validate(self, _ElementTree doc):
+    def validate(self, _ElementTree etree):
         """Validate doc using XML Schema.
 
         Returns true if document is valid, false if not.
         """
         cdef xmlschema.xmlSchemaValidCtxt* valid_ctxt
+        cdef xmlDoc* c_doc
         cdef int ret
         valid_ctxt = xmlschema.xmlSchemaNewValidCtxt(self._c_schema)
-        ret = xmlschema.xmlSchemaValidateDoc(valid_ctxt, doc._c_doc)
+
+        c_doc = _fakeRootDoc(etree._doc._c_doc, etree._context_node._c_node)
+        ret = xmlschema.xmlSchemaValidateDoc(valid_ctxt, c_doc)
+        _destroyFakeDoc(etree._doc._c_doc, c_doc)
+
         xmlschema.xmlSchemaFreeValidCtxt(valid_ctxt)
         if ret == -1:
             raise XMLSchemaValidateError, "Internal error in XML Schema validation."
@@ -1643,7 +1881,202 @@
 cdef Parser theParser
 theParser = Parser()
 
+# SAX compatible I/O
+
+cdef class SaxTreeBuilder:
+    cdef _Element _root
+    cdef object _element_stack
+    cdef object _default_ns
+    cdef object _ns_mapping
+    cdef object __ns_template
+
+    def __init__(self):
+        self._root = None
+        self._element_stack = []
+        self._default_ns = None
+        self._ns_mapping = {}
+        self.__ns_template = unicode("{%s}%s")
+
+    property etree:
+        "Contains the generated ElementTree after parsing is finished."
+        def __get__(self):
+            return ElementTree(self._root)
+
+    def startDocument(self):
+        pass
+    def endDocument(self):
+        pass
+
+    def startPrefixMapping(self, prefix, uri):
+        if prefix is None:
+            self._default_ns = uri
+        else:
+            self._ns_mapping[prefix] = uri
+    def endPrefixMapping(self, prefix):
+        if prefix is None:
+            self._default_ns = None
+        else:
+            del self._ns_mapping[prefix]
+
+    def startElementNS(self, tag_tuple, qname, attributes):
+        if tag_tuple[0]:
+            el_name = self.__ns_template % tag_tuple
+        elif self._default_ns:
+            el_name = self.__ns_template % (tag_tuple[1], self._default_ns)
+        else:
+            el_name = tag_tuple[1]
+
+        try:
+            iter_attributes = attributes.iteritems()
+        except AttributeError:
+            iter_attributes = attributes.items()
+
+        attrs = {}
+        for name_tuple, value in iter_attributes:
+            if name_tuple[0]:
+                attr_name = self.__ns_template % name_tuple
+            else:
+                attr_name = name_tuple[1]
+            attrs[attr_name] = value
+
+        element_stack = self._element_stack
+        if self._root is None:
+            element = self._root = Element(el_name, attrs, self._ns_mapping)
+        else:
+            element = SubElement(element_stack[-1], el_name, attrs, self._ns_mapping)
+        element_stack.append(element)
+    def endElementNS(self, tag, qname):
+        self._element_stack.pop()
+
+    def characters(self, data):
+        element = self._element_stack[-1]
+        try:
+            element = element[-1]
+            element.tail = (element.tail or unicode()) + data
+        except IndexError:
+            element.text = (element.text or unicode()) + data
+
+cdef _saxify(_Element element, content_handler):
+    from xml.sax.xmlreader import AttributesNSImpl as attr_class
+    empty_attributes = attr_class({}, {})
+
+    _recursive_saxify(element, content_handler, {}, empty_attributes, attr_class)
+
+cdef object _build_qname(namespace, local_name, prefixes, new_prefixes):
+    if namespace is None:
+        return local_name
+    try:
+        prefix = prefixes[namespace]
+    except KeyError:
+        prefix = prefixes[namespace] = unicode('ns%02d') % len(prefixes)
+        new_prefixes.append( (prefix, namespace) )
+    return prefix + ':' + local_name
+
+cdef _recursive_saxify(_Element element, content_handler, prefixes, empty_attributes, attr_class):
+    new_prefixes = []
+    if element.attrib:
+        attr_values = {}
+        attr_qnames = {}
+        for attr_ns_name, value in element.attrib.items():
+            attr_ns_tuple = _getNsTag(attr_ns_name)
+            attr_values[attr_ns_tuple] = value
+            attr_qnames[attr_ns_tuple] = _build_qname(attr_ns_tuple[0], attr_ns_tuple[1],
+                                                      prefixes, new_prefixes)
+        sax_attributes = attr_class(attr_values, attr_qnames)
+    else:
+        sax_attributes = empty_attributes
+
+    namespace, tag = _getNsTag(element.tag)
+    qname = _build_qname(namespace, tag, prefixes, new_prefixes)
+
+    for prefix, uri in new_prefixes:
+        content_handler.startPrefixMapping(prefix, uri)
+    content_handler.startElementNS((namespace, tag), qname, sax_attributes)
+    if element.text:
+        content_handler.characters(element.text)
+    for child in element:
+        _recursive_saxify(child, content_handler, prefixes, empty_attributes, attr_class)
+    content_handler.endElementNS((namespace, tag), qname)
+    for prefix, uri in new_prefixes:
+        content_handler.endPrefixMapping(prefix)
+    if element.tail:
+        content_handler.characters(element.tail)
+
 # Private helper functions
+__NAMESPACE_CLASSES = {}
+
+cdef object _find_element_class(char* c_namespace_utf, char* c_element_name_utf):
+    element_name_utf = c_element_name_utf
+    if c_namespace_utf == NULL:
+        if element_name_utf[:1] == '{':
+            namespace_utf, element_name_utf = element_name_utf[1:].split('}', 1)
+        else:
+            namespace_utf = None
+    else:
+        namespace_utf = c_namespace_utf
+
+    try:
+        class_dict = __NAMESPACE_CLASSES[namespace_utf]
+    except KeyError:
+        return _Element
+    try:
+        return class_dict[element_name_utf]
+    except KeyError:
+        pass
+    try:
+        return class_dict[None]
+    except KeyError:
+        return _Element
+
+cdef object _build_namespace_impl_dict(class_dict):
+    d = {}
+    for name, cls in class_dict.iteritems():
+        if issubclass(cls, _Element):
+            if name is None:
+                name_utf = None
+            else:
+                name_utf = name.encode('UTF-8')
+            d[name_utf] = cls
+    return d
+
+cdef object _stripXmlPI(object text):
+    if text[:5] == '<?xml':
+        i = text.find('?>')
+        if i != -1:
+            if text[i+2:i+3] == '\n':
+                i = i+1
+            text = text[i + 2:]
+    return text
+
+cdef xmlDoc* _fakeRootDoc(xmlDoc* c_base_doc, xmlNode* c_node):
+    # build a temporary document that has the given node as root node
+    # note that copy and original must not be modified during its lifetime!!
+    # always call _destroyFakeDoc() after use!
+    cdef xmlNode* c_root
+    cdef xmlDoc*  c_doc
+    c_root = tree.xmlDocGetRootElement(c_base_doc)
+    if c_root == c_node:
+        # already the root node
+        return c_base_doc
+
+    c_doc  = tree.xmlCopyDoc(c_base_doc, 0)        # non recursive!
+    c_root = tree.xmlDocCopyNode(c_node, c_doc, 2) # non recursive!
+
+    c_root.children = c_node.children
+    c_root.last = c_node.last
+    c_root.next = c_root.prev = c_root.parent = NULL
+
+    c_doc.children = c_root
+    return c_doc
+
+cdef void _destroyFakeDoc(xmlDoc* c_base_doc, xmlDoc* c_doc):
+    # delete a temporary document
+    cdef xmlNode* c_root
+    if c_doc != c_base_doc:
+        c_root = tree.xmlDocGetRootElement(c_doc)
+        c_root.children = c_root.last = NULL # prevent recursive removal of children
+        tree.xmlFreeDoc(c_doc)
+
 cdef _dumpToFile(f, xmlDoc* c_doc, xmlNode* c_node):
     cdef tree.PyObject* o
     cdef tree.xmlOutputBuffer* c_buffer
@@ -1940,7 +2373,7 @@
 
 
 cdef void changeDocumentBelow(_NodeBase node,
-                              _DocumentBase doc):
+                              _Document doc):
     """For a node and all nodes below, change document.
 
     A node can change document in certain operations as an XML
@@ -1952,7 +2385,7 @@
     tree.xmlReconciliateNs(doc._c_doc, node._c_node)
     
 cdef void changeDocumentBelowHelper(xmlNode* c_node,
-                                    _DocumentBase doc):
+                                    _Document doc):
     cdef ProxyRef* ref
     cdef xmlNode* c_current
     cdef xmlAttr* c_attr_current
Index: src/lxml/xinclude.pxd
===================================================================
--- src/lxml/xinclude.pxd	(Revision 18892)
+++ src/lxml/xinclude.pxd	(Arbeitskopie)
@@ -1,6 +1,7 @@
-from tree cimport xmlDoc
+from tree cimport xmlDoc, xmlNode
 
 cdef extern from "libxml/xinclude.h":
     
     cdef int xmlXIncludeProcess(xmlDoc* doc)
+    cdef int xmlXIncludeProcessTree(xmlNode* doc)
     
Index: src/lxml/tree.pxd
===================================================================
--- src/lxml/tree.pxd	(Revision 18892)
+++ src/lxml/tree.pxd	(Arbeitskopie)
@@ -152,6 +152,7 @@
                                 int format, char* encoding)
     cdef void xmlNodeSetName(xmlNode* cur, char* name)
     cdef xmlDoc* xmlCopyDoc(xmlDoc* doc, int recursive)
+    cdef xmlNode* xmlCopyNode(xmlNode* node, int extended)
     cdef int xmlReconciliateNs(xmlDoc* doc, xmlNode* tree)
     cdef xmlBuffer* xmlBufferCreate()
     cdef char* xmlBufferContent(xmlBuffer* buf)
Index: src/lxml/xpath.pxd
===================================================================
--- src/lxml/xpath.pxd	(Revision 18892)
+++ src/lxml/xpath.pxd	(Arbeitskopie)
@@ -42,6 +42,9 @@
         tree.xmlNode* ancestor
         int error
 
+    ctypedef struct xmlXPathCompExpr:
+        pass
+
     ctypedef void (*xmlXPathFunction)(xmlXPathParserContext* ctxt, int nargs)
     ctypedef xmlXPathFunction (*xmlXPathFuncLookupFunc)(void* ctxt,
                                                         char* name,
@@ -50,12 +53,17 @@
     cdef xmlXPathContext* xmlXPathNewContext(tree.xmlDoc* doc)
     cdef xmlXPathObject* xmlXPathEvalExpression(char* str,
                                                 xmlXPathContext* ctxt)
+    cdef xmlXPathObject* xmlXPathCompiledEval(xmlXPathCompExpr* comp,
+                                              xmlXPathContext* ctxt)
+    cdef xmlXPathCompExpr* xmlXPathCompile(char* str)
     cdef void xmlXPathFreeContext(xmlXPathContext* ctxt)
+    cdef void xmlXPathFreeCompExpr(xmlXPathCompExpr* comp)
     cdef void xmlXPathFreeObject(xmlXPathObject* obj)
     cdef int xmlXPathRegisterNs(xmlXPathContext* ctxt,
                                 char* prefix, char* ns_uri)
     
     cdef xmlNodeSet* xmlXPathNodeSetCreate(tree.xmlNode* val)
+    cdef void xmlXPathFreeNodeSet(xmlNodeSet* val)
 
 
 cdef extern from "libxml/xpathInternals.h":
@@ -69,6 +77,15 @@
     cdef void xmlXPathRegisterFuncLookup(xmlXPathContext *ctxt,
 					 xmlXPathFuncLookupFunc f,
 					 void *funcCtxt)
+    cdef int xmlXPathRegisterVariable(xmlXPathContext *ctxt, 
+					 char* name,
+					 xmlXPathObject* value)
+    cdef int xmlXPathRegisterVariableNS(xmlXPathContext *ctxt, 
+					 char* name, 
+					 char* ns_uri, 
+					 xmlXPathObject* value)
+    cdef xmlXPathObject* xmlXPathVariableLookup(xmlXPathContext* ctxt,
+						char* name)
     cdef xmlXPathObject* valuePop (xmlXPathParserContext *ctxt)
     cdef int valuePush(xmlXPathParserContext* ctxt, xmlXPathObject *value)
     
