applied patch from Stephane Bidoul for structured error handling from

* python/libxml2-python-api.xml python/libxml_wrap.h python/types.c
  python/tests/Makefile.am python/tests/tstLastError.py: applied
  patch from Stephane Bidoul for structured error handling from
  python, and the associated test
Daniel
diff --git a/python/generator.py b/python/generator.py
index ce0b8f3..db7be28 100755
--- a/python/generator.py
+++ b/python/generator.py
@@ -268,6 +268,7 @@
     'xmlCatalogPtr': ('O', "catalog", "xmlCatalogPtr", "xmlCatalogPtr"),
     'FILE *': ('O', "File", "FILEPtr", "FILE *"),
     'xmlURIPtr': ('O', "URI", "xmlURIPtr", "xmlURIPtr"),
+    'xmlErrorPtr': ('O', "Error", "xmlErrorPtr", "xmlErrorPtr"),
     'xmlOutputBufferPtr': ('O', "outputBuffer", "xmlOutputBufferPtr", "xmlOutputBufferPtr"),
     'xmlParserInputBufferPtr': ('O', "inputBuffer", "xmlParserInputBufferPtr", "xmlParserInputBufferPtr"),
     'xmlRegexpPtr': ('O', "xmlReg", "xmlRegexpPtr", "xmlRegexpPtr"),
@@ -641,6 +642,7 @@
     "htmlParserCtxt *": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"),
     "xmlCatalogPtr": ("._o", "catalog(_obj=%s)", "catalog"),
     "xmlURIPtr": ("._o", "URI(_obj=%s)", "URI"),
+    "xmlErrorPtr": ("._o", "Error(_obj=%s)", "Error"),
     "xmlOutputBufferPtr": ("._o", "outputBuffer(_obj=%s)", "outputBuffer"),
     "xmlParserInputBufferPtr": ("._o", "inputBuffer(_obj=%s)", "inputBuffer"),
     "xmlRegexpPtr": ("._o", "xmlReg(_obj=%s)", "xmlReg"),
@@ -721,6 +723,9 @@
     elif name[0:9] == "xmlURISet" and file == "python_accessor":
         func = name[6:]
         func = string.lower(func[0:1]) + func[1:]
+    elif name[0:11] == "xmlErrorGet" and file == "python_accessor":
+        func = name[11:]
+        func = string.lower(func[0:1]) + func[1:]
     elif name[0:17] == "xmlXPathParserGet" and file == "python_accessor":
         func = name[17:]
         func = string.lower(func[0:1]) + func[1:]
diff --git a/python/libxml2-python-api.xml b/python/libxml2-python-api.xml
index 7e8e922..d5a4cbe 100644
--- a/python/libxml2-python-api.xml
+++ b/python/libxml2-python-api.xml
@@ -275,5 +275,36 @@
       <arg name='URI' type='xmlURIPtr' info='the URI'/>
       <arg name='port' type='int' info='The URI port part'/>
     </function>
+    <!-- xmlErrorPtr accessors -->
+    <function name='xmlErrorGetDomain' file='python_accessor'>
+      <info>What part of the library raised this error</info>
+      <return type='int' info="The error domain" field="domain"/>
+      <arg name='Error' type='xmlErrorPtr' info='the Error'/>
+    </function>
+    <function name='xmlErrorGetCode' file='python_accessor'>
+      <info>The error code, e.g. an xmlParserError</info>
+      <return type='int' info="The error code" field="code"/>
+      <arg name='Error' type='xmlErrorPtr' info='the Error'/>
+    </function>
+    <function name='xmlErrorGetMessage' file='python_accessor'>
+      <info>human-readable informative error message</info>
+      <return type='const char *' info="The error message" field="message"/>
+      <arg name='Error' type='xmlErrorPtr' info='the Error'/>
+    </function>
+    <function name='xmlErrorGetLevel' file='python_accessor'>
+      <info>how consequent is the error</info>
+      <return type='int' info="The error level" field="level"/>
+      <arg name='Error' type='xmlErrorPtr' info='the Error'/>
+    </function>
+    <function name='xmlErrorGetFile' file='python_accessor'>
+      <info>the filename</info>
+      <return type='const char *' info="The error file" field="file"/>
+      <arg name='Error' type='xmlErrorPtr' info='the Error'/>
+    </function>
+    <function name='xmlErrorGetLine' file='python_accessor'>
+      <info>the line number if available</info>
+      <return type='int' info="The error line" field="line"/>
+      <arg name='Error' type='xmlErrorPtr' info='the Error'/>
+    </function>
   </symbols>
 </api>
diff --git a/python/libxml2class.txt b/python/libxml2class.txt
index eebdf6f..8325abc 100644
--- a/python/libxml2class.txt
+++ b/python/libxml2class.txt
@@ -203,6 +203,7 @@
 registerHTTPPostCallbacks()
 
 # functions from module xmlerror
+lastError()
 resetLastError()
 
 # functions from module xmlreader
@@ -779,6 +780,18 @@
 
     # functions from module xmlreader
     RelaxNGSetSchema()
+Class Error()
+    # accessors
+    code()
+    domain()
+    file()
+    level()
+    line()
+    message()
+
+    # functions from module xmlerror
+    copyError()
+    resetError()
 Class relaxNgValidCtxt()
 
     # functions from module relaxng
diff --git a/python/libxml_wrap.h b/python/libxml_wrap.h
index 064f8a8..09fc77f 100644
--- a/python/libxml_wrap.h
+++ b/python/libxml_wrap.h
@@ -109,6 +109,14 @@
 
 typedef struct {
     PyObject_HEAD
+    xmlErrorPtr obj;
+} PyError_Object;
+
+#define PyError_Get(v) (((v) == Py_None) ? NULL : \
+	(((PyError_Object *)(v))->obj))
+
+typedef struct {
+    PyObject_HEAD
     xmlOutputBufferPtr obj;
 } PyoutputBuffer_Object;
 
@@ -194,3 +202,4 @@
 PyObject * libxml_xmlRelaxNGParserCtxtPtrWrap(xmlRelaxNGParserCtxtPtr ctxt);
 PyObject * libxml_xmlRelaxNGValidCtxtPtrWrap(xmlRelaxNGValidCtxtPtr valid);
 #endif /* LIBXML_SCHEMAS_ENABLED */
+PyObject * libxml_xmlErrorPtrWrap(xmlErrorPtr error);
diff --git a/python/tests/Makefile.am b/python/tests/Makefile.am
index fd704fd..c496dc8 100644
--- a/python/tests/Makefile.am
+++ b/python/tests/Makefile.am
@@ -31,7 +31,8 @@
     ctxterror.py\
     readererr.py\
     relaxng.py	\
-    thread2.py
+    thread2.py \
+    tstLastError.py
 
 XMLS=		\
     tst.xml	\
diff --git a/python/tests/tstLastError.py b/python/tests/tstLastError.py
new file mode 100755
index 0000000..12229e8
--- /dev/null
+++ b/python/tests/tstLastError.py
@@ -0,0 +1,72 @@
+#!/usr/bin/python -u
+import sys, unittest
+
+import libxml2
+
+class TestCase(unittest.TestCase):
+
+    def setUp(self):
+        libxml2.debugMemory(1)
+
+    def tearDown(self):
+        libxml2.cleanupParser()
+        if libxml2.debugMemory(1) != 0:
+            libxml2.dumpMemory() 
+            self.fail("Memory leak %d bytes" % (libxml2.debugMemory(1),))
+
+    def failUnlessXmlError(self,f,args,exc,domain,code,message,level,file,line):
+        """Run function f, with arguments args and expect an exception exc;
+        when the exception is raised, check the libxml2.lastError for
+        expected values."""
+        # disable the default error handler
+        libxml2.registerErrorHandler(None,None)
+        try:
+            f(*args)
+        except exc:
+            e = libxml2.lastError()
+            if e is None:
+                self.fail("lastError not set")
+            if 0:
+                print "domain = ",e.domain()
+                print "code = ",e.code()
+                print "message =",repr(e.message())
+                print "level =",e.level()
+                print "file =",e.file()
+                print "line =",e.line()
+                print
+            self.failUnlessEqual(domain,e.domain())
+            self.failUnlessEqual(code,e.code())
+            self.failUnlessEqual(message,e.message())
+            self.failUnlessEqual(level,e.level())
+            self.failUnlessEqual(file,e.file())
+            self.failUnlessEqual(line,e.line())
+        else:
+            self.fail("exception %s should have been raised" % exc)
+
+    def test1(self):
+        """Test readFile with a file that does not exist"""
+        self.failUnlessXmlError(libxml2.readFile,
+                        ("dummy.xml",None,0),
+                        libxml2.treeError,
+                        domain=8, # XML_FROM_IO
+                        code=1549, # XML_IO_LOAD_ERROR
+                        message='failed to load external entity "dummy.xml"\n',
+                        level=1, # XML_ERR_WARNING
+                        file=None,
+                        line=0)
+
+    def test2(self):
+        """Test a well-formedness error: we get the last error only"""
+        s = "<x>\n<a>\n</x>"
+        self.failUnlessXmlError(libxml2.readMemory,
+                        (s,len(s),"dummy.xml",None,0),
+                        libxml2.treeError,
+                        domain=1, # XML_FROM_PARSER
+                        code=77, # XML_ERR_TAG_NOT_FINISHED
+                        message='Premature end of data in tag x line 1\n',
+                        level=3, # XML_ERR_FATAL
+                        file='dummy.xml',
+                        line=3)
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/python/types.c b/python/types.c
index a971791..42279d0 100644
--- a/python/types.c
+++ b/python/types.c
@@ -641,3 +641,21 @@
     return (ret);
 }
 #endif /* LIBXML_SCHEMAS_ENABLED */
+
+PyObject *
+libxml_xmlErrorPtrWrap(xmlErrorPtr error)
+{
+    PyObject *ret;
+
+#ifdef DEBUG
+    printf("libxml_xmlErrorPtrWrap: error = %p\n", error);
+#endif
+    if (error == NULL) {
+        Py_INCREF(Py_None);
+        return (Py_None);
+    }
+    ret =
+        PyCObject_FromVoidPtrAndDesc((void *) error,
+                                     (char *) "xmlErrorPtr", NULL);
+    return (ret);
+}