diff --git a/ChangeLog b/ChangeLog index a43de442..2c5064ca 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Mon Jan 20 23:25:00 CET 2003 Daniel Veillard + + * xmlreader.c python/drv_libxml2.py python/generator.py + python/libxml.c python/libxml.py python/libxml_wrap.h + python/types.c: patch from Stéphane Bidoul for better per + context error message APIs + * python/tests/ctxterror.py python/tests/readererr.py: + update of the tests + Sun Jan 19 17:09:28 MST 2003 John Fleck * doc/guidelines.html diff --git a/include/libxml/xmlreader.h b/include/libxml/xmlreader.h index 53314b1b..e9475a77 100644 --- a/include/libxml/xmlreader.h +++ b/include/libxml/xmlreader.h @@ -24,11 +24,11 @@ typedef enum { } xmlParserProperties; typedef enum { - XMLREADER_SEVERITY_VALIDITY_WARNING = 1, - XMLREADER_SEVERITY_VALIDITY_ERROR = 2, - XMLREADER_SEVERITY_WARNING = 3, - XMLREADER_SEVERITY_ERROR = 4 -} xmlReaderSeverities; + XML_PARSER_SEVERITY_VALIDITY_WARNING = 1, + XML_PARSER_SEVERITY_VALIDITY_ERROR = 2, + XML_PARSER_SEVERITY_WARNING = 3, + XML_PARSER_SEVERITY_ERROR = 4 +} xmlParserSeverities; typedef struct _xmlTextReader xmlTextReader; typedef xmlTextReader *xmlTextReaderPtr; @@ -110,12 +110,14 @@ xmlDocPtr xmlTextReaderCurrentDoc (xmlTextReaderPtr reader); /* * Error handling extensions */ -typedef void (*xmlTextReaderErrorFunc) (void *arg, +typedef void * xmlTextReaderLocatorPtr; +typedef void (*xmlTextReaderErrorFunc) (void *arg, const char *msg, - int line, - int col, - const char *URI, - xmlReaderSeverities severity); + xmlParserSeverities severity, + xmlTextReaderLocatorPtr locator); +int xmlTextReaderLocatorLineNumber (xmlTextReaderLocatorPtr locator); +/*int xmlTextReaderLocatorLinePosition(xmlTextReaderLocatorPtr locator);*/ +xmlChar * xmlTextReaderLocatorBaseURI (xmlTextReaderLocatorPtr locator); void xmlTextReaderSetErrorHandler (xmlTextReaderPtr reader, xmlTextReaderErrorFunc f, void *arg); diff --git a/python/drv_libxml2.py b/python/drv_libxml2.py index c9951e6e..977c8686 100644 --- a/python/drv_libxml2.py +++ b/python/drv_libxml2.py @@ -9,10 +9,10 @@ USAGE CAVEATS - Lexical handlers are supported, except for start/endEntity (waiting for XmlReader.ResolveEntity) and start/endDTD - - as understand it, libxml2 error handlers are globals (per thread); - each call to parse() registers a new error handler, - overwriting any previously registered handler - --> you can't have 2 LibXml2Reader active at the same time + - Error callbacks are not exactly synchronous, they tend + to be invoked before the corresponding content callback, + because the underlying reader interface parses + data by chunks of 512 bytes TODO - search for TODO @@ -34,7 +34,7 @@ TODO """ __author__ = u"Stéphane Bidoul " -__version__ = "0.2" +__version__ = "0.3" import codecs import sys @@ -69,15 +69,27 @@ except ImportError, e: raise SAXReaderNotAvailable("libxml2 not available: " \ "import error was: %s" % e) -def _registerErrorHandler(handler): - if not sys.modules.has_key('libxslt'): - # normal behaviour when libxslt is not imported - libxml2.registerErrorHandler(handler,"drv_libxml2") - else: - # when libxslt is imported, one must - # use libxst's error handler instead (see libxml2 bug 102181) - import libxslt - libxslt.registerErrorHandler(handler,"drv_libxml2") +class Locator(xmlreader.Locator): + """SAX Locator adapter for libxml2.xmlTextReaderLocator""" + + def __init__(self,locator): + self.__locator = locator + + def getColumnNumber(self): + "Return the column number where the current event ends." + return -1 + + def getLineNumber(self): + "Return the line number where the current event ends." + return self.__locator.LineNumber() + + def getPublicId(self): + "Return the public identifier for the current event." + return None + + def getSystemId(self): + "Return the system identifier for the current event." + return self.__locator.BaseURI() class LibXml2Reader(xmlreader.XMLReader): @@ -95,21 +107,30 @@ class LibXml2Reader(xmlreader.XMLReader): # error messages accumulator self.__errors = None - def _errorHandler(self,ctx,str): + def _errorHandler(self,arg,msg,severity,locator): if self.__errors is None: self.__errors = [] - self.__errors.append(str) + self.__errors.append((severity, + SAXParseException(msg,None, + Locator(locator)))) - def _reportError(self,callback): - # TODO: use SAXParseException, but we need a Locator for that - # TODO: distinguish warnings from errors - msg = "".join(self.__errors) + def _reportErrors(self,fatal): + for severity,exception in self.__errors: + if severity in (libxml2.PARSER_SEVERITY_VALIDITY_WARNING, + libxml2.PARSER_SEVERITY_WARNING): + self._err_handler.warning(exception) + else: + # when fatal is set, the parse will stop; + # we consider that the last error reported + # is the fatal one. + if fatal and exception is self.__errors[-1][1]: + self._err_handler.fatalError(exception) + else: + self._err_handler.error(exception) self.__errors = None - callback(SAXException(msg)) def parse(self, source): self.__parsing = 1 - _registerErrorHandler(self._errorHandler) try: # prepare source and create reader if type(source) in StringTypes: @@ -118,6 +139,7 @@ class LibXml2Reader(xmlreader.XMLReader): source = saxutils.prepare_input_source(source) input = libxml2.inputBuffer(source.getByteStream()) reader = input.newTextReader(source.getSystemId()) + reader.SetErrorHandler(self._errorHandler,None) # configure reader reader.SetParserProp(libxml2.PARSER_LOADDTD,1) reader.SetParserProp(libxml2.PARSER_DEFAULTATTRS,1) @@ -137,21 +159,18 @@ class LibXml2Reader(xmlreader.XMLReader): # check for errors if r == 1: if not self.__errors is None: - # non-fatal error - self._reportError(self._err_handler.error) + self._reportErrors(0) elif r == 0: if not self.__errors is None: - # non-fatal error - self._reportError(self._err_handler.error) - break + self._reportErrors(0) + break # end of parse else: - # fatal error if not self.__errors is None: - self._reportError(self._err_handler.fatalError) + self._reportErrors(1) else: self._err_handler.fatalError(\ SAXException("Read failed (no details available)")) - break + break # fatal parse error # get node type nodeType = reader.NodeType() # Element @@ -180,6 +199,7 @@ class LibXml2Reader(xmlreader.XMLReader): _d(reader.LocalName())) qnames[attName] = qname attrs[attName] = value + reader.MoveToElement() self._cont_handler.startElementNS( \ eltName,eltQName,attributesNSImpl) if reader.IsEmptyElement(): @@ -194,6 +214,7 @@ class LibXml2Reader(xmlreader.XMLReader): while reader.MoveToNextAttribute(): attName = _d(reader.Name()) attrs[attName] = _d(reader.Value()) + reader.MoveToElement() self._cont_handler.startElement( \ eltName,attributesImpl) if reader.IsEmptyElement(): @@ -275,7 +296,6 @@ class LibXml2Reader(xmlreader.XMLReader): reader.Close() finally: self.__parsing = 0 - # TODO: unregister error handler? def setDTDHandler(self, handler): # TODO (when supported, the inherited method works just fine) diff --git a/python/generator.py b/python/generator.py index cc1b41b0..20e89b79 100755 --- a/python/generator.py +++ b/python/generator.py @@ -271,6 +271,7 @@ py_types = { 'xmlOutputBufferPtr': ('O', "outputBuffer", "xmlOutputBufferPtr", "xmlOutputBufferPtr"), 'xmlParserInputBufferPtr': ('O', "inputBuffer", "xmlParserInputBufferPtr", "xmlParserInputBufferPtr"), 'xmlRegexpPtr': ('O', "xmlReg", "xmlRegexpPtr", "xmlRegexpPtr"), + 'xmlTextReaderLocatorPtr': ('O', "xmlTextReaderLocator", "xmlTextReaderLocatorPtr", "xmlTextReaderLocatorPtr"), 'xmlTextReaderPtr': ('O', "xmlTextReader", "xmlTextReaderPtr", "xmlTextReaderPtr"), } @@ -602,6 +603,7 @@ classes_type = { "xmlOutputBufferPtr": ("._o", "outputBuffer(_obj=%s)", "outputBuffer"), "xmlParserInputBufferPtr": ("._o", "inputBuffer(_obj=%s)", "inputBuffer"), "xmlRegexpPtr": ("._o", "xmlReg(_obj=%s)", "xmlReg"), + "xmlTextReaderLocatorPtr": ("._o", "xmlTextReaderLocator(_obj=%s)", "xmlTextReaderLocator"), "xmlTextReaderPtr": ("._o", "xmlTextReader(_obj=%s)", "xmlTextReader"), } @@ -690,6 +692,8 @@ def nameFixup(name, classe, type, file): func = "regexp" + name[9:] elif name[0:6] == "xmlReg" and file == "xmlregexp": func = "regexp" + name[6:] + elif name[0:20] == "xmlTextReaderLocator" and file == "xmlreader": + func = name[20:] elif name[0:13] == "xmlTextReader" and file == "xmlreader": func = name[13:] elif name[0:11] == "xmlACatalog": diff --git a/python/libxml.c b/python/libxml.c index a762ac7e..06039501 100644 --- a/python/libxml.c +++ b/python/libxml.c @@ -1177,32 +1177,6 @@ libxml_htmlSAXParseFile(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) return (Py_None); } -PyObject * -libxml_xmlFreeParserCtxt(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { - xmlParserCtxtPtr ctxt; - PyObject *pyobj_ctxt; - xmlParserCtxtPyCtxtPtr pyCtxt; - - if (!PyArg_ParseTuple(args, (char *)"O:xmlFreeParserCtxt", &pyobj_ctxt)) - return(NULL); - ctxt = (xmlParserCtxtPtr) PyparserCtxt_Get(pyobj_ctxt); - - if (ctxt != NULL) { - pyCtxt = (xmlParserCtxtPyCtxtPtr)((xmlParserCtxtPtr)ctxt)->_private; - if (pyCtxt) { - Py_XDECREF(pyCtxt->errorFunc); - Py_XDECREF(pyCtxt->errorFuncArg); - Py_XDECREF(pyCtxt->warningFunc); - Py_XDECREF(pyCtxt->warningFuncArg); - xmlFree(pyCtxt); - } - xmlFreeParserCtxt(ctxt); - } - - Py_INCREF(Py_None); - return(Py_None); -} - /************************************************************************ * * * Error message callback * @@ -1332,44 +1306,87 @@ libxml_xmlRegisterErrorHandler(ATTRIBUTE_UNUSED PyObject * self, * * ************************************************************************/ -static void -libxml_xmlParserCtxtErrorFuncHandler(void *ctxt, const char *msg, ...) +typedef struct +{ + PyObject *f; + PyObject *arg; +} xmlParserCtxtPyCtxt; +typedef xmlParserCtxtPyCtxt *xmlParserCtxtPyCtxtPtr; + +static void +libxml_xmlParserCtxtGenericErrorFuncHandler(void *ctx, int severity, char *str) { - char *str; - va_list ap; PyObject *list; - PyObject *message; PyObject *result; + xmlParserCtxtPtr ctxt; xmlParserCtxtPyCtxtPtr pyCtxt; #ifdef DEBUG_ERROR - printf("libxml_xmlParserCtxtErrorFuncHandler(%p, %s, ...) called\n", ctx, msg); + printf("libxml_xmlParserCtxtGenericErrorFuncHandler(%p, %s, ...) called\n", ctx, msg); #endif - pyCtxt = (xmlParserCtxtPyCtxtPtr)((xmlParserCtxtPtr)ctxt)->_private; + ctxt = (xmlParserCtxtPtr)ctx; + pyCtxt = (xmlParserCtxtPyCtxtPtr)ctxt->_private; - if (pyCtxt->errorFunc == NULL) { - va_start(ap, msg); - vfprintf(stdout, msg, ap); - va_end(ap); - } else { - va_start(ap, msg); - str = libxml_buildMessage(msg,ap); - va_end(ap); - - list = PyTuple_New(2); - PyTuple_SetItem(list, 0, pyCtxt->errorFuncArg); - Py_XINCREF(pyCtxt->errorFuncArg); - message = libxml_charPtrWrap(str); - PyTuple_SetItem(list, 1, message); - result = PyEval_CallObject(pyCtxt->errorFunc, list); - Py_XDECREF(list); - Py_XDECREF(result); + list = PyTuple_New(4); + PyTuple_SetItem(list, 0, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + PyTuple_SetItem(list, 1, libxml_charPtrWrap(str)); + PyTuple_SetItem(list, 2, libxml_intWrap(severity)); + PyTuple_SetItem(list, 3, Py_None); + Py_INCREF(Py_None); + result = PyEval_CallObject(pyCtxt->f, list); + if (result == NULL) + { + /* TODO: manage for the exception to be propagated... */ + PyErr_Print(); } + Py_XDECREF(list); + Py_XDECREF(result); +} + +static void +libxml_xmlParserCtxtErrorFuncHandler(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlParserCtxtGenericErrorFuncHandler(ctx,XML_PARSER_SEVERITY_ERROR,libxml_buildMessage(msg,ap)); + va_end(ap); +} + +static void +libxml_xmlParserCtxtWarningFuncHandler(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlParserCtxtGenericErrorFuncHandler(ctx,XML_PARSER_SEVERITY_WARNING,libxml_buildMessage(msg,ap)); + va_end(ap); +} + +static void +libxml_xmlParserCtxtValidityErrorFuncHandler(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlParserCtxtGenericErrorFuncHandler(ctx,XML_PARSER_SEVERITY_VALIDITY_ERROR,libxml_buildMessage(msg,ap)); + va_end(ap); +} + +static void +libxml_xmlParserCtxtValidityWarningFuncHandler(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlParserCtxtGenericErrorFuncHandler(ctx,XML_PARSER_SEVERITY_VALIDITY_WARNING,libxml_buildMessage(msg,ap)); + va_end(ap); } PyObject * -libxml_xmlSetParserCtxtErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) +libxml_xmlParserCtxtSetErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { PyObject *py_retval; xmlParserCtxtPtr ctxt; @@ -1378,7 +1395,7 @@ libxml_xmlSetParserCtxtErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *a PyObject *pyobj_f; PyObject *pyobj_arg; - if (!PyArg_ParseTuple(args, (char *)"OOO:xmlSetParserCtxtErrorHandler", + if (!PyArg_ParseTuple(args, (char *)"OOO:xmlParserCtxtSetErrorHandler", &pyobj_ctxt, &pyobj_f, &pyobj_arg)) return(NULL); ctxt = (xmlParserCtxtPtr) PyparserCtxt_Get(pyobj_ctxt); @@ -1392,99 +1409,88 @@ libxml_xmlSetParserCtxtErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *a ctxt->_private = pyCtxt; } else { - pyCtxt = ctxt->_private; + pyCtxt = (xmlParserCtxtPyCtxtPtr)ctxt->_private; } /* TODO: check f is a function ! */ - Py_XDECREF(pyCtxt->errorFunc); + Py_XDECREF(pyCtxt->f); Py_XINCREF(pyobj_f); - pyCtxt->errorFunc = pyobj_f; - Py_XDECREF(pyCtxt->errorFuncArg); + pyCtxt->f = pyobj_f; + Py_XDECREF(pyCtxt->arg); Py_XINCREF(pyobj_arg); - pyCtxt->errorFuncArg = pyobj_arg; + pyCtxt->arg = pyobj_arg; - ctxt->sax->error = libxml_xmlParserCtxtErrorFuncHandler; - ctxt->vctxt.error = libxml_xmlParserCtxtErrorFuncHandler; + if (pyobj_f != Py_None) { + ctxt->sax->error = libxml_xmlParserCtxtErrorFuncHandler; + ctxt->sax->warning = libxml_xmlParserCtxtWarningFuncHandler; + ctxt->vctxt.error = libxml_xmlParserCtxtValidityErrorFuncHandler; + ctxt->vctxt.warning = libxml_xmlParserCtxtValidityWarningFuncHandler; + } + else { + ctxt->sax->error = xmlParserError; + ctxt->vctxt.error = xmlParserValidityError; + ctxt->sax->warning = xmlParserWarning; + ctxt->vctxt.warning = xmlParserValidityWarning; + } py_retval = libxml_intWrap(1); return(py_retval); } -static void -libxml_xmlParserCtxtWarningFuncHandler(void *ctxt, const char *msg, ...) -{ - char *str; - va_list ap; - PyObject *list; - PyObject *message; - PyObject *result; - xmlParserCtxtPyCtxtPtr pyCtxt; - -#ifdef DEBUG_ERROR - printf("libxml_xmlParserCtxtWarningFuncHandler(%p, %s, ...) called\n", ctx, msg); -#endif - - pyCtxt = (xmlParserCtxtPyCtxtPtr)((xmlParserCtxtPtr)ctxt)->_private; - - if (pyCtxt->warningFunc == NULL) { - va_start(ap, msg); - vfprintf(stdout, msg, ap); - va_end(ap); - } else { - va_start(ap, msg); - str = libxml_buildMessage(msg,ap); - va_end(ap); - - list = PyTuple_New(2); - PyTuple_SetItem(list, 0, pyCtxt->warningFuncArg); - Py_XINCREF(pyCtxt->warningFuncArg); - message = libxml_charPtrWrap(str); - PyTuple_SetItem(list, 1, message); - result = PyEval_CallObject(pyCtxt->warningFunc, list); - Py_XDECREF(list); - Py_XDECREF(result); - } -} - PyObject * -libxml_xmlSetParserCtxtWarningHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) +libxml_xmlParserCtxtGetErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { PyObject *py_retval; xmlParserCtxtPtr ctxt; xmlParserCtxtPyCtxtPtr pyCtxt; PyObject *pyobj_ctxt; - PyObject *pyobj_f; - PyObject *pyobj_arg; - if (!PyArg_ParseTuple(args, (char *)"OOO:xmlSetParserCtxtWarningHandler", &pyobj_ctxt, &pyobj_f, &pyobj_arg)) + if (!PyArg_ParseTuple(args, (char *)"O:xmlParserCtxtGetErrorHandler", + &pyobj_ctxt)) return(NULL); ctxt = (xmlParserCtxtPtr) PyparserCtxt_Get(pyobj_ctxt); - if (ctxt->_private == NULL) { - pyCtxt = xmlMalloc(sizeof(xmlParserCtxtPyCtxt)); - if (pyCtxt == NULL) { - py_retval = libxml_intWrap(-1); - return(py_retval); - } - memset(pyCtxt,0,sizeof(xmlParserCtxtPyCtxt)); - ctxt->_private = pyCtxt; + py_retval = PyTuple_New(2); + if (ctxt->_private != NULL) { + pyCtxt = (xmlParserCtxtPyCtxtPtr)ctxt->_private; + + PyTuple_SetItem(py_retval, 0, pyCtxt->f); + Py_XINCREF(pyCtxt->f); + PyTuple_SetItem(py_retval, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); } else { - pyCtxt = ctxt->_private; + /* no python error handler registered */ + PyTuple_SetItem(py_retval, 0, Py_None); + Py_XINCREF(Py_None); + PyTuple_SetItem(py_retval, 1, Py_None); + Py_XINCREF(Py_None); } - /* TODO: check f is a function ! */ - Py_XDECREF(pyCtxt->warningFunc); - Py_XINCREF(pyobj_f); - pyCtxt->warningFunc = pyobj_f; - Py_XDECREF(pyCtxt->warningFuncArg); - Py_XINCREF(pyobj_arg); - pyCtxt->warningFuncArg = pyobj_arg; - - ctxt->sax->warning = libxml_xmlParserCtxtWarningFuncHandler; - ctxt->vctxt.warning = libxml_xmlParserCtxtWarningFuncHandler; - - py_retval = libxml_intWrap(1); return(py_retval); } +PyObject * +libxml_xmlFreeParserCtxt(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { + xmlParserCtxtPtr ctxt; + PyObject *pyobj_ctxt; + xmlParserCtxtPyCtxtPtr pyCtxt; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlFreeParserCtxt", &pyobj_ctxt)) + return(NULL); + ctxt = (xmlParserCtxtPtr) PyparserCtxt_Get(pyobj_ctxt); + + if (ctxt != NULL) { + pyCtxt = (xmlParserCtxtPyCtxtPtr)((xmlParserCtxtPtr)ctxt)->_private; + if (pyCtxt) { + Py_XDECREF(pyCtxt->f); + Py_XDECREF(pyCtxt->arg); + xmlFree(pyCtxt); + } + xmlFreeParserCtxt(ctxt); + } + + Py_INCREF(Py_None); + return(Py_None); +} + /************************************************************************ * * * Per xmlTextReader error handler * @@ -1501,27 +1507,23 @@ typedef xmlTextReaderPyCtxt *xmlTextReaderPyCtxtPtr; static void libxml_xmlTextReaderErrorCallback(void *arg, const char *msg, - int line, - int col, - const char *URI, - int severity) + int severity, + xmlTextReaderLocatorPtr locator) { xmlTextReaderPyCtxt *pyCtxt = (xmlTextReaderPyCtxt *)arg; PyObject *list; PyObject *result; - list = PyTuple_New(6); + list = PyTuple_New(4); PyTuple_SetItem(list, 0, pyCtxt->arg); Py_XINCREF(pyCtxt->arg); PyTuple_SetItem(list, 1, libxml_charPtrConstWrap(msg)); - PyTuple_SetItem(list, 2, libxml_intWrap(line)); - PyTuple_SetItem(list, 3, libxml_intWrap(col)); - PyTuple_SetItem(list, 4, libxml_charPtrConstWrap(URI)); - PyTuple_SetItem(list, 5, libxml_intWrap(severity)); + PyTuple_SetItem(list, 2, libxml_intWrap(severity)); + PyTuple_SetItem(list, 3, libxml_xmlTextReaderLocatorPtrWrap(locator)); result = PyEval_CallObject(pyCtxt->f, list); if (result == NULL) { - /* TODO: manage for the exception to be go up... */ + /* TODO: manage for the exception to be propagated... */ PyErr_Print(); } Py_XDECREF(list); @@ -2558,8 +2560,8 @@ static PyMethodDef libxmlMethods[] = { {(char *) "inputBufferCreate", libxml_xmlCreateInputBuffer, METH_VARARGS, NULL}, {(char *) "setEntityLoader", libxml_xmlSetEntityLoader, METH_VARARGS, NULL}, {(char *)"xmlRegisterErrorHandler", libxml_xmlRegisterErrorHandler, METH_VARARGS, NULL }, - {(char *)"xmlSetParserCtxtErrorHandler", libxml_xmlSetParserCtxtErrorHandler, METH_VARARGS, NULL }, - {(char *)"xmlSetParserCtxtWarningHandler", libxml_xmlSetParserCtxtWarningHandler, METH_VARARGS, NULL }, + {(char *)"xmlParserCtxtSetErrorHandler", libxml_xmlParserCtxtSetErrorHandler, METH_VARARGS, NULL }, + {(char *)"xmlParserCtxtGetErrorHandler", libxml_xmlParserCtxtGetErrorHandler, METH_VARARGS, NULL }, {(char *)"xmlFreeParserCtxt", libxml_xmlFreeParserCtxt, METH_VARARGS, NULL }, {(char *)"xmlTextReaderSetErrorHandler", libxml_xmlTextReaderSetErrorHandler, METH_VARARGS, NULL }, {(char *)"xmlTextReaderGetErrorHandler", libxml_xmlTextReaderGetErrorHandler, METH_VARARGS, NULL }, diff --git a/python/libxml.py b/python/libxml.py index 8f9185ca..8ffa383f 100644 --- a/python/libxml.py +++ b/python/libxml.py @@ -462,12 +462,12 @@ PARSER_VALIDATE=3 PARSER_SUBST_ENTITIES=4 # -# For the xmlTextReader error severities +# For the error callback severities # -XMLREADER_SEVERITY_VALIDITY_WARNING=1 -XMLREADER_SEVERITY_VALIDITY_ERROR=2 -XMLREADER_SEVERITY_WARNING=3 -XMLREADER_SEVERITY_ERROR=4 +PARSER_SEVERITY_VALIDITY_WARNING=1 +PARSER_SEVERITY_VALIDITY_ERROR=2 +PARSER_SEVERITY_WARNING=3 +PARSER_SEVERITY_ERROR=4 # # register the libxml2 error handler @@ -499,11 +499,21 @@ class parserCtxtCore: libxml2mod.xmlFreeParserCtxt(self._o) self._o = None - def registerErrorHandler(self,f,arg): - libxml2mod.xmlSetParserCtxtErrorHandler(self._o,f,arg) + def setErrorHandler(self,f,arg): + """Register an error handler that will be called back as + f(arg,msg,severity,reserved). + + @reserved is currently always None.""" + libxml2mod.xmlParserCtxtSetErrorHandler(self._o,f,arg) - def registerWarningHandler(self,f,arg): - libxml2mod.xmlSetParserCtxtWarningHandler(self._o,f,arg) + def getErrorHandler(self): + """Return (f,arg) as previously registered with setErrorHandler + or (None,None).""" + return libxml2mod.xmlParserCtxtGetErrorHandler(self._o) + +def _xmlTextReaderErrorFunc((f,arg),msg,severity,locator): + """Intermediate callback to wrap the locator""" + return f(arg,msg,severity,xmlTextReaderLocator(locator)) class xmlTextReaderCore: @@ -517,15 +527,25 @@ class xmlTextReaderCore: libxml2mod.xmlFreeTextReader(self._o) self._o = None - def setErrorHandler(self,f,arg): + def SetErrorHandler(self,f,arg): """Register an error handler that will be called back as - f(arg,msg,line,col,URI,severity).""" - libxml2mod.xmlTextReaderSetErrorHandler(self._o,f,arg) + f(arg,msg,severity,locator).""" + if f is None: + libxml2mod.xmlTextReaderSetErrorHandler(\ + self._o,None,None) + else: + libxml2mod.xmlTextReaderSetErrorHandler(\ + self._o,_xmlTextReaderErrorFunc,(f,arg)) - def getErrorHandler(self): + def GetErrorHandler(self): """Return (f,arg) as previously registered with setErrorHandler or (None,None).""" - return libxml2mod.xmlTextReaderGetErrorHandler(self._o) + f,arg = libxml2mod.xmlTextReaderGetErrorHandler(self._o) + if f is None: + return None,None + else: + # assert f is _xmlTextReaderErrorFunc + return arg # WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING # diff --git a/python/libxml_wrap.h b/python/libxml_wrap.h index c8730caa..c59b706b 100644 --- a/python/libxml_wrap.h +++ b/python/libxml_wrap.h @@ -93,6 +93,14 @@ typedef struct { xmlTextReaderPtr obj; } PyxmlTextReader_Object; +#define PyxmlTextReaderLocator_Get(v) (((v) == Py_None) ? NULL : \ + (((PyxmlTextReaderLocator_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlTextReaderLocatorPtr obj; +} PyxmlTextReaderLocator_Object; + #define PyURI_Get(v) (((v) == Py_None) ? NULL : \ (((PyURI_Object *)(v))->obj)) @@ -147,19 +155,6 @@ PyObject * libxml_xmlOutputBufferPtrWrap(xmlOutputBufferPtr buffer); PyObject * libxml_xmlParserInputBufferPtrWrap(xmlParserInputBufferPtr buffer); PyObject * libxml_xmlRegexpPtrWrap(xmlRegexpPtr regexp); PyObject * libxml_xmlTextReaderPtrWrap(xmlTextReaderPtr reader); +PyObject * libxml_xmlTextReaderLocatorPtrWrap(xmlTextReaderLocatorPtr locator); xmlXPathObjectPtr libxml_xmlXPathObjectPtrConvert(PyObject * obj); - -/* - * Data structure that makes the link from the parser context - * to the python wrapper. - */ -typedef struct -{ - PyObject *errorFunc; - PyObject *errorFuncArg; - PyObject *warningFunc; - PyObject *warningFuncArg; -} xmlParserCtxtPyCtxt; -typedef xmlParserCtxtPyCtxt *xmlParserCtxtPyCtxtPtr; - diff --git a/python/tests/ctxterror.py b/python/tests/ctxterror.py index 44589cd5..265bb1e1 100755 --- a/python/tests/ctxterror.py +++ b/python/tests/ctxterror.py @@ -9,20 +9,22 @@ import libxml2 # Memory debug specific libxml2.debugMemory(1) -expect="""--> Opening and ending tag mismatch: x and y +expect="""--> (3) xmlns: URI foo is not absolute +--> (4) Opening and ending tag mismatch: x and y """ err="" -def callback(ctx, str): - global err +def callback(arg,msg,severity,reserved): + global err + err = err + "%s (%d) %s" % (arg,severity,msg) - err = err + "%s %s" % (ctx, str) - -s = """""" +s = """""" parserCtxt = libxml2.createPushParser(None,"",0,"test.xml") -parserCtxt.registerErrorHandler(callback, "-->") -parserCtxt.registerWarningHandler(callback, "-->") +parserCtxt.setErrorHandler(callback, "-->") +if parserCtxt.getErrorHandler() != (callback,"-->"): + print "getErrorHandler failed" + sys.exit(1) parserCtxt.parseChunk(s,len(s),1) doc = parserCtxt.doc() doc.freeDoc() @@ -37,8 +39,7 @@ if err != expect: i = 10000 while i > 0: parserCtxt = libxml2.createPushParser(None,"",0,"test.xml") - parserCtxt.registerErrorHandler(callback, "-->") - parserCtxt.registerWarningHandler(callback, "-->") + parserCtxt.setErrorHandler(callback, "-->") parserCtxt.parseChunk(s,len(s),1) doc = parserCtxt.doc() doc.freeDoc() diff --git a/python/tests/readererr.py b/python/tests/readererr.py index abbbba55..dbdeb479 100644 --- a/python/tests/readererr.py +++ b/python/tests/readererr.py @@ -13,14 +13,14 @@ expect="""--> (3) test1:1:xmlns: URI foo is not absolute --> (4) test1:1:Opening and ending tag mismatch: c and a """ err="" -def myErrorHandler(arg,msg,line,col,URI,severity): +def myErrorHandler(arg,msg,severity,locator): global err - err = err + "%s (%d) %s:%d:%s" % (arg,severity,URI,line,msg) + err = err + "%s (%d) %s:%d:%s" % (arg,severity,locator.BaseURI(),locator.LineNumber(),msg) f = StringIO.StringIO("""content of c""") input = libxml2.inputBuffer(f) reader = input.newTextReader("test1") -reader.setErrorHandler(myErrorHandler,"-->") +reader.SetErrorHandler(myErrorHandler,"-->") while reader.Read() == 1: pass @@ -30,9 +30,9 @@ if err != expect: print "expected %s" %(expect) sys.exit(1) -reader.setErrorHandler(None,None) -if reader.getErrorHandler() != (None,None): - print "getErrorHandler failed" +reader.SetErrorHandler(None,None) +if reader.GetErrorHandler() != (None,None): + print "GetErrorHandler failed" sys.exit(1) # diff --git a/python/types.c b/python/types.c index 65626202..a05c48a5 100644 --- a/python/types.c +++ b/python/types.c @@ -566,3 +566,22 @@ libxml_xmlTextReaderPtrWrap(xmlTextReaderPtr reader) (char *) "xmlTextReaderPtr", NULL); return (ret); } + +PyObject * +libxml_xmlTextReaderLocatorPtrWrap(xmlTextReaderLocatorPtr locator) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlTextReaderLocatorPtrWrap: locator = %p\n", locator); +#endif + if (locator == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCObject_FromVoidPtrAndDesc((void *) locator, + (char *) "xmlTextReaderLocatorPtr", NULL); + return (ret); +} + diff --git a/win32/libxml2.def.src b/win32/libxml2.def.src index 8d2a9570..5023927d 100644 --- a/win32/libxml2.def.src +++ b/win32/libxml2.def.src @@ -1219,6 +1219,9 @@ xmlCleanupPredefinedEntities /* Error handling extensions */ xmlTextReaderSetErrorHandler xmlTextReaderGetErrorHandler + xmlTextReaderLocatorLineNumber + /*xmlTextReaderLocatorLinePosition*/ + xmlTextReaderLocatorBaseURI diff --git a/xmlreader.c b/xmlreader.c index 9ae4b6e2..36323762 100644 --- a/xmlreader.c +++ b/xmlreader.c @@ -2281,6 +2281,74 @@ xmlTextReaderBuildMessage(const char *msg, va_list ap) { return str; } +/** + * xmlTextReaderLocatorLineNumber + * @locator: the xmlTextReaderLocatorPtr used + * + * Obtain the line number for the given locator. + * + * Returns the line number or -1 in case of error. + */ +int +xmlTextReaderLocatorLineNumber(xmlTextReaderLocatorPtr locator) { + /* we know that locator is a xmlParserCtxtPtr */ + xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)locator; + int ret = -1; + + if (ctx->node != NULL) { + ret = xmlGetLineNo(ctx->node); + } + else { + /* inspired from error.c */ + xmlParserInputPtr input; + input = ctx->input; + if ((input->filename == NULL) && (ctx->inputNr > 1)) + input = ctx->inputTab[ctx->inputNr - 2]; + if (input != NULL) { + ret = input->line; + } + else { + ret = -1; + } + } + + return ret; +} + +/** + * xmlTextReaderLocatorBaseURI + * @locator: the xmlTextReaderLocatorPtr used + * + * Obtain the base URI for the given locator. + * + * Returns the base URI or NULL in case of error. + */ +xmlChar * +xmlTextReaderLocatorBaseURI(xmlTextReaderLocatorPtr locator) { + /* we know that locator is a xmlParserCtxtPtr */ + xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)locator; + xmlChar *ret = NULL; + + if (ctx->node != NULL) { + ret = xmlNodeGetBase(NULL,ctx->node); + } + else { + /* inspired from error.c */ + xmlParserInputPtr input; + input = ctx->input; + if ((input->filename == NULL) && (ctx->inputNr > 1)) + input = ctx->inputTab[ctx->inputNr - 2]; + if (input != NULL) { + ret = xmlStrdup(input->filename); + } + else { + ret = NULL; + } + } + + return ret; +} + static void xmlTextReaderGenericError(void *ctxt, int severity, char *str) { xmlParserCtxtPtr ctx = (xmlParserCtxtPtr)ctxt; @@ -2289,10 +2357,8 @@ xmlTextReaderGenericError(void *ctxt, int severity, char *str) { if (str != NULL) { reader->errorFunc(reader->errorFuncArg, str, - ctx->input->line, - ctx->input->col, - ctx->input->filename, - severity); + severity, + (xmlTextReaderLocatorPtr)ctx); xmlFree(str); } } @@ -2303,7 +2369,7 @@ xmlTextReaderError(void *ctxt, const char *msg, ...) { va_start(ap,msg); xmlTextReaderGenericError(ctxt, - XMLREADER_SEVERITY_ERROR, + XML_PARSER_SEVERITY_ERROR, xmlTextReaderBuildMessage(msg,ap)); va_end(ap); @@ -2315,7 +2381,7 @@ xmlTextReaderWarning(void *ctxt, const char *msg, ...) { va_start(ap,msg); xmlTextReaderGenericError(ctxt, - XMLREADER_SEVERITY_WARNING, + XML_PARSER_SEVERITY_WARNING, xmlTextReaderBuildMessage(msg,ap)); va_end(ap); } @@ -2323,24 +2389,37 @@ xmlTextReaderWarning(void *ctxt, const char *msg, ...) { static void xmlTextReaderValidityError(void *ctxt, const char *msg, ...) { va_list ap; + int len = xmlStrlen((const xmlChar *) msg); - va_start(ap,msg); - xmlTextReaderGenericError(ctxt, - XMLREADER_SEVERITY_VALIDITY_ERROR, - xmlTextReaderBuildMessage(msg,ap)); - va_end(ap); - + if ((len > 1) && (msg[len - 2] != ':')) { + /* + * some callbacks only report locator information: + * skip them (mimicking behaviour in error.c) + */ + va_start(ap,msg); + xmlTextReaderGenericError(ctxt, + XML_PARSER_SEVERITY_VALIDITY_ERROR, + xmlTextReaderBuildMessage(msg,ap)); + va_end(ap); + } } static void xmlTextReaderValidityWarning(void *ctxt, const char *msg, ...) { va_list ap; + int len = xmlStrlen((const xmlChar *) msg); - va_start(ap,msg); - xmlTextReaderGenericError(ctxt, - XMLREADER_SEVERITY_VALIDITY_WARNING, - xmlTextReaderBuildMessage(msg,ap)); - va_end(ap); + if ((len != 0) && (msg[len - 1] != ':')) { + /* + * some callbacks only report locator information: + * skip them (mimicking behaviour in error.c) + */ + va_start(ap,msg); + xmlTextReaderGenericError(ctxt, + XML_PARSER_SEVERITY_VALIDITY_WARNING, + xmlTextReaderBuildMessage(msg,ap)); + va_end(ap); + } } /** @@ -2349,13 +2428,14 @@ xmlTextReaderValidityWarning(void *ctxt, const char *msg, ...) { * @f: the callback function to call on error and warnings * @arg: a user argument to pass to the callback function * + * Register a callback function that will be called on error and warnings. + * * If @f is NULL, the default error and warning handlers are restored. */ void xmlTextReaderSetErrorHandler(xmlTextReaderPtr reader, xmlTextReaderErrorFunc f, - void *arg) -{ + void *arg) { if (f != NULL) { reader->ctxt->sax->error = xmlTextReaderError; reader->ctxt->vctxt.error = xmlTextReaderValidityError; @@ -2375,11 +2455,18 @@ xmlTextReaderSetErrorHandler(xmlTextReaderPtr reader, } } +/** + * xmlTextReaderGetErrorHandler: + * @reader: the xmlTextReaderPtr used + * @f: the callback function or NULL is no callback has been registered + * @arg: a user argument + * + * Retrieve the error callback function and user argument. + */ void xmlTextReaderGetErrorHandler(xmlTextReaderPtr reader, xmlTextReaderErrorFunc *f, - void **arg) -{ + void **arg) { *f = reader->errorFunc; *arg = reader->errorFuncArg; }