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);
+}