change a small bit in the way valididy error messages get initialized
* parserInternals.c: change a small bit in the way valididy
error messages get initialized
* python/TODO python/libxml.c python/libxml2-python-api.xml
python/libxml2class.txt python/libxml_wrap.h python/types.c:
added some memory debugging to track leaks at the libxml2 level
* python/tests/*.py: changed all tests to check for leaks,
there is just one left in XPath extension registrations.
daniel
diff --git a/python/TODO b/python/TODO
index d1799fb..24f94d1 100644
--- a/python/TODO
+++ b/python/TODO
@@ -6,17 +6,11 @@
-------------
- SAX interfaces
-- memory debug interfaces
- enums -> libxml.py
- access to XPath variables
-- parserCtxt exposure:
- - entry points
- - wrappers
- - decent interface for setting/getting behaviour
- xmlBuffer exposure
- xpathContext, being able to set/get info and clean it up
- add regression tests
- - check memory
- build tree
- saving
- SAX flow
@@ -33,6 +27,7 @@
- tests/Makefile.am: export the Python class path
- xpath queries
- xpath extension
+ - check memory
- extensions based on a python.xml description of the new specific
interfaces
file libxml2-python-api.xml , first entry is xmlRegisterXPathFunction
@@ -43,5 +38,9 @@
- handling of node.content
- access to xmlParserCtxt and push mode
- needed for SAX too
+ - entry points
+ - wrappers
+ - decent interface for setting/getting behaviour
+- memory debug interfaces
Daniel Veillard
diff --git a/python/libxml.c b/python/libxml.c
index 57e370f..e37038a 100644
--- a/python/libxml.c
+++ b/python/libxml.c
@@ -15,12 +15,99 @@
#include <libxml/xpath.h>
#include <libxml/xmlerror.h>
#include <libxml/xpathInternals.h>
+#include <libxml/xmlmemory.h>
#include "libxml_wrap.h"
#include "libxml2-py.h"
/* #define DEBUG */
/* #define DEBUG_XPATH */
/* #define DEBUG_ERROR */
+/* #define DEBUG_MEMORY */
+
+/************************************************************************
+ * *
+ * Memory debug interface *
+ * *
+ ************************************************************************/
+
+extern void xmlMemFree(void *ptr);
+extern void *xmlMemMalloc(size_t size);
+extern void *xmlMemRealloc(void *ptr,size_t size);
+extern char *xmlMemoryStrdup(const char *str);
+
+static int libxmlMemoryDebugActivated = 0;
+static long libxmlMemoryAllocatedBase = 0;
+
+static int libxmlMemoryDebug = 0;
+static xmlFreeFunc freeFunc = NULL;
+static xmlMallocFunc mallocFunc = NULL;
+static xmlReallocFunc reallocFunc = NULL;
+static xmlStrdupFunc strdupFunc = NULL;
+
+PyObject *
+libxml_xmlDebugMemory(PyObject *self, PyObject *args) {
+ int activate;
+ PyObject *py_retval;
+ long ret;
+
+ if (!PyArg_ParseTuple(args, "i:xmlDebugMemory", &activate))
+ return(NULL);
+
+#ifdef DEBUG_MEMORY
+ printf("libxml_xmlDebugMemory(%d) called\n", activate);
+#endif
+
+ if (activate != 0) {
+ if (libxmlMemoryDebug == 0) {
+ /*
+ * First initialize the library and grab the old memory handlers
+ * and switch the library to memory debugging
+ */
+ xmlMemGet((xmlFreeFunc *) &freeFunc,
+ (xmlMallocFunc *)&mallocFunc,
+ (xmlReallocFunc *)&reallocFunc,
+ (xmlStrdupFunc *) &strdupFunc);
+ if ((freeFunc == xmlMemFree) && (mallocFunc == xmlMemMalloc) &&
+ (reallocFunc == xmlMemRealloc) &&
+ (strdupFunc == xmlMemoryStrdup)) {
+ libxmlMemoryAllocatedBase = xmlMemUsed();
+ } else {
+ ret = (long) xmlMemSetup(xmlMemFree, xmlMemMalloc,
+ xmlMemRealloc, xmlMemoryStrdup);
+ if (ret < 0)
+ goto error;
+ libxmlMemoryAllocatedBase = xmlMemUsed();
+ }
+ xmlInitParser();
+ ret = 0;
+ } else if (libxmlMemoryDebugActivated == 0) {
+ libxmlMemoryAllocatedBase = xmlMemUsed();
+ ret = 0;
+ } else {
+ ret = xmlMemUsed() - libxmlMemoryAllocatedBase;
+ }
+ libxmlMemoryDebug = 1;
+ libxmlMemoryDebugActivated = 1;
+ } else {
+ if (libxmlMemoryDebugActivated == 1)
+ ret = xmlMemUsed() - libxmlMemoryAllocatedBase;
+ else
+ ret = 0;
+ libxmlMemoryDebugActivated = 0;
+ }
+error:
+ py_retval = libxml_longWrap(ret);
+ return(py_retval);
+}
+
+PyObject *
+libxml_xmlDumpMemory(PyObject *self, PyObject *args) {
+
+ if (libxmlMemoryDebug != 0)
+ xmlMemoryDump();
+ Py_INCREF(Py_None);
+ return(Py_None);
+}
/************************************************************************
* *
@@ -68,6 +155,36 @@
return(pyret);
}
+PyObject *
+libxml_htmlCreatePushParser(PyObject *self, PyObject *args) {
+ xmlChar *chunk;
+ int size;
+ xmlChar *URI;
+ PyObject *pyobj_SAX;
+ xmlSAXHandlerPtr SAX = NULL;
+ pySAXhandlerPtr SAXdata = NULL;
+ xmlParserCtxtPtr ret;
+ PyObject *pyret;
+
+ if (!PyArg_ParseTuple(args, "Oziz:htmlCreatePushParser", &pyobj_SAX,
+ &chunk, &size, &URI))
+ return(NULL);
+
+#ifdef DEBUG_ERROR
+ printf("libxml_htmlCreatePushParser(%p, %s, %d, %s) called\n",
+ pyobj_SAX, chunk, size, URI);
+#endif
+ if (pyobj_SAX != Py_None) {
+ printf("htmlCreatePushParser: event interface not supported yet !\n");
+ Py_INCREF(Py_None);
+ return(Py_None);
+ }
+ ret = htmlCreatePushParserCtxt(SAX, SAXdata, chunk, size, URI,
+ XML_CHAR_ENCODING_NONE);
+ pyret = libxml_xmlParserCtxtPtrWrap(ret);
+ return(pyret);
+}
+
/************************************************************************
* *
* Error message callback *
diff --git a/python/libxml2-python-api.xml b/python/libxml2-python-api.xml
index 2c5eb04..a830c75 100644
--- a/python/libxml2-python-api.xml
+++ b/python/libxml2-python-api.xml
@@ -21,7 +21,15 @@
<arg name='ctx' type='pythonObject' info='a context for the callback'/>
</function>
<function name='xmlCreatePushParser' file='python'>
- <info>Create a progressive parser context to build either an event flow if the SAX object is not None, or a DOM tree otherwise.</info>
+ <info>Create a progressive XML parser context to build either an event flow if the SAX object is not None, or a DOM tree otherwise.</info>
+ <return type='xmlParserCtxtPtr' info="the parser context or None in case of error"/>
+ <arg name='SAX' type='pythonObject' info='the SAX callback object or None'/>
+ <arg name='chunk' type='xmlChar *' info='the initial data'/>
+ <arg name='size' type='int' info='the size of the initial data'/>
+ <arg name='URI' type='xmlChar *' info='The URI used for base computations'/>
+ </function>
+ <function name='htmlCreatePushParser' file='python'>
+ <info>Create a progressive HTML parser context to build either an event flow if the SAX object is not None, or a DOM tree otherwise.</info>
<return type='xmlParserCtxtPtr' info="the parser context or None in case of error"/>
<arg name='SAX' type='pythonObject' info='the SAX callback object or None'/>
<arg name='chunk' type='xmlChar *' info='the initial data'/>
@@ -73,5 +81,14 @@
<arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/>
<arg name='linenumbers' type='int' info='1 to save line numbers'/>
</function>
+ <function name='xmlDebugMemory' file='python'>
+ <info>Switch on the generation of line number for elements nodes. Also returns the number of bytes allocated and not freed by libxml2 since memory debugging was switched on.</info>
+ <return type='int' info="returns the number of bytes allocated and not freed"/>
+ <arg name='activate' type='int' info='1 switch on memory debugging 0 switch it off'/>
+ </function>
+ <function name='xmlDumpMemory' file='python'>
+ <info>dump the memory allocated in the file .memdump</info>
+ <return type='void'/>
+ </function>
</symbols>
</api>
diff --git a/python/libxml2class.txt b/python/libxml2class.txt
index 374fe48..cb12a2f 100644
--- a/python/libxml2class.txt
+++ b/python/libxml2class.txt
@@ -115,6 +115,9 @@
# functions from module python
createPushParser()
+debugMemory()
+dumpMemory()
+htmlCreatePushParser()
registerErrorHandler()
# functions from module tree
diff --git a/python/libxml_wrap.h b/python/libxml_wrap.h
index d6d89aa..68daf01 100644
--- a/python/libxml_wrap.h
+++ b/python/libxml_wrap.h
@@ -35,6 +35,7 @@
} PyparserCtxt_Object;
PyObject * libxml_intWrap(int val);
+PyObject * libxml_longWrap(long val);
PyObject * libxml_xmlCharPtrWrap(xmlChar *str);
PyObject * libxml_constxmlCharPtrWrap(const xmlChar *str);
PyObject * libxml_charPtrWrap(char *str);
diff --git a/python/tests/error.py b/python/tests/error.py
index 21cf558..9337945 100755
--- a/python/tests/error.py
+++ b/python/tests/error.py
@@ -6,6 +6,9 @@
import sys
import libxml2
+# Memory debug specific
+libxml2.debugMemory(1)
+
expect='--> warning: --> failed to load external entity "missing.xml"\n'
err=""
def callback(ctx, str):
@@ -27,4 +30,10 @@
err = ""
i = i - 1
-print "OK"
+# Memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/python/tests/push.py b/python/tests/push.py
index 738d3e9..5b60a16 100755
--- a/python/tests/push.py
+++ b/python/tests/push.py
@@ -2,6 +2,9 @@
import sys
import libxml2
+# Memory debug specific
+libxml2.debugMemory(1)
+
ctxt = libxml2.createPushParser(None, "<foo", 4, "test.xml")
ctxt.parseChunk("/>", 2, 1)
doc = ctxt.doc()
@@ -22,4 +25,11 @@
doc.freeDoc()
i = i -1
ctxt=None
-print "OK"
+
+# Memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/python/tests/tst.py b/python/tests/tst.py
index dc6e4d4..9540cda 100755
--- a/python/tests/tst.py
+++ b/python/tests/tst.py
@@ -2,6 +2,9 @@
import sys
import libxml2
+# Memory debug specific
+libxml2.debugMemory(1)
+
doc = libxml2.parseFile("tst.xml")
if doc.name != "tst.xml":
print "doc.name failed"
@@ -15,4 +18,11 @@
print "child.name failed"
sys.exit(1)
doc.freeDoc()
-print "OK"
+
+# Memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/python/tests/tstxpath.py b/python/tests/tstxpath.py
index 97d392c..9fc3d4c 100755
--- a/python/tests/tstxpath.py
+++ b/python/tests/tstxpath.py
@@ -2,6 +2,10 @@
import sys
import libxml2
+#memory debug specific
+libxml2.debugMemory(1)
+
+
def foo(x):
return x + 1
@@ -35,4 +39,12 @@
sys.exit(1)
i = i - 1
doc.freeDoc()
-print "OK"
+del ctxt
+
+#memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/python/tests/validate.py b/python/tests/validate.py
index 5762f60..e9841a9 100755
--- a/python/tests/validate.py
+++ b/python/tests/validate.py
@@ -2,6 +2,9 @@
import sys
import libxml2
+# Memory debug specific
+libxml2.debugMemory(1)
+
ctxt = libxml2.createFileParserCtxt("valid.xml")
ctxt.validate(1)
ctxt.parseDocument()
@@ -68,5 +71,12 @@
print "validity check failed"
sys.exit(1)
i = i - 1
+del ctxt
-print "OK"
+# Memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/python/tests/xpath.py b/python/tests/xpath.py
index 49a697f..0d35a99 100755
--- a/python/tests/xpath.py
+++ b/python/tests/xpath.py
@@ -6,6 +6,9 @@
import sys
import libxml2
+# Memory debug specific
+libxml2.debugMemory(1)
+
doc = libxml2.parseFile("tst.xml")
if doc.name != "tst.xml":
print "doc.name error"
@@ -27,4 +30,12 @@
res = ctxt.xpathEval("//*")
doc.freeDoc()
i = i -1
-print "OK"
+del ctxt
+
+# Memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/python/tests/xpathext.py b/python/tests/xpathext.py
index 97d392c..14cb69f 100755
--- a/python/tests/xpathext.py
+++ b/python/tests/xpathext.py
@@ -2,6 +2,9 @@
import sys
import libxml2
+# Memory debug specific
+libxml2.debugMemory(1)
+
def foo(x):
return x + 1
@@ -35,4 +38,12 @@
sys.exit(1)
i = i - 1
doc.freeDoc()
-print "OK"
+del ctxt
+
+# Memory debug specific
+libxml2.cleanupParser()
+if libxml2.debugMemory(1) == 0:
+ print "OK"
+else:
+ print "Memory leak %d bytes" % (libxml2.debugMemory(1))
+ libxml2.dumpMemory()
diff --git a/python/types.c b/python/types.c
index 07a178e..56f472f 100644
--- a/python/types.c
+++ b/python/types.c
@@ -20,6 +20,17 @@
}
PyObject *
+libxml_longWrap(long val) {
+ PyObject *ret;
+
+#ifdef DEBUG
+ printf("libxml_longWrap: val = %ld\n", val);
+#endif
+ ret = PyInt_FromLong(val);
+ return(ret);
+}
+
+PyObject *
libxml_doubleWrap(double val) {
PyObject *ret;