diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-11-17 21:10:17 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-11-17 21:10:17 (GMT) |
commit | 8096d34300076a1aa9cb517de49fb920a051939f (patch) | |
tree | 5f2b1b7c41f89fedc31af973d6a747ca674cfd24 /libxml2/python | |
parent | fc7f7edd0b8011cb71573b15462ef83068d9e54b (diff) | |
parent | 574585fa78070b0cc6b5ad22543e21a3502a122b (diff) | |
download | blt-8096d34300076a1aa9cb517de49fb920a051939f.zip blt-8096d34300076a1aa9cb517de49fb920a051939f.tar.gz blt-8096d34300076a1aa9cb517de49fb920a051939f.tar.bz2 |
Merge commit '574585fa78070b0cc6b5ad22543e21a3502a122b' as 'libxml2'
Diffstat (limited to 'libxml2/python')
64 files changed, 12471 insertions, 0 deletions
diff --git a/libxml2/python/Makefile.am b/libxml2/python/Makefile.am new file mode 100644 index 0000000..34aed96 --- /dev/null +++ b/libxml2/python/Makefile.am @@ -0,0 +1,54 @@ +# Makefile for libxml2 python library +AUTOMAKE_OPTIONS = 1.4 foreign + +SUBDIRS = . tests + +docsdir = $(datadir)/doc/libxml2-python-$(LIBXML_VERSION) +dist_docs_DATA = TODO + +EXTRA_DIST = \ + setup.py \ + generator.py \ + libxml.py \ + libxml2-export.c \ + libxml2-python-api.xml \ + libxml2class.py \ + libxml2class.txt + +if WITH_PYTHON +AM_CPPFLAGS = \ + -I$(top_builddir)/include \ + -I$(top_srcdir)/include \ + -I$(PYTHON_INCLUDES) + +python_LTLIBRARIES = libxml2mod.la + +libxml2mod_la_SOURCES = libxml.c libxml_wrap.h libxml2-py.h libxml2-py.c types.c +libxml2mod_la_LDFLAGS = $(CYGWIN_EXTRA_LDFLAGS) $(WIN32_EXTRA_LDFLAGS) -module -avoid-version \ + $(top_builddir)/libxml2.la $(CYGWIN_EXTRA_PYTHON_LIBADD) $(WIN32_EXTRA_PYTHON_LIBADD) $(PYTHON_LIBS) + +BUILT_SOURCES = libxml2-export.c libxml2-py.h libxml2-py.c + +# libxml.c #includes libxml2-export.c +libxml.$(OBJEXT): libxml2-export.c + +libxml2.py: $(srcdir)/libxml.py libxml2class.py + cat $(srcdir)/libxml.py `test -f libxml2class.py || echo $(srcdir)/`libxml2class.py > $@ + +dist_python_DATA = \ + drv_libxml2.py \ + libxml2.py + +CLEANFILES = *.pyc + +MAINTAINERCLEANFILES = libxml2.py libxml2class.* + +API_DESC = $(top_srcdir)/doc/libxml2-api.xml $(srcdir)/libxml2-python-api.xml +GENERATED = libxml2class.py libxml2class.txt $(BUILT_SOURCES) + +$(GENERATED): $(srcdir)/generator.py $(API_DESC) + $(PYTHON) $(srcdir)/generator.py $(srcdir) +endif + +tests test: all + cd tests && $(MAKE) tests diff --git a/libxml2/python/README b/libxml2/python/README new file mode 100644 index 0000000..b46eee4 --- /dev/null +++ b/libxml2/python/README @@ -0,0 +1,34 @@ + Module libxml2-python + ===================== + +This is the libxml2 python module, providing access to the +libxml2 and libxslt (if available) libraries. For general +informationss on those XML and XSLT libraries check their +web pages at : + http://xmlsoft.org/ + and + http://xmlsoft.org/XSLT/ + +The latest version of the sources for this module and the +associated libraries can be found at: + ftp://xmlsoft.org/ + +Binaries packages of the libxml2 and libxslt libraries can +be found either on the FTP site for Linux, from external +sources linked from the web pages, or as part of your set of +packages provided with your operating system. + +NOTE: +this module distribution is not the primary distribution +of the libxml2 and libxslt Python binding code, but as +the Python way of packaging those for non-Linux systems. +The main sources are the libxml2 and libxslt tar.gz found on +the site. One side effect is that the official RPM packages for +those modules are not generated from the libxml2-python +distributions but as part of the normal RPM packaging of +those two libraries. +The RPM packages can be found at: + http://rpmfind.net/linux/rpm2html/search.php?query=libxml2-python + http://rpmfind.net/linux/rpm2html/search.php?query=libxslt-python + +Daniel Veillard diff --git a/libxml2/python/TODO b/libxml2/python/TODO new file mode 100644 index 0000000..f446f6e --- /dev/null +++ b/libxml2/python/TODO @@ -0,0 +1,57 @@ + TODO for the libxml2 Python wrappers + + $Id$ + +Things to do: +------------- + +- SAX interfaces + - push is done but no generic interface + - elementDecl need some work + - need more testing and check full callbacks for xmllib/sgmlop replacement +- enums -> libxml.py +- access to XPath variables +- xmlBuffer exposure +- xpathContext, being able to set/get info and clean it up +- more work needed on context handling for function lookup + and use of an hash table. +- add regression tests + - SAX flow +- DTD element and attributes content accesses + - attribute handled in SAX + - element needed in both + + +Done: +----- +- class hierarchy: + + make specific node type inherit from xmlNode + done, had to sort the classes in the output + + get the generator to output a classes.txt description + done libxml2class.txt +- add regression tests + - tests/Makefile.am: export the Python class path + - xpath queries + - xpath extension + - check memory + - build tree + - saving +- extensions based on a python.xml description of the new specific + interfaces + file libxml2-python-api.xml , first entry is xmlRegisterXPathFunction +- spec file: automatically generate for pythonX.Y if found + Done, a bit ugly by running new makes in %install for each level + found. +- error redirections and preformat +- 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 +- SAX interfaces + - basic stuff with push is available + - basic xmllib replacement + +Daniel Veillard diff --git a/libxml2/python/drv_libxml2.py b/libxml2/python/drv_libxml2.py new file mode 100644 index 0000000..71b1c67 --- /dev/null +++ b/libxml2/python/drv_libxml2.py @@ -0,0 +1,379 @@ +# -*- coding: iso-8859-1 -*- +""" A SAX2 driver for libxml2, on top of it's XmlReader API + +USAGE + # put this file (drv_libxml2.py) in PYTHONPATH + import xml.sax + reader = xml.sax.make_parser(["drv_libxml2"]) + # ...and the rest is standard python sax. + +CAVEATS + - Lexical handlers are supported, except for start/endEntity + (waiting for XmlReader.ResolveEntity) and start/endDTD + - 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 + - some ErrorHandler events (warning) + - some ContentHandler events (setDocumentLocator, skippedEntity) + - EntityResolver (using libxml2.?) + - DTDHandler (if/when libxml2 exposes such node types) + - DeclHandler (if/when libxml2 exposes such node types) + - property_xml_string? + - feature_string_interning? + - Incremental parser + - additional performance tuning: + - one might cache callbacks to avoid some name lookups + - one might implement a smarter way to pass attributes to startElement + (some kind of lazy evaluation?) + - there might be room for improvement in start/endPrefixMapping + - other? + +""" + +__author__ = "Stéphane Bidoul <sbi@skynet.be>" +__version__ = "0.3" + +import sys +import codecs + +if sys.version_info[0] < 3: + __author__ = codecs.unicode_escape_decode(__author__)[0] + + StringTypes = (str, unicode) + # libxml2 returns strings as UTF8 + _decoder = codecs.lookup("utf8")[1] + def _d(s): + if s is None: + return s + else: + return _decoder(s)[0] +else: + StringTypes = str + # s is Unicode `str` already + def _d(s): + return s + +from xml.sax._exceptions import * +from xml.sax import xmlreader, saxutils +from xml.sax.handler import \ + feature_namespaces, \ + feature_namespace_prefixes, \ + feature_string_interning, \ + feature_validation, \ + feature_external_ges, \ + feature_external_pes, \ + property_lexical_handler, \ + property_declaration_handler, \ + property_dom_node, \ + property_xml_string + +try: + import libxml2 +except ImportError: + raise SAXReaderNotAvailable("libxml2 not available: " \ + "import error was: %s" % sys.exc_info()[1]) + +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): + + def __init__(self): + xmlreader.XMLReader.__init__(self) + # features + self.__ns = 0 + self.__nspfx = 0 + self.__validate = 0 + self.__extparams = 1 + # parsing flag + self.__parsing = 0 + # additional handlers + self.__lex_handler = None + self.__decl_handler = None + # error messages accumulator + self.__errors = None + + def _errorHandler(self,arg,msg,severity,locator): + if self.__errors is None: + self.__errors = [] + self.__errors.append((severity, + SAXParseException(msg,None, + Locator(locator)))) + + 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 + + def parse(self, source): + self.__parsing = 1 + try: + # prepare source and create reader + if isinstance(source, StringTypes): + reader = libxml2.newTextReaderFilename(source) + else: + source = saxutils.prepare_input_source(source) + input = libxml2.inputBuffer(source.getByteStream()) + reader = input.newTextReader(source.getSystemId()) + reader.SetErrorHandler(self._errorHandler,None) + # configure reader + if self.__extparams: + reader.SetParserProp(libxml2.PARSER_LOADDTD,1) + reader.SetParserProp(libxml2.PARSER_DEFAULTATTRS,1) + reader.SetParserProp(libxml2.PARSER_SUBST_ENTITIES,1) + reader.SetParserProp(libxml2.PARSER_VALIDATE,self.__validate) + else: + reader.SetParserProp(libxml2.PARSER_LOADDTD, 0) + # we reuse attribute maps (for a slight performance gain) + if self.__ns: + attributesNSImpl = xmlreader.AttributesNSImpl({},{}) + else: + attributesImpl = xmlreader.AttributesImpl({}) + # prefixes to pop (for endPrefixMapping) + prefixes = [] + # start loop + self._cont_handler.startDocument() + while 1: + r = reader.Read() + # check for errors + if r == 1: + if not self.__errors is None: + self._reportErrors(0) + elif r == 0: + if not self.__errors is None: + self._reportErrors(0) + break # end of parse + else: + if not self.__errors is None: + self._reportErrors(1) + else: + self._err_handler.fatalError(\ + SAXException("Read failed (no details available)")) + break # fatal parse error + # get node type + nodeType = reader.NodeType() + # Element + if nodeType == 1: + if self.__ns: + eltName = (_d(reader.NamespaceUri()),\ + _d(reader.LocalName())) + eltQName = _d(reader.Name()) + attributesNSImpl._attrs = attrs = {} + attributesNSImpl._qnames = qnames = {} + newPrefixes = [] + while reader.MoveToNextAttribute(): + qname = _d(reader.Name()) + value = _d(reader.Value()) + if qname.startswith("xmlns"): + if len(qname) > 5: + newPrefix = qname[6:] + else: + newPrefix = None + newPrefixes.append(newPrefix) + self._cont_handler.startPrefixMapping(\ + newPrefix,value) + if not self.__nspfx: + continue # don't report xmlns attribute + attName = (_d(reader.NamespaceUri()), + _d(reader.LocalName())) + qnames[attName] = qname + attrs[attName] = value + reader.MoveToElement() + self._cont_handler.startElementNS( \ + eltName,eltQName,attributesNSImpl) + if reader.IsEmptyElement(): + self._cont_handler.endElementNS(eltName,eltQName) + for newPrefix in newPrefixes: + self._cont_handler.endPrefixMapping(newPrefix) + else: + prefixes.append(newPrefixes) + else: + eltName = _d(reader.Name()) + attributesImpl._attrs = attrs = {} + while reader.MoveToNextAttribute(): + attName = _d(reader.Name()) + attrs[attName] = _d(reader.Value()) + reader.MoveToElement() + self._cont_handler.startElement( \ + eltName,attributesImpl) + if reader.IsEmptyElement(): + self._cont_handler.endElement(eltName) + # EndElement + elif nodeType == 15: + if self.__ns: + self._cont_handler.endElementNS( \ + (_d(reader.NamespaceUri()),_d(reader.LocalName())), + _d(reader.Name())) + for prefix in prefixes.pop(): + self._cont_handler.endPrefixMapping(prefix) + else: + self._cont_handler.endElement(_d(reader.Name())) + # Text + elif nodeType == 3: + self._cont_handler.characters(_d(reader.Value())) + # Whitespace + elif nodeType == 13: + self._cont_handler.ignorableWhitespace(_d(reader.Value())) + # SignificantWhitespace + elif nodeType == 14: + self._cont_handler.characters(_d(reader.Value())) + # CDATA + elif nodeType == 4: + if not self.__lex_handler is None: + self.__lex_handler.startCDATA() + self._cont_handler.characters(_d(reader.Value())) + if not self.__lex_handler is None: + self.__lex_handler.endCDATA() + # EntityReference + elif nodeType == 5: + if not self.__lex_handler is None: + self.startEntity(_d(reader.Name())) + reader.ResolveEntity() + # EndEntity + elif nodeType == 16: + if not self.__lex_handler is None: + self.endEntity(_d(reader.Name())) + # ProcessingInstruction + elif nodeType == 7: + self._cont_handler.processingInstruction( \ + _d(reader.Name()),_d(reader.Value())) + # Comment + elif nodeType == 8: + if not self.__lex_handler is None: + self.__lex_handler.comment(_d(reader.Value())) + # DocumentType + elif nodeType == 10: + #if not self.__lex_handler is None: + # self.__lex_handler.startDTD() + pass # TODO (how to detect endDTD? on first non-dtd event?) + # XmlDeclaration + elif nodeType == 17: + pass # TODO + # Entity + elif nodeType == 6: + pass # TODO (entity decl) + # Notation (decl) + elif nodeType == 12: + pass # TODO + # Attribute (never in this loop) + #elif nodeType == 2: + # pass + # Document (not exposed) + #elif nodeType == 9: + # pass + # DocumentFragment (never returned by XmlReader) + #elif nodeType == 11: + # pass + # None + #elif nodeType == 0: + # pass + # - + else: + raise SAXException("Unexpected node type %d" % nodeType) + if r == 0: + self._cont_handler.endDocument() + reader.Close() + finally: + self.__parsing = 0 + + def setDTDHandler(self, handler): + # TODO (when supported, the inherited method works just fine) + raise SAXNotSupportedException("DTDHandler not supported") + + def setEntityResolver(self, resolver): + # TODO (when supported, the inherited method works just fine) + raise SAXNotSupportedException("EntityResolver not supported") + + def getFeature(self, name): + if name == feature_namespaces: + return self.__ns + elif name == feature_namespace_prefixes: + return self.__nspfx + elif name == feature_validation: + return self.__validate + elif name == feature_external_ges: + return 1 # TODO (does that relate to PARSER_LOADDTD)? + elif name == feature_external_pes: + return self.__extparams + else: + raise SAXNotRecognizedException("Feature '%s' not recognized" % \ + name) + + def setFeature(self, name, state): + if self.__parsing: + raise SAXNotSupportedException("Cannot set feature %s " \ + "while parsing" % name) + if name == feature_namespaces: + self.__ns = state + elif name == feature_namespace_prefixes: + self.__nspfx = state + elif name == feature_validation: + self.__validate = state + elif name == feature_external_ges: + if state == 0: + # TODO (does that relate to PARSER_LOADDTD)? + raise SAXNotSupportedException("Feature '%s' not supported" % \ + name) + elif name == feature_external_pes: + self.__extparams = state + else: + raise SAXNotRecognizedException("Feature '%s' not recognized" % \ + name) + + def getProperty(self, name): + if name == property_lexical_handler: + return self.__lex_handler + elif name == property_declaration_handler: + return self.__decl_handler + else: + raise SAXNotRecognizedException("Property '%s' not recognized" % \ + name) + + def setProperty(self, name, value): + if name == property_lexical_handler: + self.__lex_handler = value + elif name == property_declaration_handler: + # TODO: remove if/when libxml2 supports dtd events + raise SAXNotSupportedException("Property '%s' not supported" % \ + name) + self.__decl_handler = value + else: + raise SAXNotRecognizedException("Property '%s' not recognized" % \ + name) + +def create_parser(): + return LibXml2Reader() + diff --git a/libxml2/python/generator.py b/libxml2/python/generator.py new file mode 100755 index 0000000..c0cb3ad --- /dev/null +++ b/libxml2/python/generator.py @@ -0,0 +1,1228 @@ +#!/usr/bin/python -u +# +# generate python wrappers from the XML API description +# + +functions = {} +enums = {} # { enumType: { enumConstant: enumValue } } + +import os +import sys +import string + +if __name__ == "__main__": + # launched as a script + srcPref = os.path.dirname(sys.argv[0]) +else: + # imported + srcPref = os.path.dirname(__file__) + +####################################################################### +# +# That part if purely the API acquisition phase from the +# XML API description +# +####################################################################### +import os +import xml.sax + +debug = 0 + +def getparser(): + # Attach parser to an unmarshalling object. return both objects. + target = docParser() + parser = xml.sax.make_parser() + parser.setContentHandler(target) + return parser, target + +class docParser(xml.sax.handler.ContentHandler): + def __init__(self): + self._methodname = None + self._data = [] + self.in_function = 0 + + self.startElement = self.start + self.endElement = self.end + self.characters = self.data + + def close(self): + if debug: + print("close") + + def getmethodname(self): + return self._methodname + + def data(self, text): + if debug: + print("data %s" % text) + self._data.append(text) + + def start(self, tag, attrs): + if debug: + print("start %s, %s" % (tag, attrs)) + if tag == 'function': + self._data = [] + self.in_function = 1 + self.function = None + self.function_cond = None + self.function_args = [] + self.function_descr = None + self.function_return = None + self.function_file = None + if 'name' in attrs.keys(): + self.function = attrs['name'] + if 'file' in attrs.keys(): + self.function_file = attrs['file'] + elif tag == 'cond': + self._data = [] + elif tag == 'info': + self._data = [] + elif tag == 'arg': + if self.in_function == 1: + self.function_arg_name = None + self.function_arg_type = None + self.function_arg_info = None + if 'name' in attrs.keys(): + self.function_arg_name = attrs['name'] + if 'type' in attrs.keys(): + self.function_arg_type = attrs['type'] + if 'info' in attrs.keys(): + self.function_arg_info = attrs['info'] + elif tag == 'return': + if self.in_function == 1: + self.function_return_type = None + self.function_return_info = None + self.function_return_field = None + if 'type' in attrs.keys(): + self.function_return_type = attrs['type'] + if 'info' in attrs.keys(): + self.function_return_info = attrs['info'] + if 'field' in attrs.keys(): + self.function_return_field = attrs['field'] + elif tag == 'enum': + enum(attrs['type'],attrs['name'],attrs['value']) + + def end(self, tag): + if debug: + print("end %s" % tag) + if tag == 'function': + if self.function != None: + function(self.function, self.function_descr, + self.function_return, self.function_args, + self.function_file, self.function_cond) + self.in_function = 0 + elif tag == 'arg': + if self.in_function == 1: + self.function_args.append([self.function_arg_name, + self.function_arg_type, + self.function_arg_info]) + elif tag == 'return': + if self.in_function == 1: + self.function_return = [self.function_return_type, + self.function_return_info, + self.function_return_field] + elif tag == 'info': + str = '' + for c in self._data: + str = str + c + if self.in_function == 1: + self.function_descr = str + elif tag == 'cond': + str = '' + for c in self._data: + str = str + c + if self.in_function == 1: + self.function_cond = str + + +def function(name, desc, ret, args, file, cond): + functions[name] = (desc, ret, args, file, cond) + +def enum(type, name, value): + if type not in enums: + enums[type] = {} + enums[type][name] = value + +####################################################################### +# +# Some filtering rukes to drop functions/types which should not +# be exposed as-is on the Python interface +# +####################################################################### + +skipped_modules = { + 'xmlmemory': None, + 'DOCBparser': None, + 'SAX': None, + 'hash': None, + 'list': None, + 'threads': None, +# 'xpointer': None, +} +skipped_types = { + 'int *': "usually a return type", + 'xmlSAXHandlerPtr': "not the proper interface for SAX", + 'htmlSAXHandlerPtr': "not the proper interface for SAX", + 'xmlRMutexPtr': "thread specific, skipped", + 'xmlMutexPtr': "thread specific, skipped", + 'xmlGlobalStatePtr': "thread specific, skipped", + 'xmlListPtr': "internal representation not suitable for python", + 'xmlBufferPtr': "internal representation not suitable for python", + 'FILE *': None, +} + +####################################################################### +# +# Table of remapping to/from the python type or class to the C +# counterpart. +# +####################################################################### + +py_types = { + 'void': (None, None, None, None), + 'int': ('i', None, "int", "int"), + 'long': ('l', None, "long", "long"), + 'double': ('d', None, "double", "double"), + 'unsigned int': ('i', None, "int", "int"), + 'xmlChar': ('c', None, "int", "int"), + 'unsigned char *': ('z', None, "charPtr", "char *"), + 'char *': ('z', None, "charPtr", "char *"), + 'const char *': ('z', None, "charPtrConst", "const char *"), + 'xmlChar *': ('z', None, "xmlCharPtr", "xmlChar *"), + 'const xmlChar *': ('z', None, "xmlCharPtrConst", "const xmlChar *"), + 'xmlNodePtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlNodePtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlNode *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlNode *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlDtdPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlDtdPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlDtd *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlDtd *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlAttrPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlAttrPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlAttr *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlAttr *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlEntityPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlEntityPtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlEntity *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const xmlEntity *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlElementPtr': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'const xmlElementPtr': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'xmlElement *': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'const xmlElement *': ('O', "xmlElement", "xmlElementPtr", "xmlElementPtr"), + 'xmlAttributePtr': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'const xmlAttributePtr': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'xmlAttribute *': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'const xmlAttribute *': ('O', "xmlAttribute", "xmlAttributePtr", "xmlAttributePtr"), + 'xmlNsPtr': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), + 'const xmlNsPtr': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), + 'xmlNs *': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), + 'const xmlNs *': ('O', "xmlNode", "xmlNsPtr", "xmlNsPtr"), + 'xmlDocPtr': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'const xmlDocPtr': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'xmlDoc *': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'const xmlDoc *': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'htmlDocPtr': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'const htmlDocPtr': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'htmlDoc *': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'const htmlDoc *': ('O', "xmlNode", "xmlDocPtr", "xmlDocPtr"), + 'htmlNodePtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const htmlNodePtr': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'htmlNode *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'const htmlNode *': ('O', "xmlNode", "xmlNodePtr", "xmlNodePtr"), + 'xmlXPathContextPtr': ('O', "xmlXPathContext", "xmlXPathContextPtr", "xmlXPathContextPtr"), + 'xmlXPathContext *': ('O', "xpathContext", "xmlXPathContextPtr", "xmlXPathContextPtr"), + 'xmlXPathParserContextPtr': ('O', "xmlXPathParserContext", "xmlXPathParserContextPtr", "xmlXPathParserContextPtr"), + 'xmlParserCtxtPtr': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"), + 'xmlParserCtxt *': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"), + 'htmlParserCtxtPtr': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"), + 'htmlParserCtxt *': ('O', "parserCtxt", "xmlParserCtxtPtr", "xmlParserCtxtPtr"), + 'xmlValidCtxtPtr': ('O', "ValidCtxt", "xmlValidCtxtPtr", "xmlValidCtxtPtr"), + '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"), + 'xmlTextReaderLocatorPtr': ('O', "xmlTextReaderLocator", "xmlTextReaderLocatorPtr", "xmlTextReaderLocatorPtr"), + 'xmlTextReaderPtr': ('O', "xmlTextReader", "xmlTextReaderPtr", "xmlTextReaderPtr"), + 'xmlRelaxNGPtr': ('O', "relaxNgSchema", "xmlRelaxNGPtr", "xmlRelaxNGPtr"), + 'xmlRelaxNGParserCtxtPtr': ('O', "relaxNgParserCtxt", "xmlRelaxNGParserCtxtPtr", "xmlRelaxNGParserCtxtPtr"), + 'xmlRelaxNGValidCtxtPtr': ('O', "relaxNgValidCtxt", "xmlRelaxNGValidCtxtPtr", "xmlRelaxNGValidCtxtPtr"), + 'xmlSchemaPtr': ('O', "Schema", "xmlSchemaPtr", "xmlSchemaPtr"), + 'xmlSchemaParserCtxtPtr': ('O', "SchemaParserCtxt", "xmlSchemaParserCtxtPtr", "xmlSchemaParserCtxtPtr"), + 'xmlSchemaValidCtxtPtr': ('O', "SchemaValidCtxt", "xmlSchemaValidCtxtPtr", "xmlSchemaValidCtxtPtr"), +} + +py_return_types = { + 'xmlXPathObjectPtr': ('O', "foo", "xmlXPathObjectPtr", "xmlXPathObjectPtr"), +} + +unknown_types = {} + +foreign_encoding_args = ( + 'htmlCreateMemoryParserCtxt', + 'htmlCtxtReadMemory', + 'htmlParseChunk', + 'htmlReadMemory', + 'xmlCreateMemoryParserCtxt', + 'xmlCtxtReadMemory', + 'xmlCtxtResetPush', + 'xmlParseChunk', + 'xmlParseMemory', + 'xmlReadMemory', + 'xmlRecoverMemory', +) + +####################################################################### +# +# This part writes the C <-> Python stubs libxml2-py.[ch] and +# the table libxml2-export.c to add when registrering the Python module +# +####################################################################### + +# Class methods which are written by hand in libxml.c but the Python-level +# code is still automatically generated (so they are not in skip_function()). +skip_impl = ( + 'xmlSaveFileTo', + 'xmlSaveFormatFileTo', +) + +def skip_function(name): + if name[0:12] == "xmlXPathWrap": + return 1 + if name == "xmlFreeParserCtxt": + return 1 + if name == "xmlCleanupParser": + return 1 + if name == "xmlFreeTextReader": + return 1 +# if name[0:11] == "xmlXPathNew": +# return 1 + # the next function is defined in libxml.c + if name == "xmlRelaxNGFreeValidCtxt": + return 1 + if name == "xmlFreeValidCtxt": + return 1 + if name == "xmlSchemaFreeValidCtxt": + return 1 + +# +# Those are skipped because the Const version is used of the bindings +# instead. +# + if name == "xmlTextReaderBaseUri": + return 1 + if name == "xmlTextReaderLocalName": + return 1 + if name == "xmlTextReaderName": + return 1 + if name == "xmlTextReaderNamespaceUri": + return 1 + if name == "xmlTextReaderPrefix": + return 1 + if name == "xmlTextReaderXmlLang": + return 1 + if name == "xmlTextReaderValue": + return 1 + if name == "xmlOutputBufferClose": # handled by by the superclass + return 1 + if name == "xmlOutputBufferFlush": # handled by by the superclass + return 1 + if name == "xmlErrMemory": + return 1 + + if name == "xmlValidBuildContentModel": + return 1 + if name == "xmlValidateElementDecl": + return 1 + if name == "xmlValidateAttributeDecl": + return 1 + if name == "xmlPopInputCallbacks": + return 1 + + return 0 + +def print_function_wrapper(name, output, export, include): + global py_types + global unknown_types + global functions + global skipped_modules + + try: + (desc, ret, args, file, cond) = functions[name] + except: + print("failed to get function %s infos") + return + + if file in skipped_modules: + return 0 + if skip_function(name) == 1: + return 0 + if name in skip_impl: + # Don't delete the function entry in the caller. + return 1 + + c_call = "" + format="" + format_args="" + c_args="" + c_return="" + c_convert="" + c_release="" + num_bufs=0 + for arg in args: + # This should be correct + if arg[1][0:6] == "const ": + arg[1] = arg[1][6:] + c_args = c_args + " %s %s;\n" % (arg[1], arg[0]) + if arg[1] in py_types: + (f, t, n, c) = py_types[arg[1]] + if (f == 'z') and (name in foreign_encoding_args) and (num_bufs == 0): + f = 's#' + if f != None: + format = format + f + if t != None: + format_args = format_args + ", &pyobj_%s" % (arg[0]) + c_args = c_args + " PyObject *pyobj_%s;\n" % (arg[0]) + c_convert = c_convert + \ + " %s = (%s) Py%s_Get(pyobj_%s);\n" % (arg[0], + arg[1], t, arg[0]) + else: + format_args = format_args + ", &%s" % (arg[0]) + if f == 's#': + format_args = format_args + ", &py_buffsize%d" % num_bufs + c_args = c_args + " int py_buffsize%d;\n" % num_bufs + num_bufs = num_bufs + 1 + if c_call != "": + c_call = c_call + ", " + c_call = c_call + "%s" % (arg[0]) + if t == "File": + c_release = c_release + \ + " PyFile_Release(%s);\n" % (arg[0]) + else: + if arg[1] in skipped_types: + return 0 + if arg[1] in unknown_types: + lst = unknown_types[arg[1]] + lst.append(name) + else: + unknown_types[arg[1]] = [name] + return -1 + if format != "": + format = format + ":%s" % (name) + + if ret[0] == 'void': + if file == "python_accessor": + if args[1][1] == "char *" or args[1][1] == "xmlChar *": + c_call = "\n if (%s->%s != NULL) xmlFree(%s->%s);\n" % ( + args[0][0], args[1][0], args[0][0], args[1][0]) + c_call = c_call + " %s->%s = (%s)xmlStrdup((const xmlChar *)%s);\n" % (args[0][0], + args[1][0], args[1][1], args[1][0]) + else: + c_call = "\n %s->%s = %s;\n" % (args[0][0], args[1][0], + args[1][0]) + else: + c_call = "\n %s(%s);\n" % (name, c_call) + ret_convert = " Py_INCREF(Py_None);\n return(Py_None);\n" + elif ret[0] in py_types: + (f, t, n, c) = py_types[ret[0]] + c_return = c_return + " %s c_retval;\n" % (ret[0]) + if file == "python_accessor" and ret[2] != None: + c_call = "\n c_retval = %s->%s;\n" % (args[0][0], ret[2]) + else: + c_call = "\n c_retval = %s(%s);\n" % (name, c_call) + ret_convert = " py_retval = libxml_%sWrap((%s) c_retval);\n" % (n,c) + ret_convert = ret_convert + " return(py_retval);\n" + elif ret[0] in py_return_types: + (f, t, n, c) = py_return_types[ret[0]] + c_return = c_return + " %s c_retval;\n" % (ret[0]) + c_call = "\n c_retval = %s(%s);\n" % (name, c_call) + ret_convert = " py_retval = libxml_%sWrap((%s) c_retval);\n" % (n,c) + ret_convert = ret_convert + " return(py_retval);\n" + else: + if ret[0] in skipped_types: + return 0 + if ret[0] in unknown_types: + lst = unknown_types[ret[0]] + lst.append(name) + else: + unknown_types[ret[0]] = [name] + return -1 + + if cond != None and cond != "": + include.write("#if %s\n" % cond) + export.write("#if %s\n" % cond) + output.write("#if %s\n" % cond) + + include.write("PyObject * ") + include.write("libxml_%s(PyObject *self, PyObject *args);\n" % (name)) + + export.write(" { (char *)\"%s\", libxml_%s, METH_VARARGS, NULL },\n" % + (name, name)) + + if file == "python": + # Those have been manually generated + if cond != None and cond != "": + include.write("#endif\n") + export.write("#endif\n") + output.write("#endif\n") + return 1 + if file == "python_accessor" and ret[0] != "void" and ret[2] is None: + # Those have been manually generated + if cond != None and cond != "": + include.write("#endif\n") + export.write("#endif\n") + output.write("#endif\n") + return 1 + + output.write("PyObject *\n") + output.write("libxml_%s(PyObject *self ATTRIBUTE_UNUSED," % (name)) + output.write(" PyObject *args") + if format == "": + output.write(" ATTRIBUTE_UNUSED") + output.write(") {\n") + if ret[0] != 'void': + output.write(" PyObject *py_retval;\n") + if c_return != "": + output.write(c_return) + if c_args != "": + output.write(c_args) + if format != "": + output.write("\n if (!PyArg_ParseTuple(args, (char *)\"%s\"%s))\n" % + (format, format_args)) + output.write(" return(NULL);\n") + if c_convert != "": + output.write(c_convert) + + output.write(c_call) + if c_release != "": + output.write(c_release) + output.write(ret_convert) + output.write("}\n\n") + if cond != None and cond != "": + include.write("#endif /* %s */\n" % cond) + export.write("#endif /* %s */\n" % cond) + output.write("#endif /* %s */\n" % cond) + return 1 + +def buildStubs(): + global py_types + global py_return_types + global unknown_types + + try: + f = open(os.path.join(srcPref,"libxml2-api.xml")) + data = f.read() + (parser, target) = getparser() + parser.feed(data) + parser.close() + except IOError as msg: + try: + f = open(os.path.join(srcPref,"..","doc","libxml2-api.xml")) + data = f.read() + (parser, target) = getparser() + parser.feed(data) + parser.close() + except IOError as msg: + print(file, ":", msg) + sys.exit(1) + + n = len(list(functions.keys())) + print("Found %d functions in libxml2-api.xml" % (n)) + + py_types['pythonObject'] = ('O', "pythonObject", "pythonObject", "pythonObject") + try: + f = open(os.path.join(srcPref,"libxml2-python-api.xml")) + data = f.read() + (parser, target) = getparser() + parser.feed(data) + parser.close() + except IOError as msg: + print(file, ":", msg) + + + print("Found %d functions in libxml2-python-api.xml" % ( + len(list(functions.keys())) - n)) + nb_wrap = 0 + failed = 0 + skipped = 0 + + include = open("libxml2-py.h", "w") + include.write("/* Generated */\n\n") + export = open("libxml2-export.c", "w") + export.write("/* Generated */\n\n") + wrapper = open("libxml2-py.c", "w") + wrapper.write("/* Generated */\n\n") + wrapper.write("#include <Python.h>\n") + wrapper.write("#include <libxml/xmlversion.h>\n") + wrapper.write("#include <libxml/tree.h>\n") + wrapper.write("#include <libxml/xmlschemastypes.h>\n") + wrapper.write("#include \"libxml_wrap.h\"\n") + wrapper.write("#include \"libxml2-py.h\"\n\n") + for function in sorted(functions.keys()): + ret = print_function_wrapper(function, wrapper, export, include) + if ret < 0: + failed = failed + 1 + del functions[function] + if ret == 0: + skipped = skipped + 1 + del functions[function] + if ret == 1: + nb_wrap = nb_wrap + 1 + include.close() + export.close() + wrapper.close() + + print("Generated %d wrapper functions, %d failed, %d skipped\n" % (nb_wrap, + failed, skipped)) + print("Missing type converters: ") + for type in list(unknown_types.keys()): + print("%s:%d " % (type, len(unknown_types[type]))) + print() + +####################################################################### +# +# This part writes part of the Python front-end classes based on +# mapping rules between types and classes and also based on function +# renaming to get consistent function names at the Python level +# +####################################################################### + +# +# The type automatically remapped to generated classes +# +classes_type = { + "xmlNodePtr": ("._o", "xmlNode(_obj=%s)", "xmlNode"), + "xmlNode *": ("._o", "xmlNode(_obj=%s)", "xmlNode"), + "xmlDocPtr": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "xmlDoc *": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "htmlDocPtr": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "htmlxmlDocPtr *": ("._o", "xmlDoc(_obj=%s)", "xmlDoc"), + "xmlAttrPtr": ("._o", "xmlAttr(_obj=%s)", "xmlAttr"), + "xmlAttr *": ("._o", "xmlAttr(_obj=%s)", "xmlAttr"), + "xmlNsPtr": ("._o", "xmlNs(_obj=%s)", "xmlNs"), + "xmlNs *": ("._o", "xmlNs(_obj=%s)", "xmlNs"), + "xmlDtdPtr": ("._o", "xmlDtd(_obj=%s)", "xmlDtd"), + "xmlDtd *": ("._o", "xmlDtd(_obj=%s)", "xmlDtd"), + "xmlEntityPtr": ("._o", "xmlEntity(_obj=%s)", "xmlEntity"), + "xmlEntity *": ("._o", "xmlEntity(_obj=%s)", "xmlEntity"), + "xmlElementPtr": ("._o", "xmlElement(_obj=%s)", "xmlElement"), + "xmlElement *": ("._o", "xmlElement(_obj=%s)", "xmlElement"), + "xmlAttributePtr": ("._o", "xmlAttribute(_obj=%s)", "xmlAttribute"), + "xmlAttribute *": ("._o", "xmlAttribute(_obj=%s)", "xmlAttribute"), + "xmlXPathContextPtr": ("._o", "xpathContext(_obj=%s)", "xpathContext"), + "xmlXPathContext *": ("._o", "xpathContext(_obj=%s)", "xpathContext"), + "xmlXPathParserContext *": ("._o", "xpathParserContext(_obj=%s)", "xpathParserContext"), + "xmlXPathParserContextPtr": ("._o", "xpathParserContext(_obj=%s)", "xpathParserContext"), + "xmlParserCtxtPtr": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"), + "xmlParserCtxt *": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"), + "htmlParserCtxtPtr": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"), + "htmlParserCtxt *": ("._o", "parserCtxt(_obj=%s)", "parserCtxt"), + "xmlValidCtxtPtr": ("._o", "ValidCtxt(_obj=%s)", "ValidCtxt"), + "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"), + "xmlTextReaderLocatorPtr": ("._o", "xmlTextReaderLocator(_obj=%s)", "xmlTextReaderLocator"), + "xmlTextReaderPtr": ("._o", "xmlTextReader(_obj=%s)", "xmlTextReader"), + 'xmlRelaxNGPtr': ('._o', "relaxNgSchema(_obj=%s)", "relaxNgSchema"), + 'xmlRelaxNGParserCtxtPtr': ('._o', "relaxNgParserCtxt(_obj=%s)", "relaxNgParserCtxt"), + 'xmlRelaxNGValidCtxtPtr': ('._o', "relaxNgValidCtxt(_obj=%s)", "relaxNgValidCtxt"), + 'xmlSchemaPtr': ("._o", "Schema(_obj=%s)", "Schema"), + 'xmlSchemaParserCtxtPtr': ("._o", "SchemaParserCtxt(_obj=%s)", "SchemaParserCtxt"), + 'xmlSchemaValidCtxtPtr': ("._o", "SchemaValidCtxt(_obj=%s)", "SchemaValidCtxt"), +} + +converter_type = { + "xmlXPathObjectPtr": "xpathObjectRet(%s)", +} + +primary_classes = ["xmlNode", "xmlDoc"] + +classes_ancestor = { + "xmlNode" : "xmlCore", + "xmlDtd" : "xmlNode", + "xmlDoc" : "xmlNode", + "xmlAttr" : "xmlNode", + "xmlNs" : "xmlNode", + "xmlEntity" : "xmlNode", + "xmlElement" : "xmlNode", + "xmlAttribute" : "xmlNode", + "outputBuffer": "ioWriteWrapper", + "inputBuffer": "ioReadWrapper", + "parserCtxt": "parserCtxtCore", + "xmlTextReader": "xmlTextReaderCore", + "ValidCtxt": "ValidCtxtCore", + "SchemaValidCtxt": "SchemaValidCtxtCore", + "relaxNgValidCtxt": "relaxNgValidCtxtCore", +} +classes_destructors = { + "parserCtxt": "xmlFreeParserCtxt", + "catalog": "xmlFreeCatalog", + "URI": "xmlFreeURI", +# "outputBuffer": "xmlOutputBufferClose", + "inputBuffer": "xmlFreeParserInputBuffer", + "xmlReg": "xmlRegFreeRegexp", + "xmlTextReader": "xmlFreeTextReader", + "relaxNgSchema": "xmlRelaxNGFree", + "relaxNgParserCtxt": "xmlRelaxNGFreeParserCtxt", + "relaxNgValidCtxt": "xmlRelaxNGFreeValidCtxt", + "Schema": "xmlSchemaFree", + "SchemaParserCtxt": "xmlSchemaFreeParserCtxt", + "SchemaValidCtxt": "xmlSchemaFreeValidCtxt", + "ValidCtxt": "xmlFreeValidCtxt", +} + +functions_noexcept = { + "xmlHasProp": 1, + "xmlHasNsProp": 1, + "xmlDocSetRootElement": 1, + "xmlNodeGetNs": 1, + "xmlNodeGetNsDefs": 1, + "xmlNextElementSibling": 1, + "xmlPreviousElementSibling": 1, + "xmlFirstElementChild": 1, + "xmlLastElementChild": 1, +} + +reference_keepers = { + "xmlTextReader": [('inputBuffer', 'input')], + "relaxNgValidCtxt": [('relaxNgSchema', 'schema')], + "SchemaValidCtxt": [('Schema', 'schema')], +} + +function_classes = {} + +function_classes["None"] = [] + +def nameFixup(name, classe, type, file): + listname = classe + "List" + ll = len(listname) + l = len(classe) + if name[0:l] == listname: + func = name[l:] + func = func[0:1].lower() + func[1:] + elif name[0:12] == "xmlParserGet" and file == "python_accessor": + func = name[12:] + func = func[0:1].lower() + func[1:] + elif name[0:12] == "xmlParserSet" and file == "python_accessor": + func = name[12:] + func = func[0:1].lower() + func[1:] + elif name[0:10] == "xmlNodeGet" and file == "python_accessor": + func = name[10:] + func = func[0:1].lower() + func[1:] + elif name[0:9] == "xmlURIGet" and file == "python_accessor": + func = name[9:] + func = func[0:1].lower() + func[1:] + elif name[0:9] == "xmlURISet" and file == "python_accessor": + func = name[6:] + func = func[0:1].lower() + func[1:] + elif name[0:11] == "xmlErrorGet" and file == "python_accessor": + func = name[11:] + func = func[0:1].lower() + func[1:] + elif name[0:17] == "xmlXPathParserGet" and file == "python_accessor": + func = name[17:] + func = func[0:1].lower() + func[1:] + elif name[0:11] == "xmlXPathGet" and file == "python_accessor": + func = name[11:] + func = func[0:1].lower() + func[1:] + elif name[0:11] == "xmlXPathSet" and file == "python_accessor": + func = name[8:] + func = func[0:1].lower() + func[1:] + elif name[0:15] == "xmlOutputBuffer" and file != "python": + func = name[15:] + func = func[0:1].lower() + func[1:] + elif name[0:20] == "xmlParserInputBuffer" and file != "python": + func = name[20:] + func = func[0:1].lower() + func[1:] + elif name[0:9] == "xmlRegexp" and file == "xmlregexp": + 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:18] == "xmlTextReaderConst" and file == "xmlreader": + func = name[18:] + elif name[0:13] == "xmlTextReader" and file == "xmlreader": + func = name[13:] + elif name[0:12] == "xmlReaderNew" and file == "xmlreader": + func = name[9:] + elif name[0:11] == "xmlACatalog": + func = name[11:] + func = func[0:1].lower() + func[1:] + elif name[0:l] == classe: + func = name[l:] + func = func[0:1].lower() + func[1:] + elif name[0:7] == "libxml_": + func = name[7:] + func = func[0:1].lower() + func[1:] + elif name[0:6] == "xmlGet": + func = name[6:] + func = func[0:1].lower() + func[1:] + elif name[0:3] == "xml": + func = name[3:] + func = func[0:1].lower() + func[1:] + else: + func = name + if func[0:5] == "xPath": + func = "xpath" + func[5:] + elif func[0:4] == "xPtr": + func = "xpointer" + func[4:] + elif func[0:8] == "xInclude": + func = "xinclude" + func[8:] + elif func[0:2] == "iD": + func = "ID" + func[2:] + elif func[0:3] == "uRI": + func = "URI" + func[3:] + elif func[0:4] == "uTF8": + func = "UTF8" + func[4:] + elif func[0:3] == 'sAX': + func = "SAX" + func[3:] + return func + + +def functionCompare(info1, info2): + (index1, func1, name1, ret1, args1, file1) = info1 + (index2, func2, name2, ret2, args2, file2) = info2 + if file1 == file2: + if func1 < func2: + return -1 + if func1 > func2: + return 1 + if file1 == "python_accessor": + return -1 + if file2 == "python_accessor": + return 1 + if file1 < file2: + return -1 + if file1 > file2: + return 1 + return 0 + +def cmp_to_key(mycmp): + 'Convert a cmp= function into a key= function' + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + return mycmp(self.obj, other.obj) < 0 + def __gt__(self, other): + return mycmp(self.obj, other.obj) > 0 + def __eq__(self, other): + return mycmp(self.obj, other.obj) == 0 + def __le__(self, other): + return mycmp(self.obj, other.obj) <= 0 + def __ge__(self, other): + return mycmp(self.obj, other.obj) >= 0 + def __ne__(self, other): + return mycmp(self.obj, other.obj) != 0 + return K +def writeDoc(name, args, indent, output): + if functions[name][0] is None or functions[name][0] == "": + return + val = functions[name][0] + val = val.replace("NULL", "None") + output.write(indent) + output.write('"""') + while len(val) > 60: + if val[0] == " ": + val = val[1:] + continue + str = val[0:60] + i = str.rfind(" ") + if i < 0: + i = 60 + str = val[0:i] + val = val[i:] + output.write(str) + output.write('\n ') + output.write(indent) + output.write(val) + output.write(' """\n') + +def buildWrappers(): + global ctypes + global py_types + global py_return_types + global unknown_types + global functions + global function_classes + global classes_type + global classes_list + global converter_type + global primary_classes + global converter_type + global classes_ancestor + global converter_type + global primary_classes + global classes_ancestor + global classes_destructors + global functions_noexcept + + for type in classes_type.keys(): + function_classes[classes_type[type][2]] = [] + + # + # Build the list of C types to look for ordered to start + # with primary classes + # + ctypes = [] + classes_list = [] + ctypes_processed = {} + classes_processed = {} + for classe in primary_classes: + classes_list.append(classe) + classes_processed[classe] = () + for type in classes_type.keys(): + tinfo = classes_type[type] + if tinfo[2] == classe: + ctypes.append(type) + ctypes_processed[type] = () + for type in sorted(classes_type.keys()): + if type in ctypes_processed: + continue + tinfo = classes_type[type] + if tinfo[2] not in classes_processed: + classes_list.append(tinfo[2]) + classes_processed[tinfo[2]] = () + + ctypes.append(type) + ctypes_processed[type] = () + + for name in functions.keys(): + found = 0 + (desc, ret, args, file, cond) = functions[name] + for type in ctypes: + classe = classes_type[type][2] + + if name[0:3] == "xml" and len(args) >= 1 and args[0][1] == type: + found = 1 + func = nameFixup(name, classe, type, file) + info = (0, func, name, ret, args, file) + function_classes[classe].append(info) + elif name[0:3] == "xml" and len(args) >= 2 and args[1][1] == type \ + and file != "python_accessor": + found = 1 + func = nameFixup(name, classe, type, file) + info = (1, func, name, ret, args, file) + function_classes[classe].append(info) + elif name[0:4] == "html" and len(args) >= 1 and args[0][1] == type: + found = 1 + func = nameFixup(name, classe, type, file) + info = (0, func, name, ret, args, file) + function_classes[classe].append(info) + elif name[0:4] == "html" and len(args) >= 2 and args[1][1] == type \ + and file != "python_accessor": + found = 1 + func = nameFixup(name, classe, type, file) + info = (1, func, name, ret, args, file) + function_classes[classe].append(info) + if found == 1: + continue + if name[0:8] == "xmlXPath": + continue + if name[0:6] == "xmlStr": + continue + if name[0:10] == "xmlCharStr": + continue + func = nameFixup(name, "None", file, file) + info = (0, func, name, ret, args, file) + function_classes['None'].append(info) + + classes = open("libxml2class.py", "w") + txt = open("libxml2class.txt", "w") + txt.write(" Generated Classes for libxml2-python\n\n") + + txt.write("#\n# Global functions of the module\n#\n\n") + if "None" in function_classes: + flist = function_classes["None"] + flist = sorted(flist, key=cmp_to_key(functionCompare)) + oldfile = "" + for info in flist: + (index, func, name, ret, args, file) = info + if file != oldfile: + classes.write("#\n# Functions from module %s\n#\n\n" % file) + txt.write("\n# functions from module %s\n" % file) + oldfile = file + classes.write("def %s(" % func) + txt.write("%s()\n" % func) + n = 0 + for arg in args: + if n != 0: + classes.write(", ") + classes.write("%s" % arg[0]) + n = n + 1 + classes.write("):\n") + writeDoc(name, args, ' ', classes) + + for arg in args: + if arg[1] in classes_type: + classes.write(" if %s is None: %s__o = None\n" % + (arg[0], arg[0])) + classes.write(" else: %s__o = %s%s\n" % + (arg[0], arg[0], classes_type[arg[1]][0])) + if arg[1] in py_types: + (f, t, n, c) = py_types[arg[1]] + if t == "File": + classes.write(" if %s is not None: %s.flush()\n" % ( + arg[0], arg[0])) + + if ret[0] != "void": + classes.write(" ret = ") + else: + classes.write(" ") + classes.write("libxml2mod.%s(" % name) + n = 0 + for arg in args: + if n != 0: + classes.write(", ") + classes.write("%s" % arg[0]) + if arg[1] in classes_type: + classes.write("__o") + n = n + 1 + classes.write(")\n") + +# This may be needed to reposition the I/O, but likely to cause more harm +# than good. Those changes in Python3 really break the model. +# for arg in args: +# if arg[1] in py_types: +# (f, t, n, c) = py_types[arg[1]] +# if t == "File": +# classes.write(" if %s is not None: %s.seek(0,0)\n"%( +# arg[0], arg[0])) + + if ret[0] != "void": + if ret[0] in classes_type: + # + # Raise an exception + # + if name in functions_noexcept: + classes.write(" if ret is None:return None\n") + elif name.find("URI") >= 0: + classes.write( + " if ret is None:raise uriError('%s() failed')\n" + % (name)) + elif name.find("XPath") >= 0: + classes.write( + " if ret is None:raise xpathError('%s() failed')\n" + % (name)) + elif name.find("Parse") >= 0: + classes.write( + " if ret is None:raise parserError('%s() failed')\n" + % (name)) + else: + classes.write( + " if ret is None:raise treeError('%s() failed')\n" + % (name)) + classes.write(" return ") + classes.write(classes_type[ret[0]][1] % ("ret")) + classes.write("\n") + else: + classes.write(" return ret\n") + classes.write("\n") + + txt.write("\n\n#\n# Set of classes of the module\n#\n\n") + for classname in classes_list: + if classname == "None": + pass + else: + if classname in classes_ancestor: + txt.write("\n\nClass %s(%s)\n" % (classname, + classes_ancestor[classname])) + classes.write("class %s(%s):\n" % (classname, + classes_ancestor[classname])) + classes.write(" def __init__(self, _obj=None):\n") + if classes_ancestor[classname] == "xmlCore" or \ + classes_ancestor[classname] == "xmlNode": + classes.write(" if checkWrapper(_obj) != 0:") + classes.write(" raise TypeError") + classes.write("('%s got a wrong wrapper object type')\n" % \ + classname) + if classname in reference_keepers: + rlist = reference_keepers[classname] + for ref in rlist: + classes.write(" self.%s = None\n" % ref[1]) + classes.write(" self._o = _obj\n") + classes.write(" %s.__init__(self, _obj=_obj)\n\n" % ( + classes_ancestor[classname])) + if classes_ancestor[classname] == "xmlCore" or \ + classes_ancestor[classname] == "xmlNode": + classes.write(" def __repr__(self):\n") + format = "<%s (%%s) object at 0x%%x>" % (classname) + classes.write(" return \"%s\" %% (self.name, int(pos_id (self)))\n\n" % ( + format)) + else: + txt.write("Class %s()\n" % (classname)) + classes.write("class %s:\n" % (classname)) + classes.write(" def __init__(self, _obj=None):\n") + if classname in reference_keepers: + list = reference_keepers[classname] + for ref in list: + classes.write(" self.%s = None\n" % ref[1]) + classes.write(" if _obj != None:self._o = _obj;return\n") + classes.write(" self._o = None\n\n") + destruct=None + if classname in classes_destructors: + classes.write(" def __del__(self):\n") + classes.write(" if self._o != None:\n") + classes.write(" libxml2mod.%s(self._o)\n" % + classes_destructors[classname]) + classes.write(" self._o = None\n\n") + destruct=classes_destructors[classname] + flist = function_classes[classname] + flist = sorted(flist, key=cmp_to_key(functionCompare)) + oldfile = "" + for info in flist: + (index, func, name, ret, args, file) = info + # + # Do not provide as method the destructors for the class + # to avoid double free + # + if name == destruct: + continue + if file != oldfile: + if file == "python_accessor": + classes.write(" # accessors for %s\n" % (classname)) + txt.write(" # accessors\n") + else: + classes.write(" #\n") + classes.write(" # %s functions from module %s\n" % ( + classname, file)) + txt.write("\n # functions from module %s\n" % file) + classes.write(" #\n\n") + oldfile = file + classes.write(" def %s(self" % func) + txt.write(" %s()\n" % func) + n = 0 + for arg in args: + if n != index: + classes.write(", %s" % arg[0]) + n = n + 1 + classes.write("):\n") + writeDoc(name, args, ' ', classes) + n = 0 + for arg in args: + if arg[1] in classes_type: + if n != index: + classes.write(" if %s is None: %s__o = None\n" % + (arg[0], arg[0])) + classes.write(" else: %s__o = %s%s\n" % + (arg[0], arg[0], classes_type[arg[1]][0])) + n = n + 1 + if ret[0] != "void": + classes.write(" ret = ") + else: + classes.write(" ") + classes.write("libxml2mod.%s(" % name) + n = 0 + for arg in args: + if n != 0: + classes.write(", ") + if n != index: + classes.write("%s" % arg[0]) + if arg[1] in classes_type: + classes.write("__o") + else: + classes.write("self") + if arg[1] in classes_type: + classes.write(classes_type[arg[1]][0]) + n = n + 1 + classes.write(")\n") + if ret[0] != "void": + if ret[0] in classes_type: + # + # Raise an exception + # + if name in functions_noexcept: + classes.write( + " if ret is None:return None\n") + elif name.find("URI") >= 0: + classes.write( + " if ret is None:raise uriError('%s() failed')\n" + % (name)) + elif name.find("XPath") >= 0: + classes.write( + " if ret is None:raise xpathError('%s() failed')\n" + % (name)) + elif name.find("Parse") >= 0: + classes.write( + " if ret is None:raise parserError('%s() failed')\n" + % (name)) + else: + classes.write( + " if ret is None:raise treeError('%s() failed')\n" + % (name)) + + # + # generate the returned class wrapper for the object + # + classes.write(" __tmp = ") + classes.write(classes_type[ret[0]][1] % ("ret")) + classes.write("\n") + + # + # Sometime one need to keep references of the source + # class in the returned class object. + # See reference_keepers for the list + # + tclass = classes_type[ret[0]][2] + if tclass in reference_keepers: + list = reference_keepers[tclass] + for pref in list: + if pref[0] == classname: + classes.write(" __tmp.%s = self\n" % + pref[1]) + # + # return the class + # + classes.write(" return __tmp\n") + elif ret[0] in converter_type: + # + # Raise an exception + # + if name in functions_noexcept: + classes.write( + " if ret is None:return None") + elif name.find("URI") >= 0: + classes.write( + " if ret is None:raise uriError('%s() failed')\n" + % (name)) + elif name.find("XPath") >= 0: + classes.write( + " if ret is None:raise xpathError('%s() failed')\n" + % (name)) + elif name.find("Parse") >= 0: + classes.write( + " if ret is None:raise parserError('%s() failed')\n" + % (name)) + else: + classes.write( + " if ret is None:raise treeError('%s() failed')\n" + % (name)) + classes.write(" return ") + classes.write(converter_type[ret[0]] % ("ret")) + classes.write("\n") + else: + classes.write(" return ret\n") + classes.write("\n") + + # + # Generate enum constants + # + for type,enum in enums.items(): + classes.write("# %s\n" % type) + items = enum.items() + items = sorted(items, key=(lambda i: int(i[1]))) + for name,value in items: + classes.write("%s = %s\n" % (name,value)) + classes.write("\n") + + txt.close() + classes.close() + +buildStubs() +buildWrappers() diff --git a/libxml2/python/libxml.c b/libxml2/python/libxml.c new file mode 100644 index 0000000..5b1ff6e --- /dev/null +++ b/libxml2/python/libxml.c @@ -0,0 +1,3934 @@ +/* + * libxml.c: this modules implements the main part of the glue of the + * libxml2 library and the Python interpreter. It provides the + * entry points where an automatically generated stub is either + * unpractical or would not match cleanly the Python model. + * + * If compiled with MERGED_MODULES, the entry point will be used to + * initialize both the libxml2 and the libxslt wrappers + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ +#include <Python.h> +#include <fileobject.h> +/* #include "config.h" */ +#include <libxml/xmlmemory.h> +#include <libxml/parser.h> +#include <libxml/tree.h> +#include <libxml/xpath.h> +#include <libxml/xmlerror.h> +#include <libxml/xpathInternals.h> +#include <libxml/xmlmemory.h> +#include <libxml/xmlIO.h> +#include <libxml/c14n.h> +#include <libxml/xmlreader.h> +#include <libxml/xmlsave.h> +#include "libxml_wrap.h" +#include "libxml2-py.h" + +#if defined(WITH_TRIO) +#include "trio.h" +#define vsnprintf trio_vsnprintf +#endif + +/* #define DEBUG */ +/* #define DEBUG_SAX */ +/* #define DEBUG_XPATH */ +/* #define DEBUG_ERROR */ +/* #define DEBUG_MEMORY */ +/* #define DEBUG_FILES */ +/* #define DEBUG_LOADER */ + +#if PY_MAJOR_VERSION >= 3 +PyObject *PyInit_libxml2mod(void); + +#define PY_IMPORT_STRING_SIZE PyUnicode_FromStringAndSize +#define PY_IMPORT_STRING PyUnicode_FromString +#else +void initlibxml2mod(void); +#define PY_IMPORT_STRING_SIZE PyString_FromStringAndSize +#define PY_IMPORT_STRING PyString_FromString +#endif + + +/** + * TODO: + * + * macro to flag unimplemented blocks + */ +#define TODO \ + xmlGenericError(xmlGenericErrorContext, \ + "Unimplemented block at %s:%d\n", \ + __FILE__, __LINE__); +/* + * the following vars are used for XPath extensions, but + * are also referenced within the parser cleanup routine. + */ +static int libxml_xpathCallbacksInitialized = 0; + +typedef struct libxml_xpathCallback { + xmlXPathContextPtr ctx; + xmlChar *name; + xmlChar *ns_uri; + PyObject *function; +} libxml_xpathCallback, *libxml_xpathCallbackPtr; +typedef libxml_xpathCallback libxml_xpathCallbackArray[]; +static int libxml_xpathCallbacksAllocd = 10; +static libxml_xpathCallbackArray *libxml_xpathCallbacks = NULL; +static int libxml_xpathCallbacksNb = 0; + +/************************************************************************ + * * + * Memory debug interface * + * * + ************************************************************************/ + +#if 0 +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); +#endif + +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; + +static void +libxml_xmlErrorInitialize(void); /* forward declare */ + +PyObject * +libxml_xmlMemoryUsed(PyObject * self ATTRIBUTE_UNUSED, + PyObject * args ATTRIBUTE_UNUSED) +{ + long ret; + PyObject *py_retval; + + ret = xmlMemUsed(); + + py_retval = libxml_longWrap(ret); + return (py_retval); +} + +PyObject * +libxml_xmlDebugMemory(PyObject * self ATTRIBUTE_UNUSED, PyObject * args) +{ + int activate; + PyObject *py_retval; + long ret; + + if (!PyArg_ParseTuple(args, (char *) "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 { + /* + * cleanup first, because some memory has been + * allocated with the non-debug malloc in xmlInitParser + * when the python module was imported + */ + xmlCleanupParser(); + ret = (long) xmlMemSetup(xmlMemFree, xmlMemMalloc, + xmlMemRealloc, xmlMemoryStrdup); + if (ret < 0) + goto error; + libxmlMemoryAllocatedBase = xmlMemUsed(); + /* reinitialize */ + xmlInitParser(); + libxml_xmlErrorInitialize(); + } + 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_xmlPythonCleanupParser(PyObject *self ATTRIBUTE_UNUSED, + PyObject *args ATTRIBUTE_UNUSED) { + + int ix; + long freed = -1; + + if (libxmlMemoryDebug) { + freed = xmlMemUsed(); + } + + xmlCleanupParser(); + /* + * Need to confirm whether we really want to do this (required for + * memcheck) in all cases... + */ + + if (libxml_xpathCallbacks != NULL) { /* if ext funcs declared */ + for (ix=0; ix<libxml_xpathCallbacksNb; ix++) { + if ((*libxml_xpathCallbacks)[ix].name != NULL) + xmlFree((*libxml_xpathCallbacks)[ix].name); + if ((*libxml_xpathCallbacks)[ix].ns_uri != NULL) + xmlFree((*libxml_xpathCallbacks)[ix].ns_uri); + } + libxml_xpathCallbacksNb = 0; + xmlFree(libxml_xpathCallbacks); + libxml_xpathCallbacks = NULL; + } + + if (libxmlMemoryDebug) { + freed -= xmlMemUsed(); + libxmlMemoryAllocatedBase -= freed; + if (libxmlMemoryAllocatedBase < 0) + libxmlMemoryAllocatedBase = 0; + } + + Py_INCREF(Py_None); + return(Py_None); +} + +PyObject * +libxml_xmlDumpMemory(ATTRIBUTE_UNUSED PyObject * self, + ATTRIBUTE_UNUSED PyObject * args) +{ + + if (libxmlMemoryDebug != 0) + xmlMemoryDump(); + Py_INCREF(Py_None); + return (Py_None); +} + +/************************************************************************ + * * + * Handling Python FILE I/O at the C level * + * The raw I/O attack diectly the File objects, while the * + * other routines address the ioWrapper instance instead * + * * + ************************************************************************/ + +/** + * xmlPythonFileCloseUnref: + * @context: the I/O context + * + * Close an I/O channel + */ +static int +xmlPythonFileCloseRaw (void * context) { + PyObject *file, *ret; + +#ifdef DEBUG_FILES + printf("xmlPythonFileCloseUnref\n"); +#endif + file = (PyObject *) context; + if (file == NULL) return(-1); + ret = PyEval_CallMethod(file, (char *) "close", (char *) "()"); + if (ret != NULL) { + Py_DECREF(ret); + } + Py_DECREF(file); + return(0); +} + +/** + * xmlPythonFileReadRaw: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write + * + * Read @len bytes to @buffer from the Python file in the I/O channel + * + * Returns the number of bytes read + */ +static int +xmlPythonFileReadRaw (void * context, char * buffer, int len) { + PyObject *file; + PyObject *ret; + int lenread = -1; + char *data; + +#ifdef DEBUG_FILES + printf("xmlPythonFileReadRaw: %d\n", len); +#endif + file = (PyObject *) context; + if (file == NULL) return(-1); + ret = PyEval_CallMethod(file, (char *) "read", (char *) "(i)", len); + if (ret == NULL) { + printf("xmlPythonFileReadRaw: result is NULL\n"); + return(-1); + } else if (PyBytes_Check(ret)) { + lenread = PyBytes_Size(ret); + data = PyBytes_AsString(ret); +#ifdef PyUnicode_Check + } else if PyUnicode_Check (ret) { +#if PY_VERSION_HEX >= 0x03030000 + Py_ssize_t size; + const char *tmp; + + /* tmp doesn't need to be deallocated */ + tmp = PyUnicode_AsUTF8AndSize(ret, &size); + + lenread = (int) size; + data = (char *) tmp; +#else + PyObject *b; + b = PyUnicode_AsUTF8String(ret); + if (b == NULL) { + printf("xmlPythonFileReadRaw: failed to convert to UTF-8\n"); + return(-1); + } + lenread = PyBytes_Size(b); + data = PyBytes_AsString(b); + Py_DECREF(b); +#endif +#endif + } else { + printf("xmlPythonFileReadRaw: result is not a String\n"); + Py_DECREF(ret); + return(-1); + } + if (lenread > len) + memcpy(buffer, data, len); + else + memcpy(buffer, data, lenread); + Py_DECREF(ret); + return(lenread); +} + +/** + * xmlPythonFileRead: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write + * + * Read @len bytes to @buffer from the I/O channel. + * + * Returns the number of bytes read + */ +static int +xmlPythonFileRead (void * context, char * buffer, int len) { + PyObject *file; + PyObject *ret; + int lenread = -1; + char *data; + +#ifdef DEBUG_FILES + printf("xmlPythonFileRead: %d\n", len); +#endif + file = (PyObject *) context; + if (file == NULL) return(-1); + ret = PyEval_CallMethod(file, (char *) "io_read", (char *) "(i)", len); + if (ret == NULL) { + printf("xmlPythonFileRead: result is NULL\n"); + return(-1); + } else if (PyBytes_Check(ret)) { + lenread = PyBytes_Size(ret); + data = PyBytes_AsString(ret); +#ifdef PyUnicode_Check + } else if PyUnicode_Check (ret) { +#if PY_VERSION_HEX >= 0x03030000 + Py_ssize_t size; + const char *tmp; + + /* tmp doesn't need to be deallocated */ + tmp = PyUnicode_AsUTF8AndSize(ret, &size); + + lenread = (int) size; + data = (char *) tmp; +#else + PyObject *b; + b = PyUnicode_AsUTF8String(ret); + if (b == NULL) { + printf("xmlPythonFileRead: failed to convert to UTF-8\n"); + return(-1); + } + lenread = PyBytes_Size(b); + data = PyBytes_AsString(b); + Py_DECREF(b); +#endif +#endif + } else { + printf("xmlPythonFileRead: result is not a String\n"); + Py_DECREF(ret); + return(-1); + } + if (lenread > len) + memcpy(buffer, data, len); + else + memcpy(buffer, data, lenread); + Py_DECREF(ret); + return(lenread); +} + +/** + * xmlFileWrite: + * @context: the I/O context + * @buffer: where to drop data + * @len: number of bytes to write + * + * Write @len bytes from @buffer to the I/O channel. + * + * Returns the number of bytes written + */ +static int +xmlPythonFileWrite (void * context, const char * buffer, int len) { + PyObject *file; + PyObject *string; + PyObject *ret = NULL; + int written = -1; + +#ifdef DEBUG_FILES + printf("xmlPythonFileWrite: %d\n", len); +#endif + file = (PyObject *) context; + if (file == NULL) return(-1); + string = PY_IMPORT_STRING_SIZE(buffer, len); + if (string == NULL) return(-1); + if (PyObject_HasAttrString(file, (char *) "io_write")) { + ret = PyEval_CallMethod(file, (char *) "io_write", (char *) "(O)", + string); + } else if (PyObject_HasAttrString(file, (char *) "write")) { + ret = PyEval_CallMethod(file, (char *) "write", (char *) "(O)", + string); + } + Py_DECREF(string); + if (ret == NULL) { + printf("xmlPythonFileWrite: result is NULL\n"); + return(-1); + } else if (PyLong_Check(ret)) { + written = (int) PyLong_AsLong(ret); + Py_DECREF(ret); + } else if (ret == Py_None) { + written = len; + Py_DECREF(ret); + } else { + printf("xmlPythonFileWrite: result is not an Int nor None\n"); + Py_DECREF(ret); + } + return(written); +} + +/** + * xmlPythonFileClose: + * @context: the I/O context + * + * Close an I/O channel + */ +static int +xmlPythonFileClose (void * context) { + PyObject *file, *ret = NULL; + +#ifdef DEBUG_FILES + printf("xmlPythonFileClose\n"); +#endif + file = (PyObject *) context; + if (file == NULL) return(-1); + if (PyObject_HasAttrString(file, (char *) "io_close")) { + ret = PyEval_CallMethod(file, (char *) "io_close", (char *) "()"); + } else if (PyObject_HasAttrString(file, (char *) "flush")) { + ret = PyEval_CallMethod(file, (char *) "flush", (char *) "()"); + } + if (ret != NULL) { + Py_DECREF(ret); + } + return(0); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/** + * xmlOutputBufferCreatePythonFile: + * @file: a PyFile_Type + * @encoder: the encoding converter or NULL + * + * Create a buffered output for the progressive saving to a PyFile_Type + * buffered C I/O + * + * Returns the new parser output or NULL + */ +static xmlOutputBufferPtr +xmlOutputBufferCreatePythonFile(PyObject *file, + xmlCharEncodingHandlerPtr encoder) { + xmlOutputBufferPtr ret; + + if (file == NULL) return(NULL); + + ret = xmlAllocOutputBuffer(encoder); + if (ret != NULL) { + ret->context = file; + /* Py_INCREF(file); */ + ret->writecallback = xmlPythonFileWrite; + ret->closecallback = xmlPythonFileClose; + } + + return(ret); +} + +PyObject * +libxml_xmlCreateOutputBuffer(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { + PyObject *py_retval; + PyObject *file; + xmlChar *encoding; + xmlCharEncodingHandlerPtr handler = NULL; + xmlOutputBufferPtr buffer; + + + if (!PyArg_ParseTuple(args, (char *)"Oz:xmlOutputBufferCreate", + &file, &encoding)) + return(NULL); + if ((encoding != NULL) && (encoding[0] != 0)) { + handler = xmlFindCharEncodingHandler((const char *) encoding); + } + buffer = xmlOutputBufferCreatePythonFile(file, handler); + if (buffer == NULL) + printf("libxml_xmlCreateOutputBuffer: buffer == NULL\n"); + py_retval = libxml_xmlOutputBufferPtrWrap(buffer); + return(py_retval); +} + +/** + * libxml_outputBufferGetPythonFile: + * @buffer: the I/O buffer + * + * read the Python I/O from the CObject + * + * Returns the new parser output or NULL + */ +static PyObject * +libxml_outputBufferGetPythonFile(ATTRIBUTE_UNUSED PyObject *self, + PyObject *args) { + PyObject *buffer; + PyObject *file; + xmlOutputBufferPtr obj; + + if (!PyArg_ParseTuple(args, (char *)"O:outputBufferGetPythonFile", + &buffer)) + return(NULL); + + obj = PyoutputBuffer_Get(buffer); + if (obj == NULL) { + fprintf(stderr, + "outputBufferGetPythonFile: obj == NULL\n"); + Py_INCREF(Py_None); + return(Py_None); + } + if (obj->closecallback != xmlPythonFileClose) { + fprintf(stderr, + "outputBufferGetPythonFile: not a python file wrapper\n"); + Py_INCREF(Py_None); + return(Py_None); + } + file = (PyObject *) obj->context; + if (file == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + Py_INCREF(file); + return(file); +} + +static PyObject * +libxml_xmlOutputBufferClose(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval; + xmlOutputBufferPtr out; + PyObject *pyobj_out; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlOutputBufferClose", &pyobj_out)) + return(NULL); + out = (xmlOutputBufferPtr) PyoutputBuffer_Get(pyobj_out); + /* Buffer may already have been destroyed elsewhere. This is harmless. */ + if (out == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + + c_retval = xmlOutputBufferClose(out); + py_retval = libxml_intWrap((int) c_retval); + return(py_retval); +} + +static PyObject * +libxml_xmlOutputBufferFlush(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval; + xmlOutputBufferPtr out; + PyObject *pyobj_out; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlOutputBufferFlush", &pyobj_out)) + return(NULL); + out = (xmlOutputBufferPtr) PyoutputBuffer_Get(pyobj_out); + + c_retval = xmlOutputBufferFlush(out); + py_retval = libxml_intWrap((int) c_retval); + return(py_retval); +} + +static PyObject * +libxml_xmlSaveFileTo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval; + xmlOutputBufferPtr buf; + PyObject *pyobj_buf; + xmlDocPtr cur; + PyObject *pyobj_cur; + char * encoding; + + if (!PyArg_ParseTuple(args, (char *)"OOz:xmlSaveFileTo", &pyobj_buf, &pyobj_cur, &encoding)) + return(NULL); + buf = (xmlOutputBufferPtr) PyoutputBuffer_Get(pyobj_buf); + cur = (xmlDocPtr) PyxmlNode_Get(pyobj_cur); + + c_retval = xmlSaveFileTo(buf, cur, encoding); + /* xmlSaveTo() freed the memory pointed to by buf, so record that in the + * Python object. */ + ((PyoutputBuffer_Object *)(pyobj_buf))->obj = NULL; + py_retval = libxml_intWrap((int) c_retval); + return(py_retval); +} + +static PyObject * +libxml_xmlSaveFormatFileTo(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + PyObject *py_retval; + int c_retval; + xmlOutputBufferPtr buf; + PyObject *pyobj_buf; + xmlDocPtr cur; + PyObject *pyobj_cur; + char * encoding; + int format; + + if (!PyArg_ParseTuple(args, (char *)"OOzi:xmlSaveFormatFileTo", &pyobj_buf, &pyobj_cur, &encoding, &format)) + return(NULL); + buf = (xmlOutputBufferPtr) PyoutputBuffer_Get(pyobj_buf); + cur = (xmlDocPtr) PyxmlNode_Get(pyobj_cur); + + c_retval = xmlSaveFormatFileTo(buf, cur, encoding, format); + /* xmlSaveFormatFileTo() freed the memory pointed to by buf, so record that + * in the Python object */ + ((PyoutputBuffer_Object *)(pyobj_buf))->obj = NULL; + py_retval = libxml_intWrap((int) c_retval); + return(py_retval); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + + +/** + * xmlParserInputBufferCreatePythonFile: + * @file: a PyFile_Type + * @encoder: the encoding converter or NULL + * + * Create a buffered output for the progressive saving to a PyFile_Type + * buffered C I/O + * + * Returns the new parser output or NULL + */ +static xmlParserInputBufferPtr +xmlParserInputBufferCreatePythonFile(PyObject *file, + xmlCharEncoding encoding) { + xmlParserInputBufferPtr ret; + + if (file == NULL) return(NULL); + + ret = xmlAllocParserInputBuffer(encoding); + if (ret != NULL) { + ret->context = file; + /* Py_INCREF(file); */ + ret->readcallback = xmlPythonFileRead; + ret->closecallback = xmlPythonFileClose; + } + + return(ret); +} + +PyObject * +libxml_xmlCreateInputBuffer(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { + PyObject *py_retval; + PyObject *file; + xmlChar *encoding; + xmlCharEncoding enc = XML_CHAR_ENCODING_NONE; + xmlParserInputBufferPtr buffer; + + + if (!PyArg_ParseTuple(args, (char *)"Oz:xmlParserInputBufferCreate", + &file, &encoding)) + return(NULL); + if ((encoding != NULL) && (encoding[0] != 0)) { + enc = xmlParseCharEncoding((const char *) encoding); + } + buffer = xmlParserInputBufferCreatePythonFile(file, enc); + if (buffer == NULL) + printf("libxml_xmlParserInputBufferCreate: buffer == NULL\n"); + py_retval = libxml_xmlParserInputBufferPtrWrap(buffer); + return(py_retval); +} + +/************************************************************************ + * * + * Providing the resolver at the Python level * + * * + ************************************************************************/ + +static xmlExternalEntityLoader defaultExternalEntityLoader = NULL; +static PyObject *pythonExternalEntityLoaderObjext; + +static xmlParserInputPtr +pythonExternalEntityLoader(const char *URL, const char *ID, + xmlParserCtxtPtr ctxt) { + xmlParserInputPtr result = NULL; + if (pythonExternalEntityLoaderObjext != NULL) { + PyObject *ret; + PyObject *ctxtobj; + + ctxtobj = libxml_xmlParserCtxtPtrWrap(ctxt); +#ifdef DEBUG_LOADER + printf("pythonExternalEntityLoader: ready to call\n"); +#endif + + ret = PyObject_CallFunction(pythonExternalEntityLoaderObjext, + (char *) "(ssO)", URL, ID, ctxtobj); + Py_XDECREF(ctxtobj); +#ifdef DEBUG_LOADER + printf("pythonExternalEntityLoader: result "); + PyObject_Print(ret, stdout, 0); + printf("\n"); +#endif + + if (ret != NULL) { + if (PyObject_HasAttrString(ret, (char *) "read")) { + xmlParserInputBufferPtr buf; + + buf = xmlAllocParserInputBuffer(XML_CHAR_ENCODING_NONE); + if (buf != NULL) { + buf->context = ret; + buf->readcallback = xmlPythonFileReadRaw; + buf->closecallback = xmlPythonFileCloseRaw; + result = xmlNewIOInputStream(ctxt, buf, + XML_CHAR_ENCODING_NONE); + } +#if 0 + } else { + if (URL != NULL) + printf("pythonExternalEntityLoader: can't read %s\n", + URL); +#endif + } + if (result == NULL) { + Py_DECREF(ret); + } else if (URL != NULL) { + result->filename = (char *) xmlStrdup((const xmlChar *)URL); + result->directory = xmlParserGetDirectory((const char *) URL); + } + } + } + if ((result == NULL) && (defaultExternalEntityLoader != NULL)) { + result = defaultExternalEntityLoader(URL, ID, ctxt); + } + return(result); +} + +PyObject * +libxml_xmlSetEntityLoader(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { + PyObject *py_retval; + PyObject *loader; + + if (!PyArg_ParseTuple(args, (char *)"O:libxml_xmlSetEntityLoader", + &loader)) + return(NULL); + + if (!PyCallable_Check(loader)) { + PyErr_SetString(PyExc_ValueError, "entity loader is not callable"); + return(NULL); + } + +#ifdef DEBUG_LOADER + printf("libxml_xmlSetEntityLoader\n"); +#endif + if (defaultExternalEntityLoader == NULL) + defaultExternalEntityLoader = xmlGetExternalEntityLoader(); + + Py_XDECREF(pythonExternalEntityLoaderObjext); + pythonExternalEntityLoaderObjext = loader; + Py_XINCREF(pythonExternalEntityLoaderObjext); + xmlSetExternalEntityLoader(pythonExternalEntityLoader); + + py_retval = PyLong_FromLong(0); + return(py_retval); +} + +/************************************************************************ + * * + * Input callback registration * + * * + ************************************************************************/ +static PyObject *pythonInputOpenCallbackObject; +static int pythonInputCallbackID = -1; + +static int +pythonInputMatchCallback(ATTRIBUTE_UNUSED const char *URI) +{ + /* Always return success, real decision whether URI is supported will be + * made in open callback. */ + return 1; +} + +static void * +pythonInputOpenCallback(const char *URI) +{ + PyObject *ret; + + ret = PyObject_CallFunction(pythonInputOpenCallbackObject, + (char *)"s", URI); + if (ret == Py_None) { + Py_DECREF(Py_None); + return NULL; + } + return ret; +} + +PyObject * +libxml_xmlRegisterInputCallback(ATTRIBUTE_UNUSED PyObject *self, + PyObject *args) { + PyObject *cb; + + if (!PyArg_ParseTuple(args, + (const char *)"O:libxml_xmlRegisterInputCallback", &cb)) + return(NULL); + + if (!PyCallable_Check(cb)) { + PyErr_SetString(PyExc_ValueError, "input callback is not callable"); + return(NULL); + } + + /* Python module registers a single callback and manages the list of + * all callbacks internally. This is necessitated by xmlInputMatchCallback + * API, which does not allow for passing of data objects to discriminate + * different Python methods. */ + if (pythonInputCallbackID == -1) { + pythonInputCallbackID = xmlRegisterInputCallbacks( + pythonInputMatchCallback, pythonInputOpenCallback, + xmlPythonFileReadRaw, xmlPythonFileCloseRaw); + if (pythonInputCallbackID == -1) + return PyErr_NoMemory(); + pythonInputOpenCallbackObject = cb; + Py_INCREF(pythonInputOpenCallbackObject); + } + + Py_INCREF(Py_None); + return(Py_None); +} + +PyObject * +libxml_xmlUnregisterInputCallback(ATTRIBUTE_UNUSED PyObject *self, + ATTRIBUTE_UNUSED PyObject *args) { + int ret; + + ret = xmlPopInputCallbacks(); + if (pythonInputCallbackID != -1) { + /* Assert that the right input callback was popped. libxml's API does not + * allow removal by ID, so all that could be done is an assert. */ + if (pythonInputCallbackID == ret) { + pythonInputCallbackID = -1; + Py_DECREF(pythonInputOpenCallbackObject); + pythonInputOpenCallbackObject = NULL; + } else { + PyErr_SetString(PyExc_AssertionError, "popped non-python input callback"); + return(NULL); + } + } else if (ret == -1) { + /* No more callbacks to pop */ + PyErr_SetString(PyExc_IndexError, "no input callbacks to pop"); + return(NULL); + } + + Py_INCREF(Py_None); + return(Py_None); +} + +/************************************************************************ + * * + * Handling SAX/xmllib/sgmlop callback interfaces * + * * + ************************************************************************/ + +static void +pythonStartElement(void *user_data, const xmlChar * name, + const xmlChar ** attrs) +{ + int i; + PyObject *handler; + PyObject *dict; + PyObject *attrname; + PyObject *attrvalue; + PyObject *result = NULL; + int type = 0; + +#ifdef DEBUG_SAX + printf("pythonStartElement(%s) called\n", name); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "startElement")) + type = 1; + else if (PyObject_HasAttrString(handler, (char *) "start")) + type = 2; + if (type != 0) { + /* + * the xmllib interface always generates a dictionary, + * possibly empty + */ + if ((attrs == NULL) && (type == 1)) { + Py_XINCREF(Py_None); + dict = Py_None; + } else if (attrs == NULL) { + dict = PyDict_New(); + } else { + dict = PyDict_New(); + for (i = 0; attrs[i] != NULL; i++) { + attrname = PY_IMPORT_STRING((char *) attrs[i]); + i++; + if (attrs[i] != NULL) { + attrvalue = PY_IMPORT_STRING((char *) attrs[i]); + } else { + Py_XINCREF(Py_None); + attrvalue = Py_None; + } + PyDict_SetItem(dict, attrname, attrvalue); + Py_DECREF(attrname); + Py_DECREF(attrvalue); + } + } + + if (type == 1) + result = PyObject_CallMethod(handler, (char *) "startElement", + (char *) "sO", name, dict); + else if (type == 2) + result = PyObject_CallMethod(handler, (char *) "start", + (char *) "sO", name, dict); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(dict); + Py_XDECREF(result); + } +} + +static void +pythonStartDocument(void *user_data) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonStartDocument() called\n"); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "startDocument")) { + result = + PyObject_CallMethod(handler, (char *) "startDocument", NULL); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonEndDocument(void *user_data) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonEndDocument() called\n"); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "endDocument")) { + result = + PyObject_CallMethod(handler, (char *) "endDocument", NULL); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } + /* + * The reference to the handler is released there + */ + Py_XDECREF(handler); +} + +static void +pythonEndElement(void *user_data, const xmlChar * name) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonEndElement(%s) called\n", name); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "endElement")) { + result = PyObject_CallMethod(handler, (char *) "endElement", + (char *) "s", name); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } else if (PyObject_HasAttrString(handler, (char *) "end")) { + result = PyObject_CallMethod(handler, (char *) "end", + (char *) "s", name); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonReference(void *user_data, const xmlChar * name) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonReference(%s) called\n", name); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "reference")) { + result = PyObject_CallMethod(handler, (char *) "reference", + (char *) "s", name); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonCharacters(void *user_data, const xmlChar * ch, int len) +{ + PyObject *handler; + PyObject *result = NULL; + int type = 0; + +#ifdef DEBUG_SAX + printf("pythonCharacters(%s, %d) called\n", ch, len); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "characters")) + type = 1; + else if (PyObject_HasAttrString(handler, (char *) "data")) + type = 2; + if (type != 0) { + if (type == 1) + result = PyObject_CallMethod(handler, (char *) "characters", + (char *) "s#", ch, len); + else if (type == 2) + result = PyObject_CallMethod(handler, (char *) "data", + (char *) "s#", ch, len); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonIgnorableWhitespace(void *user_data, const xmlChar * ch, int len) +{ + PyObject *handler; + PyObject *result = NULL; + int type = 0; + +#ifdef DEBUG_SAX + printf("pythonIgnorableWhitespace(%s, %d) called\n", ch, len); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "ignorableWhitespace")) + type = 1; + else if (PyObject_HasAttrString(handler, (char *) "data")) + type = 2; + if (type != 0) { + if (type == 1) + result = + PyObject_CallMethod(handler, + (char *) "ignorableWhitespace", + (char *) "s#", ch, len); + else if (type == 2) + result = + PyObject_CallMethod(handler, (char *) "data", + (char *) "s#", ch, len); + Py_XDECREF(result); + } +} + +static void +pythonProcessingInstruction(void *user_data, + const xmlChar * target, const xmlChar * data) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonProcessingInstruction(%s, %s) called\n", target, data); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "processingInstruction")) { + result = PyObject_CallMethod(handler, (char *) + "processingInstruction", + (char *) "ss", target, data); + Py_XDECREF(result); + } +} + +static void +pythonComment(void *user_data, const xmlChar * value) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonComment(%s) called\n", value); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "comment")) { + result = + PyObject_CallMethod(handler, (char *) "comment", (char *) "s", + value); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonWarning(void *user_data, const char *msg, ...) +{ + PyObject *handler; + PyObject *result; + va_list args; + char buf[1024]; + +#ifdef DEBUG_SAX + printf("pythonWarning(%s) called\n", msg); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "warning")) { + va_start(args, msg); + vsnprintf(buf, 1023, msg, args); + va_end(args); + buf[1023] = 0; + result = + PyObject_CallMethod(handler, (char *) "warning", (char *) "s", + buf); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonError(void *user_data, const char *msg, ...) +{ + PyObject *handler; + PyObject *result; + va_list args; + char buf[1024]; + +#ifdef DEBUG_SAX + printf("pythonError(%s) called\n", msg); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "error")) { + va_start(args, msg); + vsnprintf(buf, 1023, msg, args); + va_end(args); + buf[1023] = 0; + result = + PyObject_CallMethod(handler, (char *) "error", (char *) "s", + buf); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonFatalError(void *user_data, const char *msg, ...) +{ + PyObject *handler; + PyObject *result; + va_list args; + char buf[1024]; + +#ifdef DEBUG_SAX + printf("pythonFatalError(%s) called\n", msg); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "fatalError")) { + va_start(args, msg); + vsnprintf(buf, 1023, msg, args); + va_end(args); + buf[1023] = 0; + result = + PyObject_CallMethod(handler, (char *) "fatalError", + (char *) "s", buf); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonCdataBlock(void *user_data, const xmlChar * ch, int len) +{ + PyObject *handler; + PyObject *result = NULL; + int type = 0; + +#ifdef DEBUG_SAX + printf("pythonCdataBlock(%s, %d) called\n", ch, len); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "cdataBlock")) + type = 1; + else if (PyObject_HasAttrString(handler, (char *) "cdata")) + type = 2; + if (type != 0) { + if (type == 1) + result = + PyObject_CallMethod(handler, (char *) "cdataBlock", + (char *) "s#", ch, len); + else if (type == 2) + result = + PyObject_CallMethod(handler, (char *) "cdata", + (char *) "s#", ch, len); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonExternalSubset(void *user_data, + const xmlChar * name, + const xmlChar * externalID, const xmlChar * systemID) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonExternalSubset(%s, %s, %s) called\n", + name, externalID, systemID); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "externalSubset")) { + result = + PyObject_CallMethod(handler, (char *) "externalSubset", + (char *) "sss", name, externalID, + systemID); + Py_XDECREF(result); + } +} + +static void +pythonEntityDecl(void *user_data, + const xmlChar * name, + int type, + const xmlChar * publicId, + const xmlChar * systemId, xmlChar * content) +{ + PyObject *handler; + PyObject *result; + + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "entityDecl")) { + result = PyObject_CallMethod(handler, (char *) "entityDecl", + (char *) "sisss", name, type, + publicId, systemId, content); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + + + +static void + +pythonNotationDecl(void *user_data, + const xmlChar * name, + const xmlChar * publicId, const xmlChar * systemId) +{ + PyObject *handler; + PyObject *result; + + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "notationDecl")) { + result = PyObject_CallMethod(handler, (char *) "notationDecl", + (char *) "sss", name, publicId, + systemId); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonAttributeDecl(void *user_data, + const xmlChar * elem, + const xmlChar * name, + int type, + int def, + const xmlChar * defaultValue, xmlEnumerationPtr tree) +{ + PyObject *handler; + PyObject *nameList; + PyObject *newName; + xmlEnumerationPtr node; + PyObject *result; + int count; + + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "attributeDecl")) { + count = 0; + for (node = tree; node != NULL; node = node->next) { + count++; + } + nameList = PyList_New(count); + count = 0; + for (node = tree; node != NULL; node = node->next) { + newName = PY_IMPORT_STRING((char *) node->name); + PyList_SetItem(nameList, count, newName); + Py_DECREF(newName); + count++; + } + result = PyObject_CallMethod(handler, (char *) "attributeDecl", + (char *) "ssiisO", elem, name, type, + def, defaultValue, nameList); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(nameList); + Py_XDECREF(result); + } +} + +static void +pythonElementDecl(void *user_data, + const xmlChar * name, + int type, ATTRIBUTE_UNUSED xmlElementContentPtr content) +{ + PyObject *handler; + PyObject *obj; + PyObject *result; + + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "elementDecl")) { + /* TODO: wrap in an elementContent object */ + printf + ("pythonElementDecl: xmlElementContentPtr wrapper missing !\n"); + obj = Py_None; + /* Py_XINCREF(Py_None); isn't the reference just borrowed ??? */ + result = PyObject_CallMethod(handler, (char *) "elementDecl", + (char *) "siO", name, type, obj); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonUnparsedEntityDecl(void *user_data, + const xmlChar * name, + const xmlChar * publicId, + const xmlChar * systemId, + const xmlChar * notationName) +{ + PyObject *handler; + PyObject *result; + + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "unparsedEntityDecl")) { + result = + PyObject_CallMethod(handler, (char *) "unparsedEntityDecl", + (char *) "ssss", name, publicId, systemId, + notationName); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static void +pythonInternalSubset(void *user_data, const xmlChar * name, + const xmlChar * ExternalID, const xmlChar * SystemID) +{ + PyObject *handler; + PyObject *result; + +#ifdef DEBUG_SAX + printf("pythonInternalSubset(%s, %s, %s) called\n", + name, ExternalID, SystemID); +#endif + handler = (PyObject *) user_data; + if (PyObject_HasAttrString(handler, (char *) "internalSubset")) { + result = PyObject_CallMethod(handler, (char *) "internalSubset", + (char *) "sss", name, ExternalID, + SystemID); + if (PyErr_Occurred()) + PyErr_Print(); + Py_XDECREF(result); + } +} + +static xmlSAXHandler pythonSaxHandler = { + pythonInternalSubset, + NULL, /* TODO pythonIsStandalone, */ + NULL, /* TODO pythonHasInternalSubset, */ + NULL, /* TODO pythonHasExternalSubset, */ + NULL, /* TODO pythonResolveEntity, */ + NULL, /* TODO pythonGetEntity, */ + pythonEntityDecl, + pythonNotationDecl, + pythonAttributeDecl, + pythonElementDecl, + pythonUnparsedEntityDecl, + NULL, /* OBSOLETED pythonSetDocumentLocator, */ + pythonStartDocument, + pythonEndDocument, + pythonStartElement, + pythonEndElement, + pythonReference, + pythonCharacters, + pythonIgnorableWhitespace, + pythonProcessingInstruction, + pythonComment, + pythonWarning, + pythonError, + pythonFatalError, + NULL, /* TODO pythonGetParameterEntity, */ + pythonCdataBlock, + pythonExternalSubset, + 1, + NULL, /* TODO mograte to SAX2 */ + NULL, + NULL, + NULL +}; + +/************************************************************************ + * * + * Handling of specific parser context * + * * + ************************************************************************/ + +PyObject * +libxml_xmlCreatePushParser(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + const char *chunk; + int size; + const char *URI; + PyObject *pyobj_SAX = NULL; + xmlSAXHandlerPtr SAX = NULL; + xmlParserCtxtPtr ret; + PyObject *pyret; + + if (!PyArg_ParseTuple + (args, (char *) "Oziz:xmlCreatePushParser", &pyobj_SAX, &chunk, + &size, &URI)) + return (NULL); + +#ifdef DEBUG + printf("libxml_xmlCreatePushParser(%p, %s, %d, %s) called\n", + pyobj_SAX, chunk, size, URI); +#endif + if (pyobj_SAX != Py_None) { + SAX = &pythonSaxHandler; + Py_INCREF(pyobj_SAX); + /* The reference is released in pythonEndDocument() */ + } + ret = xmlCreatePushParserCtxt(SAX, pyobj_SAX, chunk, size, URI); + pyret = libxml_xmlParserCtxtPtrWrap(ret); + return (pyret); +} + +PyObject * +libxml_htmlCreatePushParser(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ +#ifdef LIBXML_HTML_ENABLED + const char *chunk; + int size; + const char *URI; + PyObject *pyobj_SAX = NULL; + xmlSAXHandlerPtr SAX = NULL; + xmlParserCtxtPtr ret; + PyObject *pyret; + + if (!PyArg_ParseTuple + (args, (char *) "Oziz:htmlCreatePushParser", &pyobj_SAX, &chunk, + &size, &URI)) + return (NULL); + +#ifdef DEBUG + printf("libxml_htmlCreatePushParser(%p, %s, %d, %s) called\n", + pyobj_SAX, chunk, size, URI); +#endif + if (pyobj_SAX != Py_None) { + SAX = &pythonSaxHandler; + Py_INCREF(pyobj_SAX); + /* The reference is released in pythonEndDocument() */ + } + ret = htmlCreatePushParserCtxt(SAX, pyobj_SAX, chunk, size, URI, + XML_CHAR_ENCODING_NONE); + pyret = libxml_xmlParserCtxtPtrWrap(ret); + return (pyret); +#else + Py_INCREF(Py_None); + return (Py_None); +#endif /* LIBXML_HTML_ENABLED */ +} + +PyObject * +libxml_xmlSAXParseFile(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ +#ifdef LIBXML_SAX1_ENABLED + int recover; + const char *URI; + PyObject *pyobj_SAX = NULL; + xmlSAXHandlerPtr SAX = NULL; + + if (!PyArg_ParseTuple(args, (char *) "Osi:xmlSAXParseFile", &pyobj_SAX, + &URI, &recover)) + return (NULL); + +#ifdef DEBUG + printf("libxml_xmlSAXParseFile(%p, %s, %d) called\n", + pyobj_SAX, URI, recover); +#endif + if (pyobj_SAX == Py_None) { + Py_INCREF(Py_None); + return (Py_None); + } + SAX = &pythonSaxHandler; + Py_INCREF(pyobj_SAX); + /* The reference is released in pythonEndDocument() */ + xmlSAXUserParseFile(SAX, pyobj_SAX, URI); +#endif /* LIBXML_SAX1_ENABLED */ + Py_INCREF(Py_None); + return (Py_None); +} + +PyObject * +libxml_htmlSAXParseFile(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ +#ifdef LIBXML_HTML_ENABLED + const char *URI; + const char *encoding; + PyObject *pyobj_SAX = NULL; + xmlSAXHandlerPtr SAX = NULL; + + if (!PyArg_ParseTuple + (args, (char *) "Osz:htmlSAXParseFile", &pyobj_SAX, &URI, + &encoding)) + return (NULL); + +#ifdef DEBUG + printf("libxml_htmlSAXParseFile(%p, %s, %s) called\n", + pyobj_SAX, URI, encoding); +#endif + if (pyobj_SAX == Py_None) { + Py_INCREF(Py_None); + return (Py_None); + } + SAX = &pythonSaxHandler; + Py_INCREF(pyobj_SAX); + /* The reference is released in pythonEndDocument() */ + htmlSAXParseFile(URI, encoding, SAX, pyobj_SAX); + Py_INCREF(Py_None); + return (Py_None); +#else + Py_INCREF(Py_None); + return (Py_None); +#endif /* LIBXML_HTML_ENABLED */ +} + +/************************************************************************ + * * + * Error message callback * + * * + ************************************************************************/ + +static PyObject *libxml_xmlPythonErrorFuncHandler = NULL; +static PyObject *libxml_xmlPythonErrorFuncCtxt = NULL; + +/* helper to build a xmlMalloc'ed string from a format and va_list */ +/* + * disabled the loop, the repeated call to vsnprintf without reset of ap + * in case the initial buffer was too small segfaulted on x86_64 + * we now directly vsnprintf on a large buffer. + */ +static char * +libxml_buildMessage(const char *msg, va_list ap) +{ + int chars; + char *str; + + str = (char *) xmlMalloc(1000); + if (str == NULL) + return NULL; + + chars = vsnprintf(str, 999, msg, ap); + if (chars >= 998) + str[999] = 0; + + return str; +} + +static void +libxml_xmlErrorFuncHandler(ATTRIBUTE_UNUSED void *ctx, const char *msg, + ...) +{ + va_list ap; + PyObject *list; + PyObject *message; + PyObject *result; + char str[1000]; + +#ifdef DEBUG_ERROR + printf("libxml_xmlErrorFuncHandler(%p, %s, ...) called\n", ctx, msg); +#endif + + + if (libxml_xmlPythonErrorFuncHandler == NULL) { + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + } else { + va_start(ap, msg); + if (vsnprintf(str, 999, msg, ap) >= 998) + str[999] = 0; + va_end(ap); + + list = PyTuple_New(2); + PyTuple_SetItem(list, 0, libxml_xmlPythonErrorFuncCtxt); + Py_XINCREF(libxml_xmlPythonErrorFuncCtxt); + message = libxml_charPtrConstWrap(str); + PyTuple_SetItem(list, 1, message); + result = PyEval_CallObject(libxml_xmlPythonErrorFuncHandler, list); + Py_XDECREF(list); + Py_XDECREF(result); + } +} + +static void +libxml_xmlErrorInitialize(void) +{ +#ifdef DEBUG_ERROR + printf("libxml_xmlErrorInitialize() called\n"); +#endif + xmlSetGenericErrorFunc(NULL, libxml_xmlErrorFuncHandler); + xmlThrDefSetGenericErrorFunc(NULL, libxml_xmlErrorFuncHandler); +} + +static PyObject * +libxml_xmlRegisterErrorHandler(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval; + PyObject *pyobj_f; + PyObject *pyobj_ctx; + + if (!PyArg_ParseTuple + (args, (char *) "OO:xmlRegisterErrorHandler", &pyobj_f, + &pyobj_ctx)) + return (NULL); + +#ifdef DEBUG_ERROR + printf("libxml_xmlRegisterErrorHandler(%p, %p) called\n", pyobj_ctx, + pyobj_f); +#endif + + if (libxml_xmlPythonErrorFuncHandler != NULL) { + Py_XDECREF(libxml_xmlPythonErrorFuncHandler); + } + if (libxml_xmlPythonErrorFuncCtxt != NULL) { + Py_XDECREF(libxml_xmlPythonErrorFuncCtxt); + } + + Py_XINCREF(pyobj_ctx); + Py_XINCREF(pyobj_f); + + /* TODO: check f is a function ! */ + libxml_xmlPythonErrorFuncHandler = pyobj_f; + libxml_xmlPythonErrorFuncCtxt = pyobj_ctx; + + py_retval = libxml_intWrap(1); + return (py_retval); +} + + +/************************************************************************ + * * + * Per parserCtxt error handler * + * * + ************************************************************************/ + +typedef struct +{ + PyObject *f; + PyObject *arg; +} xmlParserCtxtPyCtxt; +typedef xmlParserCtxtPyCtxt *xmlParserCtxtPyCtxtPtr; + +static void +libxml_xmlParserCtxtGenericErrorFuncHandler(void *ctx, int severity, char *str) +{ + PyObject *list; + PyObject *result; + xmlParserCtxtPtr ctxt; + xmlParserCtxtPyCtxtPtr pyCtxt; + +#ifdef DEBUG_ERROR + printf("libxml_xmlParserCtxtGenericErrorFuncHandler(%p, %s, ...) called\n", ctx, str); +#endif + + ctxt = (xmlParserCtxtPtr)ctx; + pyCtxt = (xmlParserCtxtPyCtxtPtr)ctxt->_private; + + 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); +} + +static PyObject * +libxml_xmlParserCtxtSetErrorHandler(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:xmlParserCtxtSetErrorHandler", + &pyobj_ctxt, &pyobj_f, &pyobj_arg)) + 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; + } + else { + pyCtxt = (xmlParserCtxtPyCtxtPtr)ctxt->_private; + } + /* TODO: check f is a function ! */ + Py_XDECREF(pyCtxt->f); + Py_XINCREF(pyobj_f); + pyCtxt->f = pyobj_f; + Py_XDECREF(pyCtxt->arg); + Py_XINCREF(pyobj_arg); + pyCtxt->arg = pyobj_arg; + + 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 PyObject * +libxml_xmlParserCtxtGetErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) +{ + PyObject *py_retval; + xmlParserCtxtPtr ctxt; + xmlParserCtxtPyCtxtPtr pyCtxt; + PyObject *pyobj_ctxt; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlParserCtxtGetErrorHandler", + &pyobj_ctxt)) + return(NULL); + ctxt = (xmlParserCtxtPtr) PyparserCtxt_Get(pyobj_ctxt); + 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 { + /* 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); + } + return(py_retval); +} + +static 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); +} + +/*** + * xmlValidCtxt stuff + */ + +typedef struct +{ + PyObject *warn; + PyObject *error; + PyObject *arg; +} xmlValidCtxtPyCtxt; +typedef xmlValidCtxtPyCtxt *xmlValidCtxtPyCtxtPtr; + +static void +libxml_xmlValidCtxtGenericErrorFuncHandler(void *ctx, ATTRIBUTE_UNUSED int severity, char *str) +{ + PyObject *list; + PyObject *result; + xmlValidCtxtPyCtxtPtr pyCtxt; + +#ifdef DEBUG_ERROR + printf("libxml_xmlValidCtxtGenericErrorFuncHandler(%p, %d, %s, ...) called\n", ctx, severity, str); +#endif + + pyCtxt = (xmlValidCtxtPyCtxtPtr)ctx; + + list = PyTuple_New(2); + PyTuple_SetItem(list, 0, libxml_charPtrWrap(str)); + PyTuple_SetItem(list, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + result = PyEval_CallObject(pyCtxt->error, list); + if (result == NULL) + { + /* TODO: manage for the exception to be propagated... */ + PyErr_Print(); + } + Py_XDECREF(list); + Py_XDECREF(result); +} + +static void +libxml_xmlValidCtxtGenericWarningFuncHandler(void *ctx, ATTRIBUTE_UNUSED int severity, char *str) +{ + PyObject *list; + PyObject *result; + xmlValidCtxtPyCtxtPtr pyCtxt; + +#ifdef DEBUG_ERROR + printf("libxml_xmlValidCtxtGenericWarningFuncHandler(%p, %d, %s, ...) called\n", ctx, severity, str); +#endif + + pyCtxt = (xmlValidCtxtPyCtxtPtr)ctx; + + list = PyTuple_New(2); + PyTuple_SetItem(list, 0, libxml_charPtrWrap(str)); + PyTuple_SetItem(list, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + result = PyEval_CallObject(pyCtxt->warn, list); + if (result == NULL) + { + /* TODO: manage for the exception to be propagated... */ + PyErr_Print(); + } + Py_XDECREF(list); + Py_XDECREF(result); +} + +static void +libxml_xmlValidCtxtErrorFuncHandler(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlValidCtxtGenericErrorFuncHandler(ctx,XML_PARSER_SEVERITY_VALIDITY_ERROR,libxml_buildMessage(msg,ap)); + va_end(ap); +} + +static void +libxml_xmlValidCtxtWarningFuncHandler(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlValidCtxtGenericWarningFuncHandler(ctx,XML_PARSER_SEVERITY_VALIDITY_WARNING,libxml_buildMessage(msg,ap)); + va_end(ap); +} + +static PyObject * +libxml_xmlSetValidErrors(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval; + PyObject *pyobj_error; + PyObject *pyobj_warn; + PyObject *pyobj_ctx; + PyObject *pyobj_arg = Py_None; + xmlValidCtxtPtr ctxt; + xmlValidCtxtPyCtxtPtr pyCtxt; + + if (!PyArg_ParseTuple + (args, (char *) "OOO|O:xmlSetValidErrors", &pyobj_ctx, &pyobj_error, &pyobj_warn, &pyobj_arg)) + return (NULL); + +#ifdef DEBUG_ERROR + printf("libxml_xmlSetValidErrors(%p, %p, %p) called\n", pyobj_ctx, pyobj_error, pyobj_warn); +#endif + + ctxt = PyValidCtxt_Get(pyobj_ctx); + pyCtxt = xmlMalloc(sizeof(xmlValidCtxtPyCtxt)); + if (pyCtxt == NULL) { + py_retval = libxml_intWrap(-1); + return(py_retval); + } + memset(pyCtxt, 0, sizeof(xmlValidCtxtPyCtxt)); + + + /* TODO: check warn and error is a function ! */ + Py_XDECREF(pyCtxt->error); + Py_XINCREF(pyobj_error); + pyCtxt->error = pyobj_error; + + Py_XDECREF(pyCtxt->warn); + Py_XINCREF(pyobj_warn); + pyCtxt->warn = pyobj_warn; + + Py_XDECREF(pyCtxt->arg); + Py_XINCREF(pyobj_arg); + pyCtxt->arg = pyobj_arg; + + ctxt->error = libxml_xmlValidCtxtErrorFuncHandler; + ctxt->warning = libxml_xmlValidCtxtWarningFuncHandler; + ctxt->userData = pyCtxt; + + py_retval = libxml_intWrap(1); + return (py_retval); +} + + +static PyObject * +libxml_xmlFreeValidCtxt(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + xmlValidCtxtPtr cur; + xmlValidCtxtPyCtxtPtr pyCtxt; + PyObject *pyobj_cur; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlFreeValidCtxt", &pyobj_cur)) + return(NULL); + cur = (xmlValidCtxtPtr) PyValidCtxt_Get(pyobj_cur); + + pyCtxt = (xmlValidCtxtPyCtxtPtr)(cur->userData); + if (pyCtxt != NULL) + { + Py_XDECREF(pyCtxt->error); + Py_XDECREF(pyCtxt->warn); + Py_XDECREF(pyCtxt->arg); + xmlFree(pyCtxt); + } + + xmlFreeValidCtxt(cur); + Py_INCREF(Py_None); + return(Py_None); +} + +#ifdef LIBXML_READER_ENABLED +/************************************************************************ + * * + * Per xmlTextReader error handler * + * * + ************************************************************************/ + +typedef struct +{ + PyObject *f; + PyObject *arg; +} xmlTextReaderPyCtxt; +typedef xmlTextReaderPyCtxt *xmlTextReaderPyCtxtPtr; + +static void +libxml_xmlTextReaderErrorCallback(void *arg, + const char *msg, + int severity, + xmlTextReaderLocatorPtr locator) +{ + xmlTextReaderPyCtxt *pyCtxt = (xmlTextReaderPyCtxt *)arg; + PyObject *list; + PyObject *result; + + 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(severity)); + PyTuple_SetItem(list, 3, libxml_xmlTextReaderLocatorPtrWrap(locator)); + 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 PyObject * +libxml_xmlTextReaderSetErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) +{ + xmlTextReaderPtr reader; + xmlTextReaderPyCtxtPtr pyCtxt; + xmlTextReaderErrorFunc f; + void *arg; + PyObject *pyobj_reader; + PyObject *pyobj_f; + PyObject *pyobj_arg; + PyObject *py_retval; + + if (!PyArg_ParseTuple(args, (char *)"OOO:xmlTextReaderSetErrorHandler", &pyobj_reader, &pyobj_f, &pyobj_arg)) + return(NULL); + reader = (xmlTextReaderPtr) PyxmlTextReader_Get(pyobj_reader); + /* clear previous error handler */ + xmlTextReaderGetErrorHandler(reader,&f,&arg); + if (arg != NULL) { + if (f == (xmlTextReaderErrorFunc) libxml_xmlTextReaderErrorCallback) { + /* ok, it's our error handler! */ + pyCtxt = (xmlTextReaderPyCtxtPtr)arg; + Py_XDECREF(pyCtxt->f); + Py_XDECREF(pyCtxt->arg); + xmlFree(pyCtxt); + } + else { + /* + * there already an arg, and it's not ours, + * there is definitely something wrong going on here... + * we don't know how to free it, so we bail out... + */ + py_retval = libxml_intWrap(-1); + return(py_retval); + } + } + xmlTextReaderSetErrorHandler(reader,NULL,NULL); + /* set new error handler */ + if (pyobj_f != Py_None) + { + pyCtxt = (xmlTextReaderPyCtxtPtr)xmlMalloc(sizeof(xmlTextReaderPyCtxt)); + if (pyCtxt == NULL) { + py_retval = libxml_intWrap(-1); + return(py_retval); + } + Py_XINCREF(pyobj_f); + pyCtxt->f = pyobj_f; + Py_XINCREF(pyobj_arg); + pyCtxt->arg = pyobj_arg; + xmlTextReaderSetErrorHandler(reader, + (xmlTextReaderErrorFunc) libxml_xmlTextReaderErrorCallback, + pyCtxt); + } + + py_retval = libxml_intWrap(1); + return(py_retval); +} + +static PyObject * +libxml_xmlTextReaderGetErrorHandler(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) +{ + xmlTextReaderPtr reader; + xmlTextReaderPyCtxtPtr pyCtxt; + xmlTextReaderErrorFunc f; + void *arg; + PyObject *pyobj_reader; + PyObject *py_retval; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlTextReaderSetErrorHandler", &pyobj_reader)) + return(NULL); + reader = (xmlTextReaderPtr) PyxmlTextReader_Get(pyobj_reader); + xmlTextReaderGetErrorHandler(reader,&f,&arg); + py_retval = PyTuple_New(2); + if (f == (xmlTextReaderErrorFunc)libxml_xmlTextReaderErrorCallback) { + /* ok, it's our error handler! */ + pyCtxt = (xmlTextReaderPyCtxtPtr)arg; + PyTuple_SetItem(py_retval, 0, pyCtxt->f); + Py_XINCREF(pyCtxt->f); + PyTuple_SetItem(py_retval, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + } + else + { + /* f is null or it's not our error handler */ + PyTuple_SetItem(py_retval, 0, Py_None); + Py_XINCREF(Py_None); + PyTuple_SetItem(py_retval, 1, Py_None); + Py_XINCREF(Py_None); + } + return(py_retval); +} + +static PyObject * +libxml_xmlFreeTextReader(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { + xmlTextReaderPtr reader; + PyObject *pyobj_reader; + xmlTextReaderPyCtxtPtr pyCtxt; + xmlTextReaderErrorFunc f; + void *arg; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlFreeTextReader", &pyobj_reader)) + return(NULL); + if (!PyCapsule_CheckExact(pyobj_reader)) { + Py_INCREF(Py_None); + return(Py_None); + } + reader = (xmlTextReaderPtr) PyxmlTextReader_Get(pyobj_reader); + if (reader == NULL) { + Py_INCREF(Py_None); + return(Py_None); + } + + xmlTextReaderGetErrorHandler(reader,&f,&arg); + if (arg != NULL) { + if (f == (xmlTextReaderErrorFunc) libxml_xmlTextReaderErrorCallback) { + /* ok, it's our error handler! */ + pyCtxt = (xmlTextReaderPyCtxtPtr)arg; + Py_XDECREF(pyCtxt->f); + Py_XDECREF(pyCtxt->arg); + xmlFree(pyCtxt); + } + /* + * else, something wrong happened, because the error handler is + * not owned by the python bindings... + */ + } + + xmlFreeTextReader(reader); + Py_INCREF(Py_None); + return(Py_None); +} +#endif + +/************************************************************************ + * * + * XPath extensions * + * * + ************************************************************************/ + +static void +libxml_xmlXPathFuncCallback(xmlXPathParserContextPtr ctxt, int nargs) +{ + PyObject *list, *cur, *result; + xmlXPathObjectPtr obj; + xmlXPathContextPtr rctxt; + PyObject *current_function = NULL; + const xmlChar *name; + const xmlChar *ns_uri; + int i; + + if (ctxt == NULL) + return; + rctxt = ctxt->context; + if (rctxt == NULL) + return; + name = rctxt->function; + ns_uri = rctxt->functionURI; +#ifdef DEBUG_XPATH + printf("libxml_xmlXPathFuncCallback called name %s URI %s\n", name, + ns_uri); +#endif + + /* + * Find the function, it should be there it was there at lookup + */ + for (i = 0; i < libxml_xpathCallbacksNb; i++) { + if ( /* TODO (ctxt == libxml_xpathCallbacks[i].ctx) && */ + (xmlStrEqual(name, (*libxml_xpathCallbacks)[i].name)) && + (xmlStrEqual(ns_uri, (*libxml_xpathCallbacks)[i].ns_uri))) { + current_function = (*libxml_xpathCallbacks)[i].function; + } + } + if (current_function == NULL) { + printf + ("libxml_xmlXPathFuncCallback: internal error %s not found !\n", + name); + return; + } + + list = PyTuple_New(nargs + 1); + PyTuple_SetItem(list, 0, libxml_xmlXPathParserContextPtrWrap(ctxt)); + for (i = nargs - 1; i >= 0; i--) { + obj = valuePop(ctxt); + cur = libxml_xmlXPathObjectPtrWrap(obj); + PyTuple_SetItem(list, i + 1, cur); + } + result = PyEval_CallObject(current_function, list); + Py_DECREF(list); + + obj = libxml_xmlXPathObjectPtrConvert(result); + valuePush(ctxt, obj); +} + +static xmlXPathFunction +libxml_xmlXPathFuncLookupFunc(void *ctxt, const xmlChar * name, + const xmlChar * ns_uri) +{ + int i; + +#ifdef DEBUG_XPATH + printf("libxml_xmlXPathFuncLookupFunc(%p, %s, %s) called\n", + ctxt, name, ns_uri); +#endif + /* + * This is called once only. The address is then stored in the + * XPath expression evaluation, the proper object to call can + * then still be found using the execution context function + * and functionURI fields. + */ + for (i = 0; i < libxml_xpathCallbacksNb; i++) { + if ((ctxt == (*libxml_xpathCallbacks)[i].ctx) && + (xmlStrEqual(name, (*libxml_xpathCallbacks)[i].name)) && + (xmlStrEqual(ns_uri, (*libxml_xpathCallbacks)[i].ns_uri))) { + return (libxml_xmlXPathFuncCallback); + } + } + return (NULL); +} + +static void +libxml_xpathCallbacksInitialize(void) +{ + int i; + + if (libxml_xpathCallbacksInitialized != 0) + return; + +#ifdef DEBUG_XPATH + printf("libxml_xpathCallbacksInitialized called\n"); +#endif + libxml_xpathCallbacks = (libxml_xpathCallbackArray*)xmlMalloc( + libxml_xpathCallbacksAllocd*sizeof(libxml_xpathCallback)); + + for (i = 0; i < libxml_xpathCallbacksAllocd; i++) { + (*libxml_xpathCallbacks)[i].ctx = NULL; + (*libxml_xpathCallbacks)[i].name = NULL; + (*libxml_xpathCallbacks)[i].ns_uri = NULL; + (*libxml_xpathCallbacks)[i].function = NULL; + } + libxml_xpathCallbacksInitialized = 1; +} + +PyObject * +libxml_xmlRegisterXPathFunction(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval; + int c_retval = 0; + xmlChar *name; + xmlChar *ns_uri; + xmlXPathContextPtr ctx; + PyObject *pyobj_ctx; + PyObject *pyobj_f; + int i; + + if (!PyArg_ParseTuple + (args, (char *) "OszO:registerXPathFunction", &pyobj_ctx, &name, + &ns_uri, &pyobj_f)) + return (NULL); + + ctx = (xmlXPathContextPtr) PyxmlXPathContext_Get(pyobj_ctx); + if (libxml_xpathCallbacksInitialized == 0) + libxml_xpathCallbacksInitialize(); + xmlXPathRegisterFuncLookup(ctx, libxml_xmlXPathFuncLookupFunc, ctx); + + if ((pyobj_ctx == NULL) || (name == NULL) || (pyobj_f == NULL)) { + py_retval = libxml_intWrap(-1); + return (py_retval); + } +#ifdef DEBUG_XPATH + printf("libxml_registerXPathFunction(%p, %s, %s) called\n", + ctx, name, ns_uri); +#endif + for (i = 0; i < libxml_xpathCallbacksNb; i++) { + if ((ctx == (*libxml_xpathCallbacks)[i].ctx) && + (xmlStrEqual(name, (*libxml_xpathCallbacks)[i].name)) && + (xmlStrEqual(ns_uri, (*libxml_xpathCallbacks)[i].ns_uri))) { + Py_XINCREF(pyobj_f); + Py_XDECREF((*libxml_xpathCallbacks)[i].function); + (*libxml_xpathCallbacks)[i].function = pyobj_f; + c_retval = 1; + goto done; + } + } + if (libxml_xpathCallbacksNb >= libxml_xpathCallbacksAllocd) { + libxml_xpathCallbacksAllocd+=10; + libxml_xpathCallbacks = (libxml_xpathCallbackArray*)xmlRealloc( + libxml_xpathCallbacks, + libxml_xpathCallbacksAllocd*sizeof(libxml_xpathCallback)); + } + i = libxml_xpathCallbacksNb++; + Py_XINCREF(pyobj_f); + (*libxml_xpathCallbacks)[i].ctx = ctx; + (*libxml_xpathCallbacks)[i].name = xmlStrdup(name); + (*libxml_xpathCallbacks)[i].ns_uri = xmlStrdup(ns_uri); + (*libxml_xpathCallbacks)[i].function = pyobj_f; + c_retval = 1; + + done: + py_retval = libxml_intWrap((int) c_retval); + return (py_retval); +} + +PyObject * +libxml_xmlXPathRegisterVariable(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval; + int c_retval = 0; + xmlChar *name; + xmlChar *ns_uri; + xmlXPathContextPtr ctx; + xmlXPathObjectPtr val; + PyObject *pyobj_ctx; + PyObject *pyobj_value; + + if (!PyArg_ParseTuple + (args, (char *) "OszO:xpathRegisterVariable", &pyobj_ctx, &name, + &ns_uri, &pyobj_value)) + return (NULL); + + ctx = (xmlXPathContextPtr) PyxmlXPathContext_Get(pyobj_ctx); + val = libxml_xmlXPathObjectPtrConvert(pyobj_value); + + c_retval = xmlXPathRegisterVariableNS(ctx, name, ns_uri, val); + py_retval = libxml_intWrap(c_retval); + return (py_retval); +} + +/************************************************************************ + * * + * Global properties access * + * * + ************************************************************************/ +static PyObject * +libxml_name(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + const xmlChar *res; + + if (!PyArg_ParseTuple(args, (char *) "O:name", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + +#ifdef DEBUG + printf("libxml_name: cur = %p type %d\n", cur, cur->type); +#endif + + switch (cur->type) { + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE:{ + xmlDocPtr doc = (xmlDocPtr) cur; + + res = doc->URL; + break; + } + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + res = attr->name; + break; + } + case XML_NAMESPACE_DECL:{ + xmlNsPtr ns = (xmlNsPtr) cur; + + res = ns->prefix; + break; + } + default: + res = cur->name; + break; + } + resultobj = libxml_constxmlCharPtrWrap(res); + + return resultobj; +} + +static PyObject * +libxml_doc(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + xmlDocPtr res; + + if (!PyArg_ParseTuple(args, (char *) "O:doc", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + +#ifdef DEBUG + printf("libxml_doc: cur = %p\n", cur); +#endif + + switch (cur->type) { + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + res = NULL; + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + res = attr->doc; + break; + } + case XML_NAMESPACE_DECL: + res = NULL; + break; + default: + res = cur->doc; + break; + } + resultobj = libxml_xmlDocPtrWrap(res); + return resultobj; +} + +static PyObject * +libxml_properties(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + xmlAttrPtr res; + + if (!PyArg_ParseTuple(args, (char *) "O:properties", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + if ((cur != NULL) && (cur->type == XML_ELEMENT_NODE)) + res = cur->properties; + else + res = NULL; + resultobj = libxml_xmlAttrPtrWrap(res); + return resultobj; +} + +static PyObject * +libxml_next(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + xmlNodePtr res; + + if (!PyArg_ParseTuple(args, (char *) "O:next", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + +#ifdef DEBUG + printf("libxml_next: cur = %p\n", cur); +#endif + + switch (cur->type) { + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + res = NULL; + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + res = (xmlNodePtr) attr->next; + break; + } + case XML_NAMESPACE_DECL:{ + xmlNsPtr ns = (xmlNsPtr) cur; + + res = (xmlNodePtr) ns->next; + break; + } + default: + res = cur->next; + break; + + } + resultobj = libxml_xmlNodePtrWrap(res); + return resultobj; +} + +static PyObject * +libxml_prev(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + xmlNodePtr res; + + if (!PyArg_ParseTuple(args, (char *) "O:prev", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + +#ifdef DEBUG + printf("libxml_prev: cur = %p\n", cur); +#endif + + switch (cur->type) { + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + res = NULL; + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + res = (xmlNodePtr) attr->prev; + } + break; + case XML_NAMESPACE_DECL: + res = NULL; + break; + default: + res = cur->prev; + break; + } + resultobj = libxml_xmlNodePtrWrap(res); + return resultobj; +} + +static PyObject * +libxml_children(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + xmlNodePtr res; + + if (!PyArg_ParseTuple(args, (char *) "O:children", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + +#ifdef DEBUG + printf("libxml_children: cur = %p\n", cur); +#endif + + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + case XML_DTD_NODE: + res = cur->children; + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + res = attr->children; + break; + } + default: + res = NULL; + break; + } + resultobj = libxml_xmlNodePtrWrap(res); + return resultobj; +} + +static PyObject * +libxml_last(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + xmlNodePtr res; + + if (!PyArg_ParseTuple(args, (char *) "O:last", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + +#ifdef DEBUG + printf("libxml_last: cur = %p\n", cur); +#endif + + switch (cur->type) { + case XML_ELEMENT_NODE: + case XML_ENTITY_REF_NODE: + case XML_ENTITY_NODE: + case XML_PI_NODE: + case XML_COMMENT_NODE: + case XML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + case XML_HTML_DOCUMENT_NODE: + case XML_DTD_NODE: + res = cur->last; + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + res = attr->last; + break; + } + default: + res = NULL; + break; + } + resultobj = libxml_xmlNodePtrWrap(res); + return resultobj; +} + +static PyObject * +libxml_parent(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + xmlNodePtr res; + + if (!PyArg_ParseTuple(args, (char *) "O:parent", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + +#ifdef DEBUG + printf("libxml_parent: cur = %p\n", cur); +#endif + + switch (cur->type) { + case XML_DOCUMENT_NODE: + case XML_HTML_DOCUMENT_NODE: +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: +#endif + res = NULL; + break; + case XML_ATTRIBUTE_NODE:{ + xmlAttrPtr attr = (xmlAttrPtr) cur; + + res = attr->parent; + } + break; + case XML_ENTITY_DECL: + case XML_NAMESPACE_DECL: + case XML_XINCLUDE_START: + case XML_XINCLUDE_END: + res = NULL; + break; + default: + res = cur->parent; + break; + } + resultobj = libxml_xmlNodePtrWrap(res); + return resultobj; +} + +static PyObject * +libxml_type(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *resultobj, *obj; + xmlNodePtr cur; + const xmlChar *res = NULL; + + if (!PyArg_ParseTuple(args, (char *) "O:last", &obj)) + return NULL; + cur = PyxmlNode_Get(obj); + if (cur == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + +#ifdef DEBUG + printf("libxml_type: cur = %p\n", cur); +#endif + + switch (cur->type) { + case XML_ELEMENT_NODE: + res = (const xmlChar *) "element"; + break; + case XML_ATTRIBUTE_NODE: + res = (const xmlChar *) "attribute"; + break; + case XML_TEXT_NODE: + res = (const xmlChar *) "text"; + break; + case XML_CDATA_SECTION_NODE: + res = (const xmlChar *) "cdata"; + break; + case XML_ENTITY_REF_NODE: + res = (const xmlChar *) "entity_ref"; + break; + case XML_ENTITY_NODE: + res = (const xmlChar *) "entity"; + break; + case XML_PI_NODE: + res = (const xmlChar *) "pi"; + break; + case XML_COMMENT_NODE: + res = (const xmlChar *) "comment"; + break; + case XML_DOCUMENT_NODE: + res = (const xmlChar *) "document_xml"; + break; + case XML_DOCUMENT_TYPE_NODE: + res = (const xmlChar *) "doctype"; + break; + case XML_DOCUMENT_FRAG_NODE: + res = (const xmlChar *) "fragment"; + break; + case XML_NOTATION_NODE: + res = (const xmlChar *) "notation"; + break; + case XML_HTML_DOCUMENT_NODE: + res = (const xmlChar *) "document_html"; + break; + case XML_DTD_NODE: + res = (const xmlChar *) "dtd"; + break; + case XML_ELEMENT_DECL: + res = (const xmlChar *) "elem_decl"; + break; + case XML_ATTRIBUTE_DECL: + res = (const xmlChar *) "attribute_decl"; + break; + case XML_ENTITY_DECL: + res = (const xmlChar *) "entity_decl"; + break; + case XML_NAMESPACE_DECL: + res = (const xmlChar *) "namespace"; + break; + case XML_XINCLUDE_START: + res = (const xmlChar *) "xinclude_start"; + break; + case XML_XINCLUDE_END: + res = (const xmlChar *) "xinclude_end"; + break; +#ifdef LIBXML_DOCB_ENABLED + case XML_DOCB_DOCUMENT_NODE: + res = (const xmlChar *) "document_docbook"; + break; +#endif + } +#ifdef DEBUG + printf("libxml_type: cur = %p: %s\n", cur, res); +#endif + + resultobj = libxml_constxmlCharPtrWrap(res); + return resultobj; +} + +/************************************************************************ + * * + * Specific accessor functions * + * * + ************************************************************************/ +PyObject * +libxml_xmlNodeGetNsDefs(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval; + xmlNsPtr c_retval; + xmlNodePtr node; + PyObject *pyobj_node; + + if (!PyArg_ParseTuple + (args, (char *) "O:xmlNodeGetNsDefs", &pyobj_node)) + return (NULL); + node = (xmlNodePtr) PyxmlNode_Get(pyobj_node); + + if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) { + Py_INCREF(Py_None); + return (Py_None); + } + c_retval = node->nsDef; + py_retval = libxml_xmlNsPtrWrap((xmlNsPtr) c_retval); + return (py_retval); +} + +PyObject * +libxml_xmlNodeRemoveNsDef(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval; + xmlNsPtr ns, prev; + xmlNodePtr node; + PyObject *pyobj_node; + xmlChar *href; + xmlNsPtr c_retval; + + if (!PyArg_ParseTuple + (args, (char *) "Oz:xmlNodeRemoveNsDef", &pyobj_node, &href)) + return (NULL); + node = (xmlNodePtr) PyxmlNode_Get(pyobj_node); + ns = NULL; + + if ((node == NULL) || (node->type != XML_ELEMENT_NODE)) { + Py_INCREF(Py_None); + return (Py_None); + } + + if (href == NULL) { + ns = node->nsDef; + node->nsDef = NULL; + c_retval = 0; + } + else { + prev = NULL; + ns = node->nsDef; + while (ns != NULL) { + if (xmlStrEqual(ns->href, href)) { + if (prev != NULL) + prev->next = ns->next; + else + node->nsDef = ns->next; + ns->next = NULL; + c_retval = 0; + break; + } + prev = ns; + ns = ns->next; + } + } + + c_retval = ns; + py_retval = libxml_xmlNsPtrWrap((xmlNsPtr) c_retval); + return (py_retval); +} + +PyObject * +libxml_xmlNodeGetNs(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval; + xmlNsPtr c_retval; + xmlNodePtr node; + PyObject *pyobj_node; + + if (!PyArg_ParseTuple(args, (char *) "O:xmlNodeGetNs", &pyobj_node)) + return (NULL); + node = (xmlNodePtr) PyxmlNode_Get(pyobj_node); + + if ((node == NULL) || + ((node->type != XML_ELEMENT_NODE) && + (node->type != XML_ATTRIBUTE_NODE))) { + Py_INCREF(Py_None); + return (Py_None); + } + c_retval = node->ns; + py_retval = libxml_xmlNsPtrWrap((xmlNsPtr) c_retval); + return (py_retval); +} + +#ifdef LIBXML_OUTPUT_ENABLED +/************************************************************************ + * * + * Serialization front-end * + * * + ************************************************************************/ + +static PyObject * +libxml_serializeNode(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval = NULL; + xmlChar *c_retval; + PyObject *pyobj_node; + xmlNodePtr node; + xmlDocPtr doc; + const char *encoding; + int format; + xmlSaveCtxtPtr ctxt; + xmlBufferPtr buf; + int options = 0; + + if (!PyArg_ParseTuple(args, (char *) "Ozi:serializeNode", &pyobj_node, + &encoding, &format)) + return (NULL); + node = (xmlNodePtr) PyxmlNode_Get(pyobj_node); + + if (node == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + if (node->type == XML_DOCUMENT_NODE) { + doc = (xmlDocPtr) node; + node = NULL; +#ifdef LIBXML_HTML_ENABLED + } else if (node->type == XML_HTML_DOCUMENT_NODE) { + doc = (xmlDocPtr) node; + node = NULL; +#endif + } else { + if (node->type == XML_NAMESPACE_DECL) + doc = NULL; + else + doc = node->doc; + if ((doc == NULL) || (doc->type == XML_DOCUMENT_NODE)) { +#ifdef LIBXML_HTML_ENABLED + } else if (doc->type == XML_HTML_DOCUMENT_NODE) { +#endif /* LIBXML_HTML_ENABLED */ + } else { + Py_INCREF(Py_None); + return (Py_None); + } + } + + + buf = xmlBufferCreate(); + if (buf == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + if (format) options |= XML_SAVE_FORMAT; + ctxt = xmlSaveToBuffer(buf, encoding, options); + if (ctxt == NULL) { + xmlBufferFree(buf); + Py_INCREF(Py_None); + return (Py_None); + } + if (node == NULL) + xmlSaveDoc(ctxt, doc); + else + xmlSaveTree(ctxt, node); + xmlSaveClose(ctxt); + + c_retval = buf->content; + buf->content = NULL; + + xmlBufferFree(buf); + py_retval = libxml_charPtrWrap((char *) c_retval); + + return (py_retval); +} + +static PyObject * +libxml_saveNodeTo(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_file = NULL; + FILE *output; + PyObject *pyobj_node; + xmlNodePtr node; + xmlDocPtr doc; + const char *encoding; + int format; + int len; + xmlOutputBufferPtr buf; + xmlCharEncodingHandlerPtr handler = NULL; + + if (!PyArg_ParseTuple(args, (char *) "OOzi:serializeNode", &pyobj_node, + &py_file, &encoding, &format)) + return (NULL); + node = (xmlNodePtr) PyxmlNode_Get(pyobj_node); + if (node == NULL) { + return (PyLong_FromLong((long) -1)); + } + output = PyFile_Get(py_file); + if (output == NULL) { + return (PyLong_FromLong((long) -1)); + } + + if (node->type == XML_DOCUMENT_NODE) { + doc = (xmlDocPtr) node; + } else if (node->type == XML_HTML_DOCUMENT_NODE) { + doc = (xmlDocPtr) node; + } else { + doc = node->doc; + } +#ifdef LIBXML_HTML_ENABLED + if (doc->type == XML_HTML_DOCUMENT_NODE) { + if (encoding == NULL) + encoding = (const char *) htmlGetMetaEncoding(doc); + } +#endif /* LIBXML_HTML_ENABLED */ + if (encoding != NULL) { + handler = xmlFindCharEncodingHandler(encoding); + if (handler == NULL) { + return (PyLong_FromLong((long) -1)); + } + } + if (doc->type == XML_HTML_DOCUMENT_NODE) { + if (handler == NULL) + handler = xmlFindCharEncodingHandler("HTML"); + if (handler == NULL) + handler = xmlFindCharEncodingHandler("ascii"); + } + + buf = xmlOutputBufferCreateFile(output, handler); + if (node->type == XML_DOCUMENT_NODE) { + len = xmlSaveFormatFileTo(buf, doc, encoding, format); +#ifdef LIBXML_HTML_ENABLED + } else if (node->type == XML_HTML_DOCUMENT_NODE) { + htmlDocContentDumpFormatOutput(buf, doc, encoding, format); + len = xmlOutputBufferClose(buf); + } else if (doc->type == XML_HTML_DOCUMENT_NODE) { + htmlNodeDumpFormatOutput(buf, doc, node, encoding, format); + len = xmlOutputBufferClose(buf); +#endif /* LIBXML_HTML_ENABLED */ + } else { + xmlNodeDumpOutput(buf, doc, node, 0, format, encoding); + len = xmlOutputBufferClose(buf); + } + PyFile_Release(output); + return (PyLong_FromLong((long) len)); +} +#endif /* LIBXML_OUTPUT_ENABLED */ + +/************************************************************************ + * * + * Extra stuff * + * * + ************************************************************************/ +PyObject * +libxml_xmlNewNode(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval; + xmlChar *name; + xmlNodePtr node; + + if (!PyArg_ParseTuple(args, (char *) "s:xmlNewNode", &name)) + return (NULL); + node = (xmlNodePtr) xmlNewNode(NULL, name); +#ifdef DEBUG + printf("NewNode: %s : %p\n", name, (void *) node); +#endif + + if (node == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + py_retval = libxml_xmlNodePtrWrap(node); + return (py_retval); +} + + +/************************************************************************ + * * + * Local Catalog stuff * + * * + ************************************************************************/ +static PyObject * +libxml_addLocalCatalog(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + xmlChar *URL; + xmlParserCtxtPtr ctxt; + PyObject *pyobj_ctxt; + + if (!PyArg_ParseTuple(args, (char *)"Os:addLocalCatalog", &pyobj_ctxt, &URL)) + return(NULL); + + ctxt = (xmlParserCtxtPtr) PyparserCtxt_Get(pyobj_ctxt); + + if (URL != NULL) { + ctxt->catalogs = xmlCatalogAddLocal(ctxt->catalogs, URL); + } + +#ifdef DEBUG + printf("LocalCatalog: %s\n", URL); +#endif + + Py_INCREF(Py_None); + return (Py_None); +} + +#ifdef LIBXML_SCHEMAS_ENABLED + +/************************************************************************ + * * + * RelaxNG error handler registration * + * * + ************************************************************************/ + +typedef struct +{ + PyObject *warn; + PyObject *error; + PyObject *arg; +} xmlRelaxNGValidCtxtPyCtxt; +typedef xmlRelaxNGValidCtxtPyCtxt *xmlRelaxNGValidCtxtPyCtxtPtr; + +static void +libxml_xmlRelaxNGValidityGenericErrorFuncHandler(void *ctx, char *str) +{ + PyObject *list; + PyObject *result; + xmlRelaxNGValidCtxtPyCtxtPtr pyCtxt; + +#ifdef DEBUG_ERROR + printf("libxml_xmlRelaxNGValidityGenericErrorFuncHandler(%p, %s, ...) called\n", ctx, str); +#endif + + pyCtxt = (xmlRelaxNGValidCtxtPyCtxtPtr)ctx; + + list = PyTuple_New(2); + PyTuple_SetItem(list, 0, libxml_charPtrWrap(str)); + PyTuple_SetItem(list, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + result = PyEval_CallObject(pyCtxt->error, list); + if (result == NULL) + { + /* TODO: manage for the exception to be propagated... */ + PyErr_Print(); + } + Py_XDECREF(list); + Py_XDECREF(result); +} + +static void +libxml_xmlRelaxNGValidityGenericWarningFuncHandler(void *ctx, char *str) +{ + PyObject *list; + PyObject *result; + xmlRelaxNGValidCtxtPyCtxtPtr pyCtxt; + +#ifdef DEBUG_ERROR + printf("libxml_xmlRelaxNGValidityGenericWarningFuncHandler(%p, %s, ...) called\n", ctx, str); +#endif + + pyCtxt = (xmlRelaxNGValidCtxtPyCtxtPtr)ctx; + + list = PyTuple_New(2); + PyTuple_SetItem(list, 0, libxml_charPtrWrap(str)); + PyTuple_SetItem(list, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + result = PyEval_CallObject(pyCtxt->warn, list); + if (result == NULL) + { + /* TODO: manage for the exception to be propagated... */ + PyErr_Print(); + } + Py_XDECREF(list); + Py_XDECREF(result); +} + +static void +libxml_xmlRelaxNGValidityErrorFunc(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlRelaxNGValidityGenericErrorFuncHandler(ctx, libxml_buildMessage(msg, ap)); + va_end(ap); +} + +static void +libxml_xmlRelaxNGValidityWarningFunc(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlRelaxNGValidityGenericWarningFuncHandler(ctx, libxml_buildMessage(msg, ap)); + va_end(ap); +} + +static PyObject * +libxml_xmlRelaxNGSetValidErrors(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval; + PyObject *pyobj_error; + PyObject *pyobj_warn; + PyObject *pyobj_ctx; + PyObject *pyobj_arg = Py_None; + xmlRelaxNGValidCtxtPtr ctxt; + xmlRelaxNGValidCtxtPyCtxtPtr pyCtxt; + + if (!PyArg_ParseTuple + (args, (char *) "OOO|O:xmlRelaxNGSetValidErrors", &pyobj_ctx, &pyobj_error, &pyobj_warn, &pyobj_arg)) + return (NULL); + +#ifdef DEBUG_ERROR + printf("libxml_xmlRelaxNGSetValidErrors(%p, %p, %p) called\n", pyobj_ctx, pyobj_error, pyobj_warn); +#endif + + ctxt = PyrelaxNgValidCtxt_Get(pyobj_ctx); + if (xmlRelaxNGGetValidErrors(ctxt, NULL, NULL, (void **) &pyCtxt) == -1) + { + py_retval = libxml_intWrap(-1); + return(py_retval); + } + + if (pyCtxt == NULL) + { + /* first time to set the error handlers */ + pyCtxt = xmlMalloc(sizeof(xmlRelaxNGValidCtxtPyCtxt)); + if (pyCtxt == NULL) { + py_retval = libxml_intWrap(-1); + return(py_retval); + } + memset(pyCtxt, 0, sizeof(xmlRelaxNGValidCtxtPyCtxt)); + } + + /* TODO: check warn and error is a function ! */ + Py_XDECREF(pyCtxt->error); + Py_XINCREF(pyobj_error); + pyCtxt->error = pyobj_error; + + Py_XDECREF(pyCtxt->warn); + Py_XINCREF(pyobj_warn); + pyCtxt->warn = pyobj_warn; + + Py_XDECREF(pyCtxt->arg); + Py_XINCREF(pyobj_arg); + pyCtxt->arg = pyobj_arg; + + xmlRelaxNGSetValidErrors(ctxt, &libxml_xmlRelaxNGValidityErrorFunc, &libxml_xmlRelaxNGValidityWarningFunc, pyCtxt); + + py_retval = libxml_intWrap(1); + return (py_retval); +} + +static PyObject * +libxml_xmlRelaxNGFreeValidCtxt(ATTRIBUTE_UNUSED PyObject *self, PyObject *args) { + xmlRelaxNGValidCtxtPtr ctxt; + xmlRelaxNGValidCtxtPyCtxtPtr pyCtxt; + PyObject *pyobj_ctxt; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlRelaxNGFreeValidCtxt", &pyobj_ctxt)) + return(NULL); + ctxt = (xmlRelaxNGValidCtxtPtr) PyrelaxNgValidCtxt_Get(pyobj_ctxt); + + if (xmlRelaxNGGetValidErrors(ctxt, NULL, NULL, (void **) &pyCtxt) == 0) + { + if (pyCtxt != NULL) + { + Py_XDECREF(pyCtxt->error); + Py_XDECREF(pyCtxt->warn); + Py_XDECREF(pyCtxt->arg); + xmlFree(pyCtxt); + } + } + + xmlRelaxNGFreeValidCtxt(ctxt); + Py_INCREF(Py_None); + return(Py_None); +} + +typedef struct +{ + PyObject *warn; + PyObject *error; + PyObject *arg; +} xmlSchemaValidCtxtPyCtxt; +typedef xmlSchemaValidCtxtPyCtxt *xmlSchemaValidCtxtPyCtxtPtr; + +static void +libxml_xmlSchemaValidityGenericErrorFuncHandler(void *ctx, char *str) +{ + PyObject *list; + PyObject *result; + xmlSchemaValidCtxtPyCtxtPtr pyCtxt; + +#ifdef DEBUG_ERROR + printf("libxml_xmlSchemaValiditiyGenericErrorFuncHandler(%p, %s, ...) called\n", ctx, str); +#endif + + pyCtxt = (xmlSchemaValidCtxtPyCtxtPtr) ctx; + + list = PyTuple_New(2); + PyTuple_SetItem(list, 0, libxml_charPtrWrap(str)); + PyTuple_SetItem(list, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + result = PyEval_CallObject(pyCtxt->error, list); + if (result == NULL) + { + /* TODO: manage for the exception to be propagated... */ + PyErr_Print(); + } + Py_XDECREF(list); + Py_XDECREF(result); +} + +static void +libxml_xmlSchemaValidityGenericWarningFuncHandler(void *ctx, char *str) +{ + PyObject *list; + PyObject *result; + xmlSchemaValidCtxtPyCtxtPtr pyCtxt; + +#ifdef DEBUG_ERROR + printf("libxml_xmlSchemaValidityGenericWarningFuncHandler(%p, %s, ...) called\n", ctx, str); +#endif + + pyCtxt = (xmlSchemaValidCtxtPyCtxtPtr) ctx; + + list = PyTuple_New(2); + PyTuple_SetItem(list, 0, libxml_charPtrWrap(str)); + PyTuple_SetItem(list, 1, pyCtxt->arg); + Py_XINCREF(pyCtxt->arg); + result = PyEval_CallObject(pyCtxt->warn, list); + if (result == NULL) + { + /* TODO: manage for the exception to be propagated... */ + PyErr_Print(); + } + Py_XDECREF(list); + Py_XDECREF(result); +} + +static void +libxml_xmlSchemaValidityErrorFunc(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlSchemaValidityGenericErrorFuncHandler(ctx, libxml_buildMessage(msg, ap)); + va_end(ap); +} + +static void +libxml_xmlSchemaValidityWarningFunc(void *ctx, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + libxml_xmlSchemaValidityGenericWarningFuncHandler(ctx, libxml_buildMessage(msg, ap)); + va_end(ap); +} + +PyObject * +libxml_xmlSchemaSetValidErrors(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + PyObject *py_retval; + PyObject *pyobj_error; + PyObject *pyobj_warn; + PyObject *pyobj_ctx; + PyObject *pyobj_arg = Py_None; + xmlSchemaValidCtxtPtr ctxt; + xmlSchemaValidCtxtPyCtxtPtr pyCtxt; + + if (!PyArg_ParseTuple + (args, (char *) "OOO|O:xmlSchemaSetValidErrors", &pyobj_ctx, &pyobj_error, &pyobj_warn, &pyobj_arg)) + return (NULL); + +#ifdef DEBUG_ERROR + printf("libxml_xmlSchemaSetValidErrors(%p, %p, %p) called\n", pyobj_ctx, pyobj_error, pyobj_warn); +#endif + + ctxt = PySchemaValidCtxt_Get(pyobj_ctx); + if (xmlSchemaGetValidErrors(ctxt, NULL, NULL, (void **) &pyCtxt) == -1) + { + py_retval = libxml_intWrap(-1); + return(py_retval); + } + + if (pyCtxt == NULL) + { + /* first time to set the error handlers */ + pyCtxt = xmlMalloc(sizeof(xmlSchemaValidCtxtPyCtxt)); + if (pyCtxt == NULL) { + py_retval = libxml_intWrap(-1); + return(py_retval); + } + memset(pyCtxt, 0, sizeof(xmlSchemaValidCtxtPyCtxt)); + } + + /* TODO: check warn and error is a function ! */ + Py_XDECREF(pyCtxt->error); + Py_XINCREF(pyobj_error); + pyCtxt->error = pyobj_error; + + Py_XDECREF(pyCtxt->warn); + Py_XINCREF(pyobj_warn); + pyCtxt->warn = pyobj_warn; + + Py_XDECREF(pyCtxt->arg); + Py_XINCREF(pyobj_arg); + pyCtxt->arg = pyobj_arg; + + xmlSchemaSetValidErrors(ctxt, &libxml_xmlSchemaValidityErrorFunc, &libxml_xmlSchemaValidityWarningFunc, pyCtxt); + + py_retval = libxml_intWrap(1); + return(py_retval); +} + +static PyObject * +libxml_xmlSchemaFreeValidCtxt(ATTRIBUTE_UNUSED PyObject * self, PyObject * args) +{ + xmlSchemaValidCtxtPtr ctxt; + xmlSchemaValidCtxtPyCtxtPtr pyCtxt; + PyObject *pyobj_ctxt; + + if (!PyArg_ParseTuple(args, (char *)"O:xmlSchemaFreeValidCtxt", &pyobj_ctxt)) + return(NULL); + ctxt = (xmlSchemaValidCtxtPtr) PySchemaValidCtxt_Get(pyobj_ctxt); + + if (xmlSchemaGetValidErrors(ctxt, NULL, NULL, (void **) &pyCtxt) == 0) + { + if (pyCtxt != NULL) + { + Py_XDECREF(pyCtxt->error); + Py_XDECREF(pyCtxt->warn); + Py_XDECREF(pyCtxt->arg); + xmlFree(pyCtxt); + } + } + + xmlSchemaFreeValidCtxt(ctxt); + Py_INCREF(Py_None); + return(Py_None); +} + +#endif + +#ifdef LIBXML_C14N_ENABLED +#ifdef LIBXML_OUTPUT_ENABLED + +/************************************************************************ + * * + * XML Canonicalization c14n * + * * + ************************************************************************/ + +static int +PyxmlNodeSet_Convert(PyObject *py_nodeset, xmlNodeSetPtr *result) +{ + xmlNodeSetPtr nodeSet; + int is_tuple = 0; + + if (PyTuple_Check(py_nodeset)) + is_tuple = 1; + else if (PyList_Check(py_nodeset)) + is_tuple = 0; + else if (py_nodeset == Py_None) { + *result = NULL; + return 0; + } + else { + PyErr_SetString(PyExc_TypeError, + "must be a tuple or list of nodes."); + return -1; + } + + nodeSet = (xmlNodeSetPtr) xmlMalloc(sizeof(xmlNodeSet)); + if (nodeSet == NULL) { + PyErr_SetString(PyExc_MemoryError, ""); + return -1; + } + + nodeSet->nodeNr = 0; + nodeSet->nodeMax = (is_tuple + ? PyTuple_GET_SIZE(py_nodeset) + : PyList_GET_SIZE(py_nodeset)); + nodeSet->nodeTab + = (xmlNodePtr *) xmlMalloc (nodeSet->nodeMax + * sizeof(xmlNodePtr)); + if (nodeSet->nodeTab == NULL) { + xmlFree(nodeSet); + PyErr_SetString(PyExc_MemoryError, ""); + return -1; + } + memset(nodeSet->nodeTab, 0 , + nodeSet->nodeMax * sizeof(xmlNodePtr)); + + { + int idx; + for (idx=0; idx < nodeSet->nodeMax; ++idx) { + xmlNodePtr pynode = + PyxmlNode_Get (is_tuple + ? PyTuple_GET_ITEM(py_nodeset, idx) + : PyList_GET_ITEM(py_nodeset, idx)); + if (pynode) + nodeSet->nodeTab[nodeSet->nodeNr++] = pynode; + } + } + *result = nodeSet; + return 0; +} + +static int +PystringSet_Convert(PyObject *py_strings, xmlChar *** result) +{ + /* NOTE: the array should be freed, but the strings are shared + with the python strings and so must not be freed. */ + + xmlChar ** strings; + int is_tuple = 0; + int count; + int init_index = 0; + + if (PyTuple_Check(py_strings)) + is_tuple = 1; + else if (PyList_Check(py_strings)) + is_tuple = 0; + else if (py_strings == Py_None) { + *result = NULL; + return 0; + } + else { + PyErr_SetString(PyExc_TypeError, + "must be a tuple or list of strings."); + return -1; + } + + count = (is_tuple + ? PyTuple_GET_SIZE(py_strings) + : PyList_GET_SIZE(py_strings)); + + strings = (xmlChar **) xmlMalloc(sizeof(xmlChar *) * count); + + if (strings == NULL) { + PyErr_SetString(PyExc_MemoryError, ""); + return -1; + } + + memset(strings, 0 , sizeof(xmlChar *) * count); + + { + int idx; + for (idx=0; idx < count; ++idx) { + char* s = PyBytes_AsString + (is_tuple + ? PyTuple_GET_ITEM(py_strings, idx) + : PyList_GET_ITEM(py_strings, idx)); + if (s) + strings[init_index++] = (xmlChar *)s; + else { + xmlFree(strings); + PyErr_SetString(PyExc_TypeError, + "must be a tuple or list of strings."); + return -1; + } + } + } + + *result = strings; + return 0; +} + +static PyObject * +libxml_C14NDocDumpMemory(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *py_retval = NULL; + + PyObject *pyobj_doc; + PyObject *pyobj_nodes; + int exclusive; + PyObject *pyobj_prefixes; + int with_comments; + + xmlDocPtr doc; + xmlNodeSetPtr nodes; + xmlChar **prefixes = NULL; + xmlChar *doc_txt; + + int result; + + if (!PyArg_ParseTuple(args, (char *) "OOiOi:C14NDocDumpMemory", + &pyobj_doc, + &pyobj_nodes, + &exclusive, + &pyobj_prefixes, + &with_comments)) + return (NULL); + + doc = (xmlDocPtr) PyxmlNode_Get(pyobj_doc); + if (!doc) { + PyErr_SetString(PyExc_TypeError, "bad document."); + return NULL; + } + + result = PyxmlNodeSet_Convert(pyobj_nodes, &nodes); + if (result < 0) return NULL; + + if (exclusive) { + result = PystringSet_Convert(pyobj_prefixes, &prefixes); + if (result < 0) { + if (nodes) { + xmlFree(nodes->nodeTab); + xmlFree(nodes); + } + return NULL; + } + } + + result = xmlC14NDocDumpMemory(doc, + nodes, + exclusive, + prefixes, + with_comments, + &doc_txt); + + if (nodes) { + xmlFree(nodes->nodeTab); + xmlFree(nodes); + } + if (prefixes) { + xmlChar ** idx = prefixes; + while (*idx) xmlFree(*(idx++)); + xmlFree(prefixes); + } + + if (result < 0) { + PyErr_SetString(PyExc_Exception, + "libxml2 xmlC14NDocDumpMemory failure."); + return NULL; + } + else { + py_retval = PY_IMPORT_STRING_SIZE((const char *) doc_txt, + result); + xmlFree(doc_txt); + return py_retval; + } +} + +static PyObject * +libxml_C14NDocSaveTo(ATTRIBUTE_UNUSED PyObject * self, + PyObject * args) +{ + PyObject *pyobj_doc; + PyObject *py_file; + PyObject *pyobj_nodes; + int exclusive; + PyObject *pyobj_prefixes; + int with_comments; + + xmlDocPtr doc; + xmlNodeSetPtr nodes; + xmlChar **prefixes = NULL; + FILE * output; + xmlOutputBufferPtr buf; + + int result; + int len; + + if (!PyArg_ParseTuple(args, (char *) "OOiOiO:C14NDocSaveTo", + &pyobj_doc, + &pyobj_nodes, + &exclusive, + &pyobj_prefixes, + &with_comments, + &py_file)) + return (NULL); + + doc = (xmlDocPtr) PyxmlNode_Get(pyobj_doc); + if (!doc) { + PyErr_SetString(PyExc_TypeError, "bad document."); + return NULL; + } + + output = PyFile_Get(py_file); + if (output == NULL) { + PyErr_SetString(PyExc_TypeError, "bad file."); + return NULL; + } + buf = xmlOutputBufferCreateFile(output, NULL); + + result = PyxmlNodeSet_Convert(pyobj_nodes, &nodes); + if (result < 0) return NULL; + + if (exclusive) { + result = PystringSet_Convert(pyobj_prefixes, &prefixes); + if (result < 0) { + if (nodes) { + xmlFree(nodes->nodeTab); + xmlFree(nodes); + } + return NULL; + } + } + + result = xmlC14NDocSaveTo(doc, + nodes, + exclusive, + prefixes, + with_comments, + buf); + + if (nodes) { + xmlFree(nodes->nodeTab); + xmlFree(nodes); + } + if (prefixes) { + xmlChar ** idx = prefixes; + while (*idx) xmlFree(*(idx++)); + xmlFree(prefixes); + } + + PyFile_Release(output); + len = xmlOutputBufferClose(buf); + + if (result < 0) { + PyErr_SetString(PyExc_Exception, + "libxml2 xmlC14NDocSaveTo failure."); + return NULL; + } + else + return PyLong_FromLong((long) len); +} + +#endif +#endif + +static PyObject * +libxml_getObjDesc(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + + PyObject *obj; + char *str; + + if (!PyArg_ParseTuple(args, (char *)"O:getObjDesc", &obj)) + return NULL; + str = PyCapsule_GetPointer(obj, PyCapsule_GetName(obj)); + return Py_BuildValue((char *)"s", str); +} + +static PyObject * +libxml_compareNodesEqual(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + + PyObject *py_node1, *py_node2; + xmlNodePtr node1, node2; + + if (!PyArg_ParseTuple(args, (char *)"OO:compareNodesEqual", + &py_node1, &py_node2)) + return NULL; + /* To compare two node objects, we compare their pointer addresses */ + node1 = PyxmlNode_Get(py_node1); + node2 = PyxmlNode_Get(py_node2); + if ( node1 == node2 ) + return Py_BuildValue((char *)"i", 1); + else + return Py_BuildValue((char *)"i", 0); + +} + +static PyObject * +libxml_nodeHash(PyObject *self ATTRIBUTE_UNUSED, PyObject *args) { + + PyObject *py_node1; + xmlNodePtr node1; + + if (!PyArg_ParseTuple(args, (char *)"O:nodeHash", &py_node1)) + return NULL; + /* For simplicity, we use the node pointer address as a hash value */ + node1 = PyxmlNode_Get(py_node1); + + return PyLong_FromVoidPtr(node1); + +} + +/************************************************************************ + * * + * The registration stuff * + * * + ************************************************************************/ +static PyMethodDef libxmlMethods[] = { +#include "libxml2-export.c" + {(char *) "name", libxml_name, METH_VARARGS, NULL}, + {(char *) "children", libxml_children, METH_VARARGS, NULL}, + {(char *) "properties", libxml_properties, METH_VARARGS, NULL}, + {(char *) "last", libxml_last, METH_VARARGS, NULL}, + {(char *) "prev", libxml_prev, METH_VARARGS, NULL}, + {(char *) "next", libxml_next, METH_VARARGS, NULL}, + {(char *) "parent", libxml_parent, METH_VARARGS, NULL}, + {(char *) "type", libxml_type, METH_VARARGS, NULL}, + {(char *) "doc", libxml_doc, METH_VARARGS, NULL}, + {(char *) "xmlNewNode", libxml_xmlNewNode, METH_VARARGS, NULL}, + {(char *) "xmlNodeRemoveNsDef", libxml_xmlNodeRemoveNsDef, METH_VARARGS, NULL}, + {(char *)"xmlSetValidErrors", libxml_xmlSetValidErrors, METH_VARARGS, NULL}, + {(char *)"xmlFreeValidCtxt", libxml_xmlFreeValidCtxt, METH_VARARGS, NULL}, +#ifdef LIBXML_OUTPUT_ENABLED + {(char *) "serializeNode", libxml_serializeNode, METH_VARARGS, NULL}, + {(char *) "saveNodeTo", libxml_saveNodeTo, METH_VARARGS, NULL}, + {(char *) "outputBufferCreate", libxml_xmlCreateOutputBuffer, METH_VARARGS, NULL}, + {(char *) "outputBufferGetPythonFile", libxml_outputBufferGetPythonFile, METH_VARARGS, NULL}, + {(char *) "xmlOutputBufferClose", libxml_xmlOutputBufferClose, METH_VARARGS, NULL}, + { (char *)"xmlOutputBufferFlush", libxml_xmlOutputBufferFlush, METH_VARARGS, NULL }, + { (char *)"xmlSaveFileTo", libxml_xmlSaveFileTo, METH_VARARGS, NULL }, + { (char *)"xmlSaveFormatFileTo", libxml_xmlSaveFormatFileTo, METH_VARARGS, NULL }, +#endif /* LIBXML_OUTPUT_ENABLED */ + {(char *) "inputBufferCreate", libxml_xmlCreateInputBuffer, METH_VARARGS, NULL}, + {(char *) "setEntityLoader", libxml_xmlSetEntityLoader, METH_VARARGS, NULL}, + {(char *)"xmlRegisterErrorHandler", libxml_xmlRegisterErrorHandler, METH_VARARGS, NULL }, + {(char *)"xmlParserCtxtSetErrorHandler", libxml_xmlParserCtxtSetErrorHandler, METH_VARARGS, NULL }, + {(char *)"xmlParserCtxtGetErrorHandler", libxml_xmlParserCtxtGetErrorHandler, METH_VARARGS, NULL }, + {(char *)"xmlFreeParserCtxt", libxml_xmlFreeParserCtxt, METH_VARARGS, NULL }, +#ifdef LIBXML_READER_ENABLED + {(char *)"xmlTextReaderSetErrorHandler", libxml_xmlTextReaderSetErrorHandler, METH_VARARGS, NULL }, + {(char *)"xmlTextReaderGetErrorHandler", libxml_xmlTextReaderGetErrorHandler, METH_VARARGS, NULL }, + {(char *)"xmlFreeTextReader", libxml_xmlFreeTextReader, METH_VARARGS, NULL }, +#endif + {(char *)"addLocalCatalog", libxml_addLocalCatalog, METH_VARARGS, NULL }, +#ifdef LIBXML_SCHEMAS_ENABLED + {(char *)"xmlRelaxNGSetValidErrors", libxml_xmlRelaxNGSetValidErrors, METH_VARARGS, NULL}, + {(char *)"xmlRelaxNGFreeValidCtxt", libxml_xmlRelaxNGFreeValidCtxt, METH_VARARGS, NULL}, + {(char *)"xmlSchemaSetValidErrors", libxml_xmlSchemaSetValidErrors, METH_VARARGS, NULL}, + {(char *)"xmlSchemaFreeValidCtxt", libxml_xmlSchemaFreeValidCtxt, METH_VARARGS, NULL}, +#endif +#ifdef LIBXML_C14N_ENABLED +#ifdef LIBXML_OUTPUT_ENABLED + {(char *)"xmlC14NDocDumpMemory", libxml_C14NDocDumpMemory, METH_VARARGS, NULL}, + {(char *)"xmlC14NDocSaveTo", libxml_C14NDocSaveTo, METH_VARARGS, NULL}, +#endif +#endif + {(char *) "getObjDesc", libxml_getObjDesc, METH_VARARGS, NULL}, + {(char *) "compareNodesEqual", libxml_compareNodesEqual, METH_VARARGS, NULL}, + {(char *) "nodeHash", libxml_nodeHash, METH_VARARGS, NULL}, + {(char *) "xmlRegisterInputCallback", libxml_xmlRegisterInputCallback, METH_VARARGS, NULL}, + {(char *) "xmlUnregisterInputCallback", libxml_xmlUnregisterInputCallback, METH_VARARGS, NULL}, + {NULL, NULL, 0, NULL} +}; + +#if PY_MAJOR_VERSION >= 3 +#define INITERROR return NULL + +static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "libxml2mod", + NULL, + -1, + libxmlMethods, + NULL, + NULL, + NULL, + NULL +}; + +#else +#define INITERROR return + +#ifdef MERGED_MODULES +extern void initlibxsltmod(void); +#endif + +#endif + +#if PY_MAJOR_VERSION >= 3 +PyObject *PyInit_libxml2mod(void) +#else +void initlibxml2mod(void) +#endif +{ + PyObject *module; + +#if PY_MAJOR_VERSION >= 3 + module = PyModule_Create(&moduledef); +#else + /* intialize the python extension module */ + module = Py_InitModule((char *) "libxml2mod", libxmlMethods); +#endif + if (module == NULL) + INITERROR; + + /* initialize libxml2 */ + xmlInitParser(); + /* TODO this probably need to be revamped for Python3 */ + libxml_xmlErrorInitialize(); + +#if PY_MAJOR_VERSION < 3 +#ifdef MERGED_MODULES + initlibxsltmod(); +#endif +#endif + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} diff --git a/libxml2/python/libxml.py b/libxml2/python/libxml.py new file mode 100644 index 0000000..2466cc9 --- /dev/null +++ b/libxml2/python/libxml.py @@ -0,0 +1,788 @@ +import libxml2mod +import types +import sys + +# The root of all libxml2 errors. +class libxmlError(Exception): pass + +# Type of the wrapper class for the C objects wrappers +def checkWrapper(obj): + try: + n = type(_obj).__name__ + if n != 'PyCObject' and n != 'PyCapsule': + return 1 + except: + return 0 + return 0 + +# +# id() is sometimes negative ... +# +def pos_id(o): + i = id(o) + if (i < 0): + return (sys.maxsize - i) + return i + +# +# Errors raised by the wrappers when some tree handling failed. +# +class treeError(libxmlError): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class parserError(libxmlError): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class uriError(libxmlError): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class xpathError(libxmlError): + def __init__(self, msg): + self.msg = msg + def __str__(self): + return self.msg + +class ioWrapper: + def __init__(self, _obj): + self.__io = _obj + self._o = None + + def io_close(self): + if self.__io == None: + return(-1) + self.__io.close() + self.__io = None + return(0) + + def io_flush(self): + if self.__io == None: + return(-1) + self.__io.flush() + return(0) + + def io_read(self, len = -1): + if self.__io == None: + return(-1) + try: + if len < 0: + ret = self.__io.read() + else: + ret = self.__io.read(len) + except Exception: + import sys + e = sys.exc_info()[1] + print("failed to read from Python:", type(e)) + print("on IO:", self.__io) + self.__io == None + return(-1) + + return(ret) + + def io_write(self, str, len = -1): + if self.__io == None: + return(-1) + if len < 0: + return(self.__io.write(str)) + return(self.__io.write(str, len)) + +class ioReadWrapper(ioWrapper): + def __init__(self, _obj, enc = ""): + ioWrapper.__init__(self, _obj) + self._o = libxml2mod.xmlCreateInputBuffer(self, enc) + + def __del__(self): + print("__del__") + self.io_close() + if self._o != None: + libxml2mod.xmlFreeParserInputBuffer(self._o) + self._o = None + + def close(self): + self.io_close() + if self._o != None: + libxml2mod.xmlFreeParserInputBuffer(self._o) + self._o = None + +class ioWriteWrapper(ioWrapper): + def __init__(self, _obj, enc = ""): +# print "ioWriteWrapper.__init__", _obj + if type(_obj) == type(''): + print("write io from a string") + self.o = None + elif type(_obj).__name__ == 'PyCapsule': + file = libxml2mod.outputBufferGetPythonFile(_obj) + if file != None: + ioWrapper.__init__(self, file) + else: + ioWrapper.__init__(self, _obj) + self._o = _obj +# elif type(_obj) == types.InstanceType: +# print(("write io from instance of %s" % (_obj.__class__))) +# ioWrapper.__init__(self, _obj) +# self._o = libxml2mod.xmlCreateOutputBuffer(self, enc) + else: + file = libxml2mod.outputBufferGetPythonFile(_obj) + if file != None: + ioWrapper.__init__(self, file) + else: + ioWrapper.__init__(self, _obj) + self._o = _obj + + def __del__(self): +# print "__del__" + self.io_close() + if self._o != None: + libxml2mod.xmlOutputBufferClose(self._o) + self._o = None + + def flush(self): + self.io_flush() + if self._o != None: + libxml2mod.xmlOutputBufferClose(self._o) + self._o = None + + def close(self): + self.io_flush() + if self._o != None: + libxml2mod.xmlOutputBufferClose(self._o) + self._o = None + +# +# Example of a class to handle SAX events +# +class SAXCallback: + """Base class for SAX handlers""" + def startDocument(self): + """called at the start of the document""" + pass + + def endDocument(self): + """called at the end of the document""" + pass + + def startElement(self, tag, attrs): + """called at the start of every element, tag is the name of + the element, attrs is a dictionary of the element's attributes""" + pass + + def endElement(self, tag): + """called at the start of every element, tag is the name of + the element""" + pass + + def characters(self, data): + """called when character data have been read, data is the string + containing the data, multiple consecutive characters() callback + are possible.""" + pass + + def cdataBlock(self, data): + """called when CDATA section have been read, data is the string + containing the data, multiple consecutive cdataBlock() callback + are possible.""" + pass + + def reference(self, name): + """called when an entity reference has been found""" + pass + + def ignorableWhitespace(self, data): + """called when potentially ignorable white spaces have been found""" + pass + + def processingInstruction(self, target, data): + """called when a PI has been found, target contains the PI name and + data is the associated data in the PI""" + pass + + def comment(self, content): + """called when a comment has been found, content contains the comment""" + pass + + def externalSubset(self, name, externalID, systemID): + """called when a DOCTYPE declaration has been found, name is the + DTD name and externalID, systemID are the DTD public and system + identifier for that DTd if available""" + pass + + def internalSubset(self, name, externalID, systemID): + """called when a DOCTYPE declaration has been found, name is the + DTD name and externalID, systemID are the DTD public and system + identifier for that DTD if available""" + pass + + def entityDecl(self, name, type, externalID, systemID, content): + """called when an ENTITY declaration has been found, name is the + entity name and externalID, systemID are the entity public and + system identifier for that entity if available, type indicates + the entity type, and content reports it's string content""" + pass + + def notationDecl(self, name, externalID, systemID): + """called when an NOTATION declaration has been found, name is the + notation name and externalID, systemID are the notation public and + system identifier for that notation if available""" + pass + + def attributeDecl(self, elem, name, type, defi, defaultValue, nameList): + """called when an ATTRIBUTE definition has been found""" + pass + + def elementDecl(self, name, type, content): + """called when an ELEMENT definition has been found""" + pass + + def entityDecl(self, name, publicId, systemID, notationName): + """called when an unparsed ENTITY declaration has been found, + name is the entity name and publicId,, systemID are the entity + public and system identifier for that entity if available, + and notationName indicate the associated NOTATION""" + pass + + def warning(self, msg): + #print msg + pass + + def error(self, msg): + raise parserError(msg) + + def fatalError(self, msg): + raise parserError(msg) + +# +# This class is the ancestor of all the Node classes. It provides +# the basic functionalities shared by all nodes (and handle +# gracefylly the exception), like name, navigation in the tree, +# doc reference, content access and serializing to a string or URI +# +class xmlCore: + def __init__(self, _obj=None): + if _obj != None: + self._o = _obj; + return + self._o = None + + def __eq__(self, other): + if other == None: + return False + ret = libxml2mod.compareNodesEqual(self._o, other._o) + if ret == None: + return False + return ret == True + def __ne__(self, other): + if other == None: + return True + ret = libxml2mod.compareNodesEqual(self._o, other._o) + return not ret + def __hash__(self): + ret = libxml2mod.nodeHash(self._o) + return ret + + def __str__(self): + return self.serialize() + def get_parent(self): + ret = libxml2mod.parent(self._o) + if ret == None: + return None + return nodeWrap(ret) + def get_children(self): + ret = libxml2mod.children(self._o) + if ret == None: + return None + return nodeWrap(ret) + def get_last(self): + ret = libxml2mod.last(self._o) + if ret == None: + return None + return nodeWrap(ret) + def get_next(self): + ret = libxml2mod.next(self._o) + if ret == None: + return None + return nodeWrap(ret) + def get_properties(self): + ret = libxml2mod.properties(self._o) + if ret == None: + return None + return xmlAttr(_obj=ret) + def get_prev(self): + ret = libxml2mod.prev(self._o) + if ret == None: + return None + return nodeWrap(ret) + def get_content(self): + return libxml2mod.xmlNodeGetContent(self._o) + getContent = get_content # why is this duplicate naming needed ? + def get_name(self): + return libxml2mod.name(self._o) + def get_type(self): + return libxml2mod.type(self._o) + def get_doc(self): + ret = libxml2mod.doc(self._o) + if ret == None: + if self.type in ["document_xml", "document_html"]: + return xmlDoc(_obj=self._o) + else: + return None + return xmlDoc(_obj=ret) + # + # Those are common attributes to nearly all type of nodes + # defined as python2 properties + # + import sys + if float(sys.version[0:3]) < 2.2: + def __getattr__(self, attr): + if attr == "parent": + ret = libxml2mod.parent(self._o) + if ret == None: + return None + return nodeWrap(ret) + elif attr == "properties": + ret = libxml2mod.properties(self._o) + if ret == None: + return None + return xmlAttr(_obj=ret) + elif attr == "children": + ret = libxml2mod.children(self._o) + if ret == None: + return None + return nodeWrap(ret) + elif attr == "last": + ret = libxml2mod.last(self._o) + if ret == None: + return None + return nodeWrap(ret) + elif attr == "next": + ret = libxml2mod.next(self._o) + if ret == None: + return None + return nodeWrap(ret) + elif attr == "prev": + ret = libxml2mod.prev(self._o) + if ret == None: + return None + return nodeWrap(ret) + elif attr == "content": + return libxml2mod.xmlNodeGetContent(self._o) + elif attr == "name": + return libxml2mod.name(self._o) + elif attr == "type": + return libxml2mod.type(self._o) + elif attr == "doc": + ret = libxml2mod.doc(self._o) + if ret == None: + if self.type == "document_xml" or self.type == "document_html": + return xmlDoc(_obj=self._o) + else: + return None + return xmlDoc(_obj=ret) + raise AttributeError(attr) + else: + parent = property(get_parent, None, None, "Parent node") + children = property(get_children, None, None, "First child node") + last = property(get_last, None, None, "Last sibling node") + next = property(get_next, None, None, "Next sibling node") + prev = property(get_prev, None, None, "Previous sibling node") + properties = property(get_properties, None, None, "List of properies") + content = property(get_content, None, None, "Content of this node") + name = property(get_name, None, None, "Node name") + type = property(get_type, None, None, "Node type") + doc = property(get_doc, None, None, "The document this node belongs to") + + # + # Serialization routines, the optional arguments have the following + # meaning: + # encoding: string to ask saving in a specific encoding + # indent: if 1 the serializer is asked to indent the output + # + def serialize(self, encoding = None, format = 0): + return libxml2mod.serializeNode(self._o, encoding, format) + def saveTo(self, file, encoding = None, format = 0): + return libxml2mod.saveNodeTo(self._o, file, encoding, format) + + # + # Canonicalization routines: + # + # nodes: the node set (tuple or list) to be included in the + # canonized image or None if all document nodes should be + # included. + # exclusive: the exclusive flag (0 - non-exclusive + # canonicalization; otherwise - exclusive canonicalization) + # prefixes: the list of inclusive namespace prefixes (strings), + # or None if there is no inclusive namespaces (only for + # exclusive canonicalization, ignored otherwise) + # with_comments: include comments in the result (!=0) or not + # (==0) + def c14nMemory(self, + nodes=None, + exclusive=0, + prefixes=None, + with_comments=0): + if nodes: + nodes = [n._o for n in nodes] + return libxml2mod.xmlC14NDocDumpMemory( + self.get_doc()._o, + nodes, + exclusive != 0, + prefixes, + with_comments != 0) + def c14nSaveTo(self, + file, + nodes=None, + exclusive=0, + prefixes=None, + with_comments=0): + if nodes: + nodes = [n._o for n in nodes] + return libxml2mod.xmlC14NDocSaveTo( + self.get_doc()._o, + nodes, + exclusive != 0, + prefixes, + with_comments != 0, + file) + + # + # Selecting nodes using XPath, a bit slow because the context + # is allocated/freed every time but convenient. + # + def xpathEval(self, expr): + doc = self.doc + if doc == None: + return None + ctxt = doc.xpathNewContext() + ctxt.setContextNode(self) + res = ctxt.xpathEval(expr) + ctxt.xpathFreeContext() + return res + +# # +# # Selecting nodes using XPath, faster because the context +# # is allocated just once per xmlDoc. +# # +# # Removed: DV memleaks c.f. #126735 +# # +# def xpathEval2(self, expr): +# doc = self.doc +# if doc == None: +# return None +# try: +# doc._ctxt.setContextNode(self) +# except: +# doc._ctxt = doc.xpathNewContext() +# doc._ctxt.setContextNode(self) +# res = doc._ctxt.xpathEval(expr) +# return res + def xpathEval2(self, expr): + return self.xpathEval(expr) + + # Remove namespaces + def removeNsDef(self, href): + """ + Remove a namespace definition from a node. If href is None, + remove all of the ns definitions on that node. The removed + namespaces are returned as a linked list. + + Note: If any child nodes referred to the removed namespaces, + they will be left with dangling links. You should call + renconciliateNs() to fix those pointers. + + Note: This method does not free memory taken by the ns + definitions. You will need to free it manually with the + freeNsList() method on the returns xmlNs object. + """ + + ret = libxml2mod.xmlNodeRemoveNsDef(self._o, href) + if ret is None:return None + __tmp = xmlNs(_obj=ret) + return __tmp + + # support for python2 iterators + def walk_depth_first(self): + return xmlCoreDepthFirstItertor(self) + def walk_breadth_first(self): + return xmlCoreBreadthFirstItertor(self) + __iter__ = walk_depth_first + + def free(self): + try: + self.doc._ctxt.xpathFreeContext() + except: + pass + libxml2mod.xmlFreeDoc(self._o) + + +# +# implements the depth-first iterator for libxml2 DOM tree +# +class xmlCoreDepthFirstItertor: + def __init__(self, node): + self.node = node + self.parents = [] + def __iter__(self): + return self + def __next__(self): + while 1: + if self.node: + ret = self.node + self.parents.append(self.node) + self.node = self.node.children + return ret + try: + parent = self.parents.pop() + except IndexError: + raise StopIteration + self.node = parent.next + next = __next__ + +# +# implements the breadth-first iterator for libxml2 DOM tree +# +class xmlCoreBreadthFirstItertor: + def __init__(self, node): + self.node = node + self.parents = [] + def __iter__(self): + return self + def __next__(self): + while 1: + if self.node: + ret = self.node + self.parents.append(self.node) + self.node = self.node.next + return ret + try: + parent = self.parents.pop() + except IndexError: + raise StopIteration + self.node = parent.children + next = __next__ + +# +# converters to present a nicer view of the XPath returns +# +def nodeWrap(o): + # TODO try to cast to the most appropriate node class + name = libxml2mod.type(o) + if name == "element" or name == "text": + return xmlNode(_obj=o) + if name == "attribute": + return xmlAttr(_obj=o) + if name[0:8] == "document": + return xmlDoc(_obj=o) + if name == "namespace": + return xmlNs(_obj=o) + if name == "elem_decl": + return xmlElement(_obj=o) + if name == "attribute_decl": + return xmlAttribute(_obj=o) + if name == "entity_decl": + return xmlEntity(_obj=o) + if name == "dtd": + return xmlDtd(_obj=o) + return xmlNode(_obj=o) + +def xpathObjectRet(o): + otype = type(o) + if otype == type([]): + ret = list(map(xpathObjectRet, o)) + return ret + elif otype == type(()): + ret = list(map(xpathObjectRet, o)) + return tuple(ret) + elif otype == type('') or otype == type(0) or otype == type(0.0): + return o + else: + return nodeWrap(o) + +# +# register an XPath function +# +def registerXPathFunction(ctxt, name, ns_uri, f): + ret = libxml2mod.xmlRegisterXPathFunction(ctxt, name, ns_uri, f) + +# +# For the xmlTextReader parser configuration +# +PARSER_LOADDTD=1 +PARSER_DEFAULTATTRS=2 +PARSER_VALIDATE=3 +PARSER_SUBST_ENTITIES=4 + +# +# For the error callback severities +# +PARSER_SEVERITY_VALIDITY_WARNING=1 +PARSER_SEVERITY_VALIDITY_ERROR=2 +PARSER_SEVERITY_WARNING=3 +PARSER_SEVERITY_ERROR=4 + +# +# register the libxml2 error handler +# +def registerErrorHandler(f, ctx): + """Register a Python written function to for error reporting. + The function is called back as f(ctx, error). """ + import sys + if 'libxslt' not in sys.modules: + # normal behaviour when libxslt is not imported + ret = libxml2mod.xmlRegisterErrorHandler(f,ctx) + else: + # when libxslt is already imported, one must + # use libxst's error handler instead + import libxslt + ret = libxslt.registerErrorHandler(f,ctx) + return ret + +class parserCtxtCore: + + def __init__(self, _obj=None): + if _obj != None: + self._o = _obj; + return + self._o = None + + def __del__(self): + if self._o != None: + libxml2mod.xmlFreeParserCtxt(self._o) + self._o = None + + 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 getErrorHandler(self): + """Return (f,arg) as previously registered with setErrorHandler + or (None,None).""" + return libxml2mod.xmlParserCtxtGetErrorHandler(self._o) + + def addLocalCatalog(self, uri): + """Register a local catalog with the parser""" + return libxml2mod.addLocalCatalog(self._o, uri) + + +class ValidCtxtCore: + + def __init__(self, *args, **kw): + pass + + def setValidityErrorHandler(self, err_func, warn_func, arg=None): + """ + Register error and warning handlers for DTD validation. + These will be called back as f(msg,arg) + """ + libxml2mod.xmlSetValidErrors(self._o, err_func, warn_func, arg) + + +class SchemaValidCtxtCore: + + def __init__(self, *args, **kw): + pass + + def setValidityErrorHandler(self, err_func, warn_func, arg=None): + """ + Register error and warning handlers for Schema validation. + These will be called back as f(msg,arg) + """ + libxml2mod.xmlSchemaSetValidErrors(self._o, err_func, warn_func, arg) + + +class relaxNgValidCtxtCore: + + def __init__(self, *args, **kw): + pass + + def setValidityErrorHandler(self, err_func, warn_func, arg=None): + """ + Register error and warning handlers for RelaxNG validation. + These will be called back as f(msg,arg) + """ + libxml2mod.xmlRelaxNGSetValidErrors(self._o, err_func, warn_func, arg) + + +def _xmlTextReaderErrorFunc(xxx_todo_changeme,msg,severity,locator): + """Intermediate callback to wrap the locator""" + (f,arg) = xxx_todo_changeme + return f(arg,msg,severity,xmlTextReaderLocator(locator)) + +class xmlTextReaderCore: + + def __init__(self, _obj=None): + self.input = None + if _obj != None:self._o = _obj;return + self._o = None + + def __del__(self): + if self._o != None: + libxml2mod.xmlFreeTextReader(self._o) + self._o = None + + def SetErrorHandler(self,f,arg): + """Register an error handler that will be called back as + 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): + """Return (f,arg) as previously registered with setErrorHandler + or (None,None).""" + f,arg = libxml2mod.xmlTextReaderGetErrorHandler(self._o) + if f is None: + return None,None + else: + # assert f is _xmlTextReaderErrorFunc + return arg + +# +# The cleanup now goes though a wrapper in libxml.c +# +def cleanupParser(): + libxml2mod.xmlPythonCleanupParser() + +# +# The interface to xmlRegisterInputCallbacks. +# Since this API does not allow to pass a data object along with +# match/open callbacks, it is necessary to maintain a list of all +# Python callbacks. +# +__input_callbacks = [] +def registerInputCallback(func): + def findOpenCallback(URI): + for cb in reversed(__input_callbacks): + o = cb(URI) + if o is not None: + return o + libxml2mod.xmlRegisterInputCallback(findOpenCallback) + __input_callbacks.append(func) + +def popInputCallbacks(): + # First pop python-level callbacks, when no more available - start + # popping built-in ones. + if len(__input_callbacks) > 0: + __input_callbacks.pop() + if len(__input_callbacks) == 0: + libxml2mod.xmlUnregisterInputCallback() + +# WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING +# +# Everything before this line comes from libxml.py +# Everything after this line is automatically generated +# +# WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING + diff --git a/libxml2/python/libxml2-python-api.xml b/libxml2/python/libxml2-python-api.xml new file mode 100644 index 0000000..032b44b --- /dev/null +++ b/libxml2/python/libxml2-python-api.xml @@ -0,0 +1,350 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<api name='libxml2-python'> + <symbols> + <function name='xmlRegisterXPathFunction' file='python'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Register a Python written function to the XPath interpreter</info> + <return type='int' info="1 in case of success, 0 or -1 in case of error"/> + <arg name='ctx' type='xmlXPathContextPtr' info='the xpathContext'/> + <arg name='name' type='xmlChar *' info='the function name'/> + <arg name='ns_uri' type='xmlChar *' info='the namespace or NULL'/> + <arg name='f' type='pythonObject' info='the python function'/> + </function> + <function name='xmlXPathRegisterVariable' file='python'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Register a variable with the XPath context</info> + <return type='int' info="1 in case of success, 0 or -1 in case of error"/> + <arg name='ctx' type='xmlXPathContextPtr' info='the xpathContext'/> + <arg name='name' type='xmlChar *' info='the variable name'/> + <arg name='ns_uri' type='xmlChar *' info='the namespace or NULL'/> + <arg name='value' type='pythonObject' info='the value'/> + </function> + <function name='xmlNewNode' file='python'> + <info>Create a new Node</info> + <return type='xmlNodePtr' info="A new element node"/> + <arg name='name' type='xmlChar *' info='the node name'/> + </function> + <function name='xmlCreatePushParser' file='python'> + <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'> + <cond>defined(LIBXML_HTML_ENABLED)</cond> + <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'/> + <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='xmlSAXParseFile' file='python'> + <info>Interface to parse an XML file or resource pointed by an URI to build an event flow to the SAX object</info> + <return type='void'/> + <arg name='SAX' type='pythonObject' info='the SAX callback object or None'/> + <arg name='URI' type='xmlChar *' info='The URI of the resource'/> + <arg name='recover' type='int' info='allow recovery in case of error'/> + </function> + <function name='htmlSAXParseFile' file='python'> + <cond>defined(LIBXML_HTML_ENABLED)</cond> + <info>Interface to parse an HTML file or resource pointed by an URI to build an event flow to the SAX object</info> + <return type='void'/> + <arg name='SAX' type='pythonObject' info='the SAX callback object or None'/> + <arg name='URI' type='xmlChar *' info='The URI of the resource'/> + <arg name='encoding' type='const char *' info='encoding or None'/> + </function> + <function name='xmlCreateOutputBuffer' file='python'> + <info>Create a libxml2 output buffer from a Python file</info> + <return type='xmlOutputBufferPtr' info="the output buffer"/> + <arg name='file' type='pythonObject' info='the Python file'/> + <arg name='encoding' type='xmlChar *' info='an optionnal encoding'/> + </function> + <function name='xmlCreateInputBuffer' file='python'> + <info>Create a libxml2 input buffer from a Python file</info> + <return type='xmlParserInputBufferPtr' info="the input buffer"/> + <arg name='file' type='pythonObject' info='the Python file'/> + <arg name='encoding' type='xmlChar *' info='an optionnal encoding'/> + </function> + <function name='xmlSetEntityLoader' file='python'> + <info>Set the entity resolver as a python function</info> + <return type='int' info="0 in case of success, -1 for error"/> + <arg name='resolver' type='pythonObject' info='the Python function'/> + </function> + <!-- xmlParserCtxtPtr accessors --> + <function name='xmlParserGetDoc' file='python_accessor'> + <info>Get the document tree from a parser context.</info> + <return type='xmlDocPtr' info="the document tree" field="myDoc"/> + <arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/> + </function> + <function name='xmlParserGetWellFormed' file='python_accessor'> + <info>Get the well formed information from a parser context.</info> + <return type='int' info="the wellFormed field" field="wellFormed"/> + <arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/> + </function> + <function name='xmlParserGetIsValid' file='python_accessor'> + <info>Get the validity information from a parser context.</info> + <return type='int' info="the valid field" field="valid"/> + <arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/> + </function> + <function name='xmlParserSetValidate' file='python_accessor'> + <info>Switch the parser to validation mode.</info> + <return type='void'/> + <arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/> + <arg name='validate' type='int' info='1 to activate validation'/> + </function> + <function name='xmlParserSetReplaceEntities' file='python_accessor'> + <info>Switch the parser to replace entities.</info> + <return type='void'/> + <arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/> + <arg name='replaceEntities' type='int' info='1 to replace entities'/> + </function> + <function name='xmlParserSetPedantic' file='python_accessor'> + <info>Switch the parser to be pedantic.</info> + <return type='void'/> + <arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/> + <arg name='pedantic' type='int' info='1 to run in pedantic mode'/> + </function> + <function name='xmlParserSetLoadSubset' file='python_accessor'> + <info>Switch the parser to load the DTD without validating.</info> + <return type='void'/> + <arg name='ctxt' type='xmlParserCtxtPtr' info='the parser context'/> + <arg name='loadsubset' type='int' info='1 to load the DTD'/> + </function> + <function name='xmlParserSetLineNumbers' file='python_accessor'> + <info>Switch on the generation of line number for elements nodes.</info> + <return type='void'/> + <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> + <!-- xmlNsPtr accessors --> + <function name='xmlNodeGetNs' file='python_accessor'> + <info>Get the namespace of a node</info> + <return type='xmlNsPtr' info="The namespace or None"/> + <arg name='node' type='xmlNodePtr' info='the node'/> + </function> + <function name='xmlNodeGetNsDefs' file='python_accessor'> + <info>Get the namespace of a node</info> + <return type='xmlNsPtr' info="The namespace or None"/> + <arg name='node' type='xmlNodePtr' info='the node'/> + </function> + <!-- xmlXPathContextPtr accessors --> + <function name='xmlXPathParserGetContext' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Get the xpathContext from an xpathParserContext</info> + <return type='xmlXPathContextPtr' info="The XPath context" field="context"/> + <arg name='ctxt' type='xmlXPathParserContextPtr' info='the XPath parser context'/> + </function> + <function name='xmlXPathGetContextDoc' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Get the doc from an xpathContext</info> + <return type='xmlDocPtr' info="The doc context" field="doc"/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + </function> + <function name='xmlXPathGetContextNode' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Get the current node from an xpathContext</info> + <return type='xmlNodePtr' info="The node context" field="node"/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + </function> + <function name='xmlXPathSetContextDoc' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Set the doc of an xpathContext</info> + <return type='void'/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + <arg name="doc" type='xmlDocPtr' info="The doc context"/> + </function> + <function name='xmlXPathSetContextNode' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Set the current node of an xpathContext</info> + <return type='void'/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + <arg name="node" type='xmlNodePtr' info="The node context"/> + </function> + <function name='xmlXPathGetContextPosition' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Get the current node from an xpathContext</info> + <return type='int' info="The node context" field="proximityPosition"/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + </function> + <function name='xmlXPathGetContextSize' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Get the current node from an xpathContext</info> + <return type='int' info="The node context" field="contextSize"/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + </function> + <function name='xmlXPathGetFunction' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Get the current function name xpathContext</info> + <return type='const xmlChar *' info="The function name" field="function"/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + </function> + <function name='xmlXPathGetFunctionURI' file='python_accessor'> + <cond>defined(LIBXML_XPATH_ENABLED)</cond> + <info>Get the current function name URI xpathContext</info> + <return type='const xmlChar *' info="The function name URI" field="functionURI"/> + <arg name='ctxt' type='xmlXPathContextPtr' info='the XPath context'/> + </function> + <!-- xmlURIPtr accessors --> + <function name='xmlURIGetScheme' file='python_accessor'> + <info>Get the scheme part from an URI</info> + <return type='const char *' info="The URI scheme" field="scheme"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetScheme' file='python_accessor'> + <info>Set the scheme part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='scheme' type='char *' info='The URI scheme part'/> + </function> + <function name='xmlURIGetOpaque' file='python_accessor'> + <info>Get the opaque part from an URI</info> + <return type='const char *' info="The URI opaque" field="opaque"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetOpaque' file='python_accessor'> + <info>Set the opaque part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='opaque' type='char *' info='The URI opaque part'/> + </function> + <function name='xmlURIGetAuthority' file='python_accessor'> + <info>Get the authority part from an URI</info> + <return type='const char *' info="The URI authority" field="authority"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetAuthority' file='python_accessor'> + <info>Set the authority part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='authority' type='char *' info='The URI authority part'/> + </function> + <function name='xmlURIGetServer' file='python_accessor'> + <info>Get the server part from an URI</info> + <return type='const char *' info="The URI server" field="server"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetServer' file='python_accessor'> + <info>Set the server part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='server' type='char *' info='The URI server part'/> + </function> + <function name='xmlURIGetUser' file='python_accessor'> + <info>Get the user part from an URI</info> + <return type='const char *' info="The URI user" field="user"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetUser' file='python_accessor'> + <info>Set the user part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='user' type='char *' info='The URI user part'/> + </function> + <function name='xmlURIGetPath' file='python_accessor'> + <info>Get the path part from an URI</info> + <return type='const char *' info="The URI path" field="path"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetPath' file='python_accessor'> + <info>Set the path part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='path' type='char *' info='The URI path part'/> + </function> + <function name='xmlURIGetQuery' file='python_accessor'> + <info>Get the query part from an URI</info> + <return type='const char *' info="The URI query" field="query"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetQuery' file='python_accessor'> + <info>Set the query part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='query' type='char *' info='The URI query part'/> + </function> + <function name='xmlURIGetQueryRaw' file='python_accessor'> + <info>Get the raw query part from an URI (i.e. the unescaped form).</info> + <return type='const char *' info="The URI query" field="query_raw"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetQueryRaw' file='python_accessor'> + <info>Set the raw query part of an URI (i.e. the unescaped form).</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='query_raw' type='char *' info='The raw URI query part'/> + </function> + <function name='xmlURIGetFragment' file='python_accessor'> + <info>Get the fragment part from an URI</info> + <return type='const char *' info="The URI fragment" field="fragment"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetFragment' file='python_accessor'> + <info>Set the fragment part of an URI.</info> + <return type='void'/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + <arg name='fragment' type='char *' info='The URI fragment part'/> + </function> + <function name='xmlURIGetPort' file='python_accessor'> + <info>Get the port part from an URI</info> + <return type='int' info="The URI port" field="port"/> + <arg name='URI' type='xmlURIPtr' info='the URI'/> + </function> + <function name='xmlURISetPort' file='python_accessor'> + <info>Set the port part of an URI.</info> + <return type='void'/> + <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> + <function name='xmlPythonCleanupParser' file='python'> + <info>Cleanup function for the XML library. It tries to reclaim all parsing related global memory allocated for the library processing. It doesn't deallocate any document related memory. Calling this function should not prevent reusing the library but one should call xmlCleanupParser() only when the process has finished using the library or XML document built with it.</info> + <return type='void'/> + </function> + <function name='xmlMemoryUsed' file='python'> + <info>Returns the total amount of memory allocated by libxml2</info> + <return type='int' info='number of bytes allocated'/> + </function> + </symbols> +</api> diff --git a/libxml2/python/libxml_wrap.h b/libxml2/python/libxml_wrap.h new file mode 100644 index 0000000..53a0618 --- /dev/null +++ b/libxml2/python/libxml_wrap.h @@ -0,0 +1,279 @@ +#include <Python.h> +#include <libxml/tree.h> +#include <libxml/parser.h> +#include <libxml/parserInternals.h> +#include <libxml/catalog.h> +#include <libxml/threads.h> +#include <libxml/nanoftp.h> +#include <libxml/nanohttp.h> +#include <libxml/uri.h> +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> +#include <libxml/debugXML.h> +#include <libxml/HTMLparser.h> +#include <libxml/HTMLtree.h> +#include <libxml/xinclude.h> +#include <libxml/xpointer.h> +#include <libxml/xmlunicode.h> +#include <libxml/xmlregexp.h> +#include <libxml/xmlautomata.h> +#include <libxml/xmlreader.h> +#ifdef LIBXML_SCHEMAS_ENABLED +#include <libxml/relaxng.h> +#include <libxml/xmlschemas.h> +#endif + +/* + * for older versions of Python, we don't use PyBytes, but keep PyString + * and don't use Capsule but CObjects + */ +#if PY_VERSION_HEX < 0x02070000 +#ifndef PyBytes_Check +#define PyBytes_Check PyString_Check +#define PyBytes_Size PyString_Size +#define PyBytes_AsString PyString_AsString +#define PyBytes_AS_STRING PyString_AS_STRING +#define PyBytes_GET_SIZE PyString_GET_SIZE +#endif +#ifndef PyCapsule_New +#define PyCapsule_New PyCObject_FromVoidPtrAndDesc +#define PyCapsule_CheckExact PyCObject_Check +#define PyCapsule_GetPointer(o, n) PyCObject_GetDesc((o)) +#endif +#endif + +/** + * ATTRIBUTE_UNUSED: + * + * Macro used to signal to GCC unused function parameters + * Repeated here since the definition is not available when + * compiled outside the libxml2 build tree. + */ +#ifdef __GNUC__ +#ifdef ATTRIBUTE_UNUSED +#undef ATTRIBUTE_UNUSED +#endif +#ifndef ATTRIBUTE_UNUSED +#define ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +#endif /* ATTRIBUTE_UNUSED */ +#else +#define ATTRIBUTE_UNUSED +#endif + +#define PyxmlNode_Get(v) (((v) == Py_None) ? NULL : \ + (((PyxmlNode_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlNodePtr obj; +} PyxmlNode_Object; + +#define PyxmlXPathContext_Get(v) (((v) == Py_None) ? NULL : \ + (((PyxmlXPathContext_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlXPathContextPtr obj; +} PyxmlXPathContext_Object; + +#define PyxmlXPathParserContext_Get(v) (((v) == Py_None) ? NULL : \ + (((PyxmlXPathParserContext_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlXPathParserContextPtr obj; +} PyxmlXPathParserContext_Object; + +#define PyparserCtxt_Get(v) (((v) == Py_None) ? NULL : \ + (((PyparserCtxt_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlParserCtxtPtr obj; +} PyparserCtxt_Object; + +#define PyValidCtxt_Get(v) (((v) == Py_None) ? NULL : \ + (((PyValidCtxt_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlValidCtxtPtr obj; +} PyValidCtxt_Object; + +#define Pycatalog_Get(v) (((v) == Py_None) ? NULL : \ + (((Pycatalog_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlCatalogPtr obj; +} Pycatalog_Object; + +#ifdef LIBXML_REGEXP_ENABLED +#define PyxmlReg_Get(v) (((v) == Py_None) ? NULL : \ + (((PyxmlReg_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlRegexpPtr obj; +} PyxmlReg_Object; +#endif /* LIBXML_REGEXP_ENABLED */ + +#ifdef LIBXML_READER_ENABLED +#define PyxmlTextReader_Get(v) (((v) == Py_None) ? NULL : \ + (((PyxmlTextReader_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlTextReaderPtr obj; +} PyxmlTextReader_Object; + +#define PyxmlTextReaderLocator_Get(v) (((v) == Py_None) ? NULL : \ + (((PyxmlTextReaderLocator_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlTextReaderLocatorPtr obj; +} PyxmlTextReaderLocator_Object; +#endif + +#define PyURI_Get(v) (((v) == Py_None) ? NULL : \ + (((PyURI_Object *)(v))->obj)) + +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; + +#define PyoutputBuffer_Get(v) (((v) == Py_None) ? NULL : \ + (((PyoutputBuffer_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlParserInputBufferPtr obj; +} PyinputBuffer_Object; + +#define PyinputBuffer_Get(v) (((v) == Py_None) ? NULL : \ + (((PyinputBuffer_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlURIPtr obj; +} PyURI_Object; + +/* FILE * have their own internal representation */ +#if PY_MAJOR_VERSION >= 3 +FILE *libxml_PyFileGet(PyObject *f); +void libxml_PyFileRelease(FILE *f); +#define PyFile_Get(v) (((v) == Py_None) ? NULL : libxml_PyFileGet(v)) +#define PyFile_Release(f) libxml_PyFileRelease(f) +#else +#define PyFile_Get(v) (((v) == Py_None) ? NULL : \ + (PyFile_Check(v) ? (PyFile_AsFile(v)) : stdout)) +#define PyFile_Release(f) +#endif + +#ifdef LIBXML_SCHEMAS_ENABLED +typedef struct { + PyObject_HEAD + xmlRelaxNGPtr obj; +} PyrelaxNgSchema_Object; + +#define PyrelaxNgSchema_Get(v) (((v) == Py_None) ? NULL : \ + (((PyrelaxNgSchema_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlRelaxNGParserCtxtPtr obj; +} PyrelaxNgParserCtxt_Object; + +#define PyrelaxNgParserCtxt_Get(v) (((v) == Py_None) ? NULL : \ + (((PyrelaxNgParserCtxt_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlRelaxNGValidCtxtPtr obj; +} PyrelaxNgValidCtxt_Object; + +#define PyrelaxNgValidCtxt_Get(v) (((v) == Py_None) ? NULL : \ + (((PyrelaxNgValidCtxt_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlSchemaPtr obj; +} PySchema_Object; + +#define PySchema_Get(v) (((v) == Py_None) ? NULL : \ + (((PySchema_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlSchemaParserCtxtPtr obj; +} PySchemaParserCtxt_Object; + +#define PySchemaParserCtxt_Get(v) (((v) == Py_None) ? NULL : \ + (((PySchemaParserCtxt_Object *)(v))->obj)) + +typedef struct { + PyObject_HEAD + xmlSchemaValidCtxtPtr obj; +} PySchemaValidCtxt_Object; + +#define PySchemaValidCtxt_Get(v) (((v) == Py_None) ? NULL : \ + (((PySchemaValidCtxt_Object *)(v))->obj)) + +#endif /* LIBXML_SCHEMAS_ENABLED */ + +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); +PyObject * libxml_constcharPtrWrap(const char *str); +PyObject * libxml_charPtrConstWrap(const char *str); +PyObject * libxml_xmlCharPtrConstWrap(const xmlChar *str); +PyObject * libxml_xmlDocPtrWrap(xmlDocPtr doc); +PyObject * libxml_xmlNodePtrWrap(xmlNodePtr node); +PyObject * libxml_xmlAttrPtrWrap(xmlAttrPtr attr); +PyObject * libxml_xmlNsPtrWrap(xmlNsPtr ns); +PyObject * libxml_xmlAttributePtrWrap(xmlAttributePtr ns); +PyObject * libxml_xmlElementPtrWrap(xmlElementPtr ns); +PyObject * libxml_doubleWrap(double val); +PyObject * libxml_xmlXPathContextPtrWrap(xmlXPathContextPtr ctxt); +PyObject * libxml_xmlParserCtxtPtrWrap(xmlParserCtxtPtr ctxt); +PyObject * libxml_xmlXPathParserContextPtrWrap(xmlXPathParserContextPtr ctxt); +PyObject * libxml_xmlXPathObjectPtrWrap(xmlXPathObjectPtr obj); +PyObject * libxml_xmlValidCtxtPtrWrap(xmlValidCtxtPtr valid); +PyObject * libxml_xmlCatalogPtrWrap(xmlCatalogPtr obj); +PyObject * libxml_xmlURIPtrWrap(xmlURIPtr uri); +PyObject * libxml_xmlOutputBufferPtrWrap(xmlOutputBufferPtr buffer); +PyObject * libxml_xmlParserInputBufferPtrWrap(xmlParserInputBufferPtr buffer); +#ifdef LIBXML_REGEXP_ENABLED +PyObject * libxml_xmlRegexpPtrWrap(xmlRegexpPtr regexp); +#endif /* LIBXML_REGEXP_ENABLED */ +#ifdef LIBXML_READER_ENABLED +PyObject * libxml_xmlTextReaderPtrWrap(xmlTextReaderPtr reader); +PyObject * libxml_xmlTextReaderLocatorPtrWrap(xmlTextReaderLocatorPtr locator); +#endif + +xmlXPathObjectPtr libxml_xmlXPathObjectPtrConvert(PyObject * obj); +#ifdef LIBXML_SCHEMAS_ENABLED +PyObject * libxml_xmlRelaxNGPtrWrap(xmlRelaxNGPtr ctxt); +PyObject * libxml_xmlRelaxNGParserCtxtPtrWrap(xmlRelaxNGParserCtxtPtr ctxt); +PyObject * libxml_xmlRelaxNGValidCtxtPtrWrap(xmlRelaxNGValidCtxtPtr valid); +PyObject * libxml_xmlSchemaPtrWrap(xmlSchemaPtr ctxt); +PyObject * libxml_xmlSchemaParserCtxtPtrWrap(xmlSchemaParserCtxtPtr ctxt); +PyObject * libxml_xmlSchemaValidCtxtPtrWrap(xmlSchemaValidCtxtPtr valid); +#endif /* LIBXML_SCHEMAS_ENABLED */ +PyObject * libxml_xmlErrorPtrWrap(xmlErrorPtr error); +PyObject * libxml_xmlSchemaSetValidErrors(PyObject * self, PyObject * args); +PyObject * libxml_xmlRegisterInputCallback(PyObject *self, PyObject *args); +PyObject * libxml_xmlUnregisterInputCallback(PyObject *self, PyObject *args); +PyObject * libxml_xmlNodeRemoveNsDef(PyObject * self, PyObject * args); diff --git a/libxml2/python/setup.py b/libxml2/python/setup.py new file mode 100755 index 0000000..c44269a --- /dev/null +++ b/libxml2/python/setup.py @@ -0,0 +1,242 @@ +#!/usr/bin/python -u +# +# Setup script for libxml2 and libxslt if found +# +import sys, os +from distutils.core import setup, Extension + +# Below ROOT, we expect to find include, include/libxml2, lib and bin. +# On *nix, it is not needed (but should not harm), +# on Windows, it is set by configure.js. +ROOT = r'/usr' + +# Thread-enabled libxml2 +with_threads = 1 + +# If this flag is set (windows only), +# a private copy of the dlls are included in the package. +# If this flag is not set, the libxml2 and libxslt +# dlls must be found somewhere in the PATH at runtime. +WITHDLLS = 1 and sys.platform.startswith('win') + +def missing(file): + if os.access(file, os.R_OK) == 0: + return 1 + return 0 + +try: + HOME = os.environ['HOME'] +except: + HOME="C:" + +if WITHDLLS: + # libxml dlls (expected in ROOT/bin) + dlls = [ 'iconv.dll','libxml2.dll','libxslt.dll','libexslt.dll' ] + dlls = [os.path.join(ROOT,'bin',dll) for dll in dlls] + + # create __init__.py for the libxmlmods package + if not os.path.exists("libxmlmods"): + os.mkdir("libxmlmods") + open("libxmlmods/__init__.py","w").close() + + def altImport(s): + s = s.replace("import libxml2mod","from libxmlmods import libxml2mod") + s = s.replace("import libxsltmod","from libxmlmods import libxsltmod") + return s + +if sys.platform.startswith('win'): + libraryPrefix = 'lib' + platformLibs = [] +else: + libraryPrefix = '' + platformLibs = ["m","z"] + +# those are examined to find +# - libxml2/libxml/tree.h +# - iconv.h +# - libxslt/xsltconfig.h +includes_dir = [ +"/usr/include", +"/usr/local/include", +"/opt/include", +os.path.join(ROOT,'include'), +HOME +]; + +xml_includes="" +for dir in includes_dir: + if not missing(dir + "/libxml2/libxml/tree.h"): + xml_includes=dir + "/libxml2" + break; + +if xml_includes == "": + print("failed to find headers for libxml2: update includes_dir") + sys.exit(1) + +iconv_includes="" +for dir in includes_dir: + if not missing(dir + "/iconv.h"): + iconv_includes=dir + break; + +if iconv_includes == "": + print("failed to find headers for libiconv: update includes_dir") + sys.exit(1) + +# those are added in the linker search path for libraries +libdirs = [ +os.path.join(ROOT,'lib'), +] + +xml_files = ["libxml2-api.xml", "libxml2-python-api.xml", + "libxml.c", "libxml.py", "libxml_wrap.h", "types.c", + "xmlgenerator.py", "README", "TODO", "drv_libxml2.py"] + +xslt_files = ["libxslt-api.xml", "libxslt-python-api.xml", + "libxslt.c", "libxsl.py", "libxslt_wrap.h", + "xsltgenerator.py"] + +if missing("libxml2-py.c") or missing("libxml2.py"): + try: + try: + import xmlgenerator + except: + import generator + except: + print("failed to find and generate stubs for libxml2, aborting ...") + print(sys.exc_info()[0], sys.exc_info()[1]) + sys.exit(1) + + head = open("libxml.py", "r") + generated = open("libxml2class.py", "r") + result = open("libxml2.py", "w") + for line in head.readlines(): + if WITHDLLS: + result.write(altImport(line)) + else: + result.write(line) + for line in generated.readlines(): + result.write(line) + head.close() + generated.close() + result.close() + +with_xslt=0 +if missing("libxslt-py.c") or missing("libxslt.py"): + if missing("xsltgenerator.py") or missing("libxslt-api.xml"): + print("libxslt stub generator not found, libxslt not built") + else: + try: + import xsltgenerator + except: + print("failed to generate stubs for libxslt, aborting ...") + print(sys.exc_info()[0], sys.exc_info()[1]) + else: + head = open("libxsl.py", "r") + generated = open("libxsltclass.py", "r") + result = open("libxslt.py", "w") + for line in head.readlines(): + if WITHDLLS: + result.write(altImport(line)) + else: + result.write(line) + for line in generated.readlines(): + result.write(line) + head.close() + generated.close() + result.close() + with_xslt=1 +else: + with_xslt=1 + +if with_xslt == 1: + xslt_includes="" + for dir in includes_dir: + if not missing(dir + "/libxslt/xsltconfig.h"): + xslt_includes=dir + "/libxslt" + break; + + if xslt_includes == "": + print("failed to find headers for libxslt: update includes_dir") + with_xslt = 0 + + +descr = "libxml2 package" +modules = [ 'libxml2', 'drv_libxml2' ] +if WITHDLLS: + modules.append('libxmlmods.__init__') +c_files = ['libxml2-py.c', 'libxml.c', 'types.c' ] +includes= [xml_includes, iconv_includes] +libs = [libraryPrefix + "xml2"] + platformLibs +macros = [] +if with_threads: + macros.append(('_REENTRANT','1')) +if with_xslt == 1: + descr = "libxml2 and libxslt package" + if not sys.platform.startswith('win'): + # + # We are gonna build 2 identical shared libs with merge initializing + # both libxml2mod and libxsltmod + # + c_files = c_files + ['libxslt-py.c', 'libxslt.c'] + xslt_c_files = c_files + macros.append(('MERGED_MODULES', '1')) + else: + # + # On windows the MERGED_MODULE option is not needed + # (and does not work) + # + xslt_c_files = ['libxslt-py.c', 'libxslt.c', 'types.c'] + libs.insert(0, libraryPrefix + 'exslt') + libs.insert(0, libraryPrefix + 'xslt') + includes.append(xslt_includes) + modules.append('libxslt') + + +extens=[Extension('libxml2mod', c_files, include_dirs=includes, + library_dirs=libdirs, + libraries=libs, define_macros=macros)] +if with_xslt == 1: + extens.append(Extension('libxsltmod', xslt_c_files, include_dirs=includes, + library_dirs=libdirs, + libraries=libs, define_macros=macros)) + +if missing("MANIFEST"): + + manifest = open("MANIFEST", "w") + manifest.write("setup.py\n") + for file in xml_files: + manifest.write(file + "\n") + if with_xslt == 1: + for file in xslt_files: + manifest.write(file + "\n") + manifest.close() + +if WITHDLLS: + ext_package = "libxmlmods" + if sys.version >= "2.2": + base = "lib/site-packages/" + else: + base = "" + data_files = [(base+"libxmlmods",dlls)] +else: + ext_package = None + data_files = [] + +setup (name = "libxml2-python", + # On *nix, the version number is created from setup.py.in + # On windows, it is set by configure.js + version = "2.9.4", + description = descr, + author = "Daniel Veillard", + author_email = "veillard@redhat.com", + url = "http://xmlsoft.org/python.html", + licence="MIT Licence", + py_modules=modules, + ext_modules=extens, + ext_package=ext_package, + data_files=data_files, + ) + +sys.exit(0) + diff --git a/libxml2/python/setup.py.in b/libxml2/python/setup.py.in new file mode 100755 index 0000000..90c2114 --- /dev/null +++ b/libxml2/python/setup.py.in @@ -0,0 +1,242 @@ +#!/usr/bin/python -u +# +# Setup script for libxml2 and libxslt if found +# +import sys, os +from distutils.core import setup, Extension + +# Below ROOT, we expect to find include, include/libxml2, lib and bin. +# On *nix, it is not needed (but should not harm), +# on Windows, it is set by configure.js. +ROOT = r'@prefix@' + +# Thread-enabled libxml2 +with_threads = @WITH_THREADS@ + +# If this flag is set (windows only), +# a private copy of the dlls are included in the package. +# If this flag is not set, the libxml2 and libxslt +# dlls must be found somewhere in the PATH at runtime. +WITHDLLS = 1 and sys.platform.startswith('win') + +def missing(file): + if os.access(file, os.R_OK) == 0: + return 1 + return 0 + +try: + HOME = os.environ['HOME'] +except: + HOME="C:" + +if WITHDLLS: + # libxml dlls (expected in ROOT/bin) + dlls = [ 'iconv.dll','libxml2.dll','libxslt.dll','libexslt.dll' ] + dlls = [os.path.join(ROOT,'bin',dll) for dll in dlls] + + # create __init__.py for the libxmlmods package + if not os.path.exists("libxmlmods"): + os.mkdir("libxmlmods") + open("libxmlmods/__init__.py","w").close() + + def altImport(s): + s = s.replace("import libxml2mod","from libxmlmods import libxml2mod") + s = s.replace("import libxsltmod","from libxmlmods import libxsltmod") + return s + +if sys.platform.startswith('win'): + libraryPrefix = 'lib' + platformLibs = [] +else: + libraryPrefix = '' + platformLibs = ["m","z"] + +# those are examined to find +# - libxml2/libxml/tree.h +# - iconv.h +# - libxslt/xsltconfig.h +includes_dir = [ +"/usr/include", +"/usr/local/include", +"/opt/include", +os.path.join(ROOT,'include'), +HOME +]; + +xml_includes="" +for dir in includes_dir: + if not missing(dir + "/libxml2/libxml/tree.h"): + xml_includes=dir + "/libxml2" + break; + +if xml_includes == "": + print("failed to find headers for libxml2: update includes_dir") + sys.exit(1) + +iconv_includes="" +for dir in includes_dir: + if not missing(dir + "/iconv.h"): + iconv_includes=dir + break; + +if iconv_includes == "": + print("failed to find headers for libiconv: update includes_dir") + sys.exit(1) + +# those are added in the linker search path for libraries +libdirs = [ +os.path.join(ROOT,'lib'), +] + +xml_files = ["libxml2-api.xml", "libxml2-python-api.xml", + "libxml.c", "libxml.py", "libxml_wrap.h", "types.c", + "xmlgenerator.py", "README", "TODO", "drv_libxml2.py"] + +xslt_files = ["libxslt-api.xml", "libxslt-python-api.xml", + "libxslt.c", "libxsl.py", "libxslt_wrap.h", + "xsltgenerator.py"] + +if missing("libxml2-py.c") or missing("libxml2.py"): + try: + try: + import xmlgenerator + except: + import generator + except: + print("failed to find and generate stubs for libxml2, aborting ...") + print(sys.exc_info()[0], sys.exc_info()[1]) + sys.exit(1) + + head = open("libxml.py", "r") + generated = open("libxml2class.py", "r") + result = open("libxml2.py", "w") + for line in head.readlines(): + if WITHDLLS: + result.write(altImport(line)) + else: + result.write(line) + for line in generated.readlines(): + result.write(line) + head.close() + generated.close() + result.close() + +with_xslt=0 +if missing("libxslt-py.c") or missing("libxslt.py"): + if missing("xsltgenerator.py") or missing("libxslt-api.xml"): + print("libxslt stub generator not found, libxslt not built") + else: + try: + import xsltgenerator + except: + print("failed to generate stubs for libxslt, aborting ...") + print(sys.exc_info()[0], sys.exc_info()[1]) + else: + head = open("libxsl.py", "r") + generated = open("libxsltclass.py", "r") + result = open("libxslt.py", "w") + for line in head.readlines(): + if WITHDLLS: + result.write(altImport(line)) + else: + result.write(line) + for line in generated.readlines(): + result.write(line) + head.close() + generated.close() + result.close() + with_xslt=1 +else: + with_xslt=1 + +if with_xslt == 1: + xslt_includes="" + for dir in includes_dir: + if not missing(dir + "/libxslt/xsltconfig.h"): + xslt_includes=dir + "/libxslt" + break; + + if xslt_includes == "": + print("failed to find headers for libxslt: update includes_dir") + with_xslt = 0 + + +descr = "libxml2 package" +modules = [ 'libxml2', 'drv_libxml2' ] +if WITHDLLS: + modules.append('libxmlmods.__init__') +c_files = ['libxml2-py.c', 'libxml.c', 'types.c' ] +includes= [xml_includes, iconv_includes] +libs = [libraryPrefix + "xml2"] + platformLibs +macros = [] +if with_threads: + macros.append(('_REENTRANT','1')) +if with_xslt == 1: + descr = "libxml2 and libxslt package" + if not sys.platform.startswith('win'): + # + # We are gonna build 2 identical shared libs with merge initializing + # both libxml2mod and libxsltmod + # + c_files = c_files + ['libxslt-py.c', 'libxslt.c'] + xslt_c_files = c_files + macros.append(('MERGED_MODULES', '1')) + else: + # + # On windows the MERGED_MODULE option is not needed + # (and does not work) + # + xslt_c_files = ['libxslt-py.c', 'libxslt.c', 'types.c'] + libs.insert(0, libraryPrefix + 'exslt') + libs.insert(0, libraryPrefix + 'xslt') + includes.append(xslt_includes) + modules.append('libxslt') + + +extens=[Extension('libxml2mod', c_files, include_dirs=includes, + library_dirs=libdirs, + libraries=libs, define_macros=macros)] +if with_xslt == 1: + extens.append(Extension('libxsltmod', xslt_c_files, include_dirs=includes, + library_dirs=libdirs, + libraries=libs, define_macros=macros)) + +if missing("MANIFEST"): + + manifest = open("MANIFEST", "w") + manifest.write("setup.py\n") + for file in xml_files: + manifest.write(file + "\n") + if with_xslt == 1: + for file in xslt_files: + manifest.write(file + "\n") + manifest.close() + +if WITHDLLS: + ext_package = "libxmlmods" + if sys.version >= "2.2": + base = "lib/site-packages/" + else: + base = "" + data_files = [(base+"libxmlmods",dlls)] +else: + ext_package = None + data_files = [] + +setup (name = "libxml2-python", + # On *nix, the version number is created from setup.py.in + # On windows, it is set by configure.js + version = "@LIBXML_VERSION@", + description = descr, + author = "Daniel Veillard", + author_email = "veillard@redhat.com", + url = "http://xmlsoft.org/python.html", + licence="MIT Licence", + py_modules=modules, + ext_modules=extens, + ext_package=ext_package, + data_files=data_files, + ) + +sys.exit(0) + diff --git a/libxml2/python/tests/Makefile.am b/libxml2/python/tests/Makefile.am new file mode 100644 index 0000000..95ebead --- /dev/null +++ b/libxml2/python/tests/Makefile.am @@ -0,0 +1,75 @@ +exampledir = $(datadir)/doc/libxml2-python-$(LIBXML_VERSION)/examples +dist_example_DATA = $(PYTESTS) $(XMLS) + +PYTESTS= \ + build.py \ + attribs.py \ + tst.py \ + tstxpath.py \ + xpathext.py \ + push.py \ + pushSAX.py \ + pushSAXhtml.py \ + error.py \ + serialize.py\ + validate.py \ + tstURI.py \ + cutnpaste.py\ + xpathret.py \ + xpath.py \ + outbuf.py \ + inbuf.py \ + input_callback.py \ + resolver.py \ + regexp.py \ + reader.py \ + reader2.py \ + reader3.py \ + reader4.py \ + reader5.py \ + reader6.py \ + reader7.py \ + reader8.py \ + readernext.py \ + walker.py \ + nsdel.py \ + ctxterror.py\ + readererr.py\ + relaxng.py \ + schema.py \ + thread2.py \ + sync.py \ + tstLastError.py \ + indexes.py \ + dtdvalid.py \ + tstmem.py \ + validDTD.py \ + validSchemas.py \ + validRNG.py \ + compareNodes.py \ + xpathns.py \ + xpathleak.py + +XMLS= \ + tst.xml \ + valid.xml \ + invalid.xml \ + test.dtd + +CLEANFILES = core tmp.xml *.pyc + +if WITH_PYTHON +tests: $(PYTESTS) + @for f in $(XMLS) ; do test -f $$f || $(LN_S) $(srcdir)/$$f . ; done + @echo "## running Python regression tests" + -@(PYTHONPATH="..:../.libs:$(srcdir)/..:$$PYTHONPATH" ; \ + export PYTHONPATH; \ + LD_LIBRARY_PATH="$(top_builddir)/.libs:$$LD_LIBRARY_PATH" ; \ + export LD_LIBRARY_PATH; \ + for test in $(PYTESTS) ; \ + do log=`$(PYTHON) $(srcdir)/$$test` ; \ + if [ "`echo $$log | grep OK`" = "" ] ; then \ + echo "-- $$test" ; echo "$$log" ; fi ; done) +else +tests: +endif diff --git a/libxml2/python/tests/attribs.py b/libxml2/python/tests/attribs.py new file mode 100755 index 0000000..99132c2 --- /dev/null +++ b/libxml2/python/tests/attribs.py @@ -0,0 +1,34 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +# +# Testing XML document serialization +# +doc = libxml2.parseDoc( +"""<?xml version="1.0" encoding="iso-8859-1"?> +<!DOCTYPE test [ +<!ELEMENT test (#PCDATA) > +<!ATTLIST test xmlns:abc CDATA #FIXED "http://abc.org" > +<!ATTLIST test abc:attr CDATA #FIXED "def" > +]> +<test /> +""") +elem = doc.getRootElement() +attr = elem.hasNsProp('attr', 'http://abc.org') +if attr == None or attr.serialize()[:-1] != """<!ATTLIST test abc:attr CDATA #FIXED "def">""": + print("Failed to find defaulted attribute abc:attr") + sys.exit(1) + +doc.freeDoc() + +# 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/libxml2/python/tests/build.py b/libxml2/python/tests/build.py new file mode 100755 index 0000000..b2d3f78 --- /dev/null +++ b/libxml2/python/tests/build.py @@ -0,0 +1,59 @@ +#!/usr/bin/python -u +import libxml2 +import sys + +# Memory debug specific +libxml2.debugMemory(1) + +doc = libxml2.newDoc("1.0") +comment = doc.newDocComment("This is a generated document") +doc.addChild(comment) +pi = libxml2.newPI("test", "PI content") +doc.addChild(pi) +root = doc.newChild(None, "doc", None) +ns = root.newNs("http://example.com/doc", "my") +root.setNs(ns) +elem = root.newChild(None, "foo", "bar") +elem.setBase("http://example.com/imgs") +elem.setProp("img", "image.gif") +doc.saveFile("tmp.xml") +doc.freeDoc() + +doc = libxml2.parseFile("tmp.xml") +comment = doc.children +if comment.type != "comment" or \ + comment.content != "This is a generated document": + print("error rereading comment") + sys.exit(1) +pi = comment.next +if pi.type != "pi" or pi.name != "test" or pi.content != "PI content": + print("error rereading PI") + sys.exit(1) +root = pi.next +if root.name != "doc": + print("error rereading root") + sys.exit(1) +ns = root.ns() +if ns.name != "my" or ns.content != "http://example.com/doc": + print("error rereading namespace") + sys.exit(1) +elem = root.children +if elem.name != "foo": + print("error rereading elem") + sys.exit(1) +if elem.getBase(None) != "http://example.com/imgs": + print("error rereading base") + sys.exit(1) +if elem.prop("img") != "image.gif": + print("error rereading property") + sys.exit(1) + +doc.freeDoc() + +# 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/libxml2/python/tests/compareNodes.py b/libxml2/python/tests/compareNodes.py new file mode 100755 index 0000000..ca5a5a2 --- /dev/null +++ b/libxml2/python/tests/compareNodes.py @@ -0,0 +1,50 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +# +# Testing XML Node comparison and Node hash-value +# +doc = libxml2.parseDoc("""<root><foo/></root>""") +root = doc.getRootElement() + +# Create two different objects which point to foo +foonode1 = root.children +foonode2 = root.children + +# Now check that [in]equality tests work ok +if not ( foonode1 == foonode2 ): + print("Error comparing nodes with ==, nodes should be equal but are unequal") + sys.exit(1) +if not ( foonode1 != root ): + print("Error comparing nodes with ==, nodes should not be equal but are equal") + sys.exit(1) +if not ( foonode1 != root ): + print("Error comparing nodes with !=, nodes should not be equal but are equal") +if ( foonode1 != foonode2 ): + print("Error comparing nodes with !=, nodes should be equal but are unequal") + +# Next check that the hash function for the objects also works ok +if not (hash(foonode1) == hash(foonode2)): + print("Error hash values for two equal nodes are different") + sys.exit(1) +if not (hash(foonode1) != hash(root)): + print("Error hash values for two unequal nodes are not different") + sys.exit(1) +if hash(foonode1) == hash(root): + print("Error hash values for two unequal nodes are equal") + sys.exit(1) + +# Basic tests successful +doc.freeDoc() + +# 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/libxml2/python/tests/ctxterror.py b/libxml2/python/tests/ctxterror.py new file mode 100755 index 0000000..416e384 --- /dev/null +++ b/libxml2/python/tests/ctxterror.py @@ -0,0 +1,56 @@ +#!/usr/bin/python -u +# +# This test exercise the redirection of error messages with a +# functions defined in Python. +# +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +expect="""--> (3) xmlns: URI foo is not absolute +--> (4) Opening and ending tag mismatch: x line 0 and y +""" + +err="" +def callback(arg,msg,severity,reserved): + global err + err = err + "%s (%d) %s" % (arg,severity,msg) + +s = """<x xmlns="foo"></y>""" + +parserCtxt = libxml2.createPushParser(None,"",0,"test.xml") +parserCtxt.setErrorHandler(callback, "-->") +if parserCtxt.getErrorHandler() != (callback,"-->"): + print("getErrorHandler failed") + sys.exit(1) +parserCtxt.parseChunk(s,len(s),1) +doc = parserCtxt.doc() +doc.freeDoc() +parserCtxt = None + +if err != expect: + print("error") + print("received %s" %(err)) + print("expected %s" %(expect)) + sys.exit(1) + +i = 10000 +while i > 0: + parserCtxt = libxml2.createPushParser(None,"",0,"test.xml") + parserCtxt.setErrorHandler(callback, "-->") + parserCtxt.parseChunk(s,len(s),1) + doc = parserCtxt.doc() + doc.freeDoc() + parserCtxt = None + err = "" + i = i - 1 + +# 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/libxml2/python/tests/cutnpaste.py b/libxml2/python/tests/cutnpaste.py new file mode 100755 index 0000000..7787246 --- /dev/null +++ b/libxml2/python/tests/cutnpaste.py @@ -0,0 +1,48 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +# +# Testing XML document serialization +# +source = libxml2.parseDoc("""<?xml version="1.0"?> +<root xmlns:foo="http://example.org/foo" + xmlns:bar="http://example.org/bar"> +<include xmlns="http://example.org/include"> +<fragment><foo:elem bar="tricky"/></fragment> +</include> +</root> +""") + +target = libxml2.parseDoc("""<?xml version="1.0"?> +<root xmlns:foobar="http://example.org/bar"/>""") + +fragment = source.xpathEval("//*[name()='fragment']")[0] +dest = target.getRootElement() + +# do a cut and paste operation +fragment.unlinkNode() +dest.addChild(fragment) +# do the namespace fixup +dest.reconciliateNs(target) + +# The source tree can be freed at that point +source.freeDoc() + +# check the resulting tree +str = dest.serialize() +if str != """<root xmlns:foobar="http://example.org/bar" xmlns:default="http://example.org/include" xmlns:foo="http://example.org/foo"><default:fragment><foo:elem bar="tricky"/></default:fragment></root>""": + print("reconciliateNs() failed") + sys.exit(1) +target.freeDoc() + +# 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/libxml2/python/tests/dtdvalid.py b/libxml2/python/tests/dtdvalid.py new file mode 100755 index 0000000..d4049b8 --- /dev/null +++ b/libxml2/python/tests/dtdvalid.py @@ -0,0 +1,32 @@ +#!/usr/bin/python -u +import libxml2 +import sys + +# Memory debug specific +libxml2.debugMemory(1) + +dtd="""<!ELEMENT foo EMPTY>""" +instance="""<?xml version="1.0"?> +<foo></foo>""" + +dtd = libxml2.parseDTD(None, 'test.dtd') +ctxt = libxml2.newValidCtxt() +doc = libxml2.parseDoc(instance) +ret = doc.validateDtd(ctxt, dtd) +if ret != 1: + print("error doing DTD validation") + sys.exit(1) + +doc.freeDoc() +dtd.freeDtd() +del dtd +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/libxml2/python/tests/error.py b/libxml2/python/tests/error.py new file mode 100755 index 0000000..530c2ee --- /dev/null +++ b/libxml2/python/tests/error.py @@ -0,0 +1,51 @@ +#!/usr/bin/python -u +# +# This test exercise the redirection of error messages with a +# functions defined in Python. +# +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +expect='--> I/O --> warning : --> failed to load external entity "missing.xml"\n' +err="" +def callback(ctx, str): + global err + + err = err + "%s %s" % (ctx, str) + +got_exc = 0 +libxml2.registerErrorHandler(callback, "-->") +try: + doc = libxml2.parseFile("missing.xml") +except libxml2.parserError: + got_exc = 1 + +if got_exc == 0: + print("Failed to get a parser exception") + sys.exit(1) + +if err != expect: + print("error") + print("received %s" %(err)) + print("expected %s" %(expect)) + sys.exit(1) + +i = 10000 +while i > 0: + try: + doc = libxml2.parseFile("missing.xml") + except libxml2.parserError: + got_exc = 1 + err = "" + i = i - 1 + +# 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/libxml2/python/tests/inbuf.py b/libxml2/python/tests/inbuf.py new file mode 100755 index 0000000..0c16674 --- /dev/null +++ b/libxml2/python/tests/inbuf.py @@ -0,0 +1,30 @@ +#!/usr/bin/python -u +import sys +import libxml2 +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# Memory debug specific +libxml2.debugMemory(1) + +i = 0 +while i < 5000: + f = str_io("foobar") + buf = libxml2.inputBuffer(f) + i = i + 1 + +del f +del buf + +# 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/libxml2/python/tests/indexes.py b/libxml2/python/tests/indexes.py new file mode 100755 index 0000000..e41a0d9 --- /dev/null +++ b/libxml2/python/tests/indexes.py @@ -0,0 +1,113 @@ +#!/usr/bin/python -u +# -*- coding: ISO-8859-1 -*- +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +ctxt = None + +class callback: + def __init__(self, startd, starte, ende, delta, endd): + self.startd = startd + self.starte = starte + self.ende = ende + self.endd = endd + self.delta = delta + self.count = 0 + + def startDocument(self): + global ctxt + if ctxt.byteConsumed() != self.startd: + print("document start at wrong index: %d expecting %d\n" % ( + ctxt.byteConsumed(), self.startd)) + sys.exit(1) + + def endDocument(self): + global ctxt + expect = self.ende + self.delta * (self.count - 1) + self.endd + if ctxt.byteConsumed() != expect: + print("document end at wrong index: %d expecting %d\n" % ( + ctxt.byteConsumed(), expect)) + sys.exit(1) + + def startElement(self, tag, attrs): + global ctxt + if tag == "bar1": + expect = self.starte + self.delta * self.count + if ctxt.byteConsumed() != expect: + print("element start at wrong index: %d expecting %d\n" % ( + ctxt.byteConsumed(), expect)) + sys.exit(1) + + + def endElement(self, tag): + global ctxt + if tag == "bar1": + expect = self.ende + self.delta * self.count + if ctxt.byteConsumed() != expect: + print("element end at wrong index: %d expecting %d\n" % ( + ctxt.byteConsumed(), expect)) + sys.exit(1) + self.count = self.count + 1 + + def characters(self, data): + pass + +# +# First run a pure UTF-8 test +# +handler = callback(0, 13, 27, 198, 183) +ctxt = libxml2.createPushParser(handler, "<foo>\n", 6, "test.xml") +chunk = """ <bar1>chars1</bar1> + <bar2>chars2</bar2> + <bar3>chars3</bar3> + <bar4>chars4</bar4> + <bar5>chars5</bar5> + <bar6><s6</bar6> + <bar7>chars7</bar7> + <bar8>&8</bar8> + <bar9>chars9</bar9> +""" +i = 0 +while i < 10000: + ctxt.parseChunk(chunk, len(chunk), 0) + i = i + 1 +chunk = "</foo>" +ctxt.parseChunk(chunk, len(chunk), 1) +ctxt=None + +# +# Then run a test relying on ISO-Latin-1 +# +handler = callback(43, 57, 71, 198, 183) +chunk="""<?xml version="1.0" encoding="ISO-8859-1"?> +<foo> +""" +ctxt = libxml2.createPushParser(handler, chunk, len(chunk), "test.xml") +chunk = """ <bar1>chars1</bar1> + <bar2>chars2</bar2> + <bar3>chars3</bar3> + <bar4>chàrs4</bar4> + <bar5>chars5</bar5> + <bar6><s6</bar6> + <bar7>chars7</bar7> + <bar8>&8</bar8> + <bar9>très 9</bar9> +""" +i = 0 +while i < 10000: + ctxt.parseChunk(chunk, len(chunk), 0) + i = i + 1 +chunk = "</foo>" +ctxt.parseChunk(chunk, len(chunk), 1) +ctxt=None + +# 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/libxml2/python/tests/input_callback.py b/libxml2/python/tests/input_callback.py new file mode 100755 index 0000000..495ab62 --- /dev/null +++ b/libxml2/python/tests/input_callback.py @@ -0,0 +1,148 @@ +#!/usr/bin/python -u +# +# This tests custom input callbacks +# +import sys +import libxml2 +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# We implement a new scheme, py://strings/ that will reference this dictionary +pystrings = { + 'catalogs/catalog.xml' : +'''<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE catalog PUBLIC "-//OASIS//DTD Entity Resolution XML Catalog V1.0//EN" "http://www.oasis-open.org/committees/entity/release/1.0/catalog.dtd"> +<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog"> + <rewriteSystem systemIdStartString="http://example.com/dtds/" rewritePrefix="../dtds/"/> +</catalog>''', + + 'xml/sample.xml' : +'''<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE root SYSTEM "http://example.com/dtds/sample.dtd"> +<root>&sample.entity;</root>''', + + 'dtds/sample.dtd' : +''' +<!ELEMENT root (#PCDATA)> +<!ENTITY sample.entity "replacement text">''' +} + +prefix = "py://strings/" +startURL = prefix + "xml/sample.xml" +catURL = prefix + "catalogs/catalog.xml" + +def my_input_cb(URI): + if not(URI.startswith(prefix)): + return None + path = URI[len(prefix):] + if path not in pystrings: + return None + return str_io(pystrings[path]) + + +def run_test(desc, docpath, catalog, exp_status="verified", exp_err=[], test_callback=None, + root_name="root", root_content="replacement text"): + opts = libxml2.XML_PARSE_DTDLOAD | libxml2.XML_PARSE_NONET | libxml2.XML_PARSE_COMPACT + actual_err = [] + + def my_global_error_cb(ctx, msg): + actual_err.append((-1, msg)) + def my_ctx_error_cb(arg, msg, severity, reserved): + actual_err.append((severity, msg)) + + libxml2.registerErrorHandler(my_global_error_cb, None) + try: + parser = libxml2.createURLParserCtxt(docpath, opts) + parser.setErrorHandler(my_ctx_error_cb, None) + if catalog is not None: + parser.addLocalCatalog(catalog) + if test_callback is not None: + test_callback() + parser.parseDocument() + doc = parser.doc() + actual_status = "loaded" + e = doc.getRootElement() + if e.name == root_name and e.content == root_content: + actual_status = "verified" + doc.freeDoc() + except libxml2.parserError: + actual_status = "not loaded" + + if actual_status != exp_status: + print("Test '%s' failed: expect status '%s', actual '%s'" % (desc, exp_status, actual_status)) + sys.exit(1) + elif actual_err != exp_err: + print("Test '%s' failed" % desc) + print("Expect errors:") + for s,m in exp_err: print(" [%2d] '%s'" % (s,m)) + print("Actual errors:") + for s,m in actual_err: print(" [%2d] '%s'" % (s,m)) + sys.exit(1) + + +# Check that we cannot read custom schema without custom callback +run_test(desc="Loading entity without custom callback", + docpath=startURL, catalog=None, + exp_status="not loaded", exp_err=[ + (-1, "I/O "), + (-1, "warning : "), + (-1, "failed to load external entity \"py://strings/xml/sample.xml\"\n") + ]) + +# Register handler and try to load the same entity +libxml2.registerInputCallback(my_input_cb) +run_test(desc="Loading entity with custom callback", + docpath=startURL, catalog=None, + exp_status="loaded", exp_err=[ + (-1, "Attempt to load network entity http://example.com/dtds/sample.dtd"), + ( 4, "Entity 'sample.entity' not defined\n") + ]) + +# Register a catalog (also accessible via pystr://) and retry +run_test(desc="Loading entity with custom callback and catalog", + docpath=startURL, catalog=catURL) + +# Unregister custom callback when parser is already created +run_test(desc="Loading entity and unregistering callback", + docpath=startURL, catalog=catURL, + test_callback=lambda: libxml2.popInputCallbacks(), + exp_status="loaded", exp_err=[ + ( 3, "failed to load external entity \"py://strings/dtds/sample.dtd\"\n"), + ( 4, "Entity 'sample.entity' not defined\n") + ]) + +# Try to load the document again +run_test(desc="Retry loading document after unregistering callback", + docpath=startURL, catalog=catURL, + exp_status="not loaded", exp_err=[ + (-1, "I/O "), + (-1, "warning : "), + (-1, "failed to load external entity \"py://strings/xml/sample.xml\"\n") + ]) + +# But should be able to read standard I/O yet... +run_test(desc="Loading using standard i/o after unregistering callback", + docpath="tst.xml", catalog=None, + root_name='doc', root_content='bar') + +# Now pop ALL input callbacks, should fail to load even standard I/O +try: + while True: + libxml2.popInputCallbacks() +except IndexError: + pass + +run_test(desc="Loading using standard i/o after unregistering all callbacks", + docpath="tst.xml", catalog=None, + exp_status="not loaded", exp_err=[ + (-1, "I/O "), + (-1, "warning : "), + (-1, "failed to load external entity \"tst.xml\"\n") + ]) + +print("OK") +sys.exit(0); diff --git a/libxml2/python/tests/invalid.xml b/libxml2/python/tests/invalid.xml new file mode 100644 index 0000000..7c9b27e --- /dev/null +++ b/libxml2/python/tests/invalid.xml @@ -0,0 +1,6 @@ +<!DOCTYPE doc [ +<!ELEMENT doc (a, b, a)> +<!ELEMENT a EMPTY> +<!ELEMENT b EMPTY> +]> +<doc><b/><a/><b/></doc> diff --git a/libxml2/python/tests/nsdel.py b/libxml2/python/tests/nsdel.py new file mode 100755 index 0000000..079399a --- /dev/null +++ b/libxml2/python/tests/nsdel.py @@ -0,0 +1,62 @@ +#!/usr/bin/python -u +# +# this test exercise the XPath basic engine, parser, etc, and +# allows to detect memory leaks +# +import sys +import libxml2 + +instance="""<?xml version="1.0"?> +<tag xmlns:foo='urn:foo' xmlns:bar='urn:bar' xmlns:baz='urn:baz' />""" + +def namespaceDefs(node): + n = node.nsDefs() + while n: + yield n + n = n.next + +def checkNamespaceDefs(node, count): + nsList = list(namespaceDefs(node)) + #print nsList + if len(nsList) != count : + raise Exception("Error: saw %d namespace declarations. Expected %d" % (len(nsList), count)) + +# Memory debug specific +libxml2.debugMemory(1) + +# Remove single namespace +doc = libxml2.parseDoc(instance) +node = doc.getRootElement() +checkNamespaceDefs(node, 3) +ns = node.removeNsDef('urn:bar') +checkNamespaceDefs(node, 2) +ns.freeNsList() +doc.freeDoc() + +# Remove all namespaces +doc = libxml2.parseDoc(instance) +node = doc.getRootElement() +checkNamespaceDefs(node, 3) +ns = node.removeNsDef(None) +checkNamespaceDefs(node, 0) +ns.freeNsList() +doc.freeDoc() + +# Remove a namespace refered to by a child +doc = libxml2.newDoc("1.0") +root = doc.newChild(None, "root", None) +namespace = root.newNs("http://example.com/sample", "s") +child = root.newChild(namespace, "child", None) +root.removeNsDef("http://example.com/sample") +doc.reconciliateNs(root) +namespace.freeNsList() +doc.serialize() # This should not segfault +doc.freeDoc() + +# 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/libxml2/python/tests/outbuf.py b/libxml2/python/tests/outbuf.py new file mode 100755 index 0000000..62761cc --- /dev/null +++ b/libxml2/python/tests/outbuf.py @@ -0,0 +1,110 @@ +#!/usr/bin/python -u +import sys +import libxml2 +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +def testSimpleBufferWrites(): + f = str_io() + buf = libxml2.createOutputBuffer(f, "ISO-8859-1") + buf.write(3, "foo") + buf.writeString("bar") + buf.close() + + if f.getvalue() != "foobar": + print("Failed to save to StringIO") + sys.exit(1) + +def testSaveDocToBuffer(): + """ + Regression test for bug #154294. + """ + input = '<foo>Hello</foo>' + expected = '''\ +<?xml version="1.0" encoding="UTF-8"?> +<foo>Hello</foo> +''' + f = str_io() + buf = libxml2.createOutputBuffer(f, 'UTF-8') + doc = libxml2.parseDoc(input) + doc.saveFileTo(buf, 'UTF-8') + doc.freeDoc() + if f.getvalue() != expected: + print('xmlDoc.saveFileTo() call failed.') + print(' got: %s' % repr(f.getvalue())) + print('expected: %s' % repr(expected)) + sys.exit(1) + +def testSaveFormattedDocToBuffer(): + input = '<outer><inner>Some text</inner><inner/></outer>' + # The formatted and non-formatted versions of the output. + expected = ('''\ +<?xml version="1.0" encoding="UTF-8"?> +<outer><inner>Some text</inner><inner/></outer> +''', '''\ +<?xml version="1.0" encoding="UTF-8"?> +<outer> + <inner>Some text</inner> + <inner/> +</outer> +''') + doc = libxml2.parseDoc(input) + for i in (0, 1): + f = str_io() + buf = libxml2.createOutputBuffer(f, 'UTF-8') + doc.saveFormatFileTo(buf, 'UTF-8', i) + if f.getvalue() != expected[i]: + print('xmlDoc.saveFormatFileTo() call failed.') + print(' got: %s' % repr(f.getvalue())) + print('expected: %s' % repr(expected[i])) + sys.exit(1) + doc.freeDoc() + +def testSaveIntoOutputBuffer(): + """ + Similar to the previous two tests, except this time we invoke the save + methods on the output buffer object and pass in an XML node object. + """ + input = '<foo>Hello</foo>' + expected = '''\ +<?xml version="1.0" encoding="UTF-8"?> +<foo>Hello</foo> +''' + f = str_io() + doc = libxml2.parseDoc(input) + buf = libxml2.createOutputBuffer(f, 'UTF-8') + buf.saveFileTo(doc, 'UTF-8') + if f.getvalue() != expected: + print('outputBuffer.saveFileTo() call failed.') + print(' got: %s' % repr(f.getvalue())) + print('expected: %s' % repr(expected)) + sys.exit(1) + f = str_io() + buf = libxml2.createOutputBuffer(f, 'UTF-8') + buf.saveFormatFileTo(doc, 'UTF-8', 1) + if f.getvalue() != expected: + print('outputBuffer.saveFormatFileTo() call failed.') + print(' got: %s' % repr(f.getvalue())) + print('expected: %s' % repr(expected)) + sys.exit(1) + doc.freeDoc() + +if __name__ == '__main__': + # Memory debug specific + libxml2.debugMemory(1) + + testSimpleBufferWrites() + testSaveDocToBuffer() + testSaveFormattedDocToBuffer() + testSaveIntoOutputBuffer() + + libxml2.cleanupParser() + if libxml2.debugMemory(1) == 0: + print("OK") + else: + print("Memory leak %d bytes" % (libxml2.debugMemory(1))) + libxml2.dumpMemory() diff --git a/libxml2/python/tests/push.py b/libxml2/python/tests/push.py new file mode 100755 index 0000000..0edd61d --- /dev/null +++ b/libxml2/python/tests/push.py @@ -0,0 +1,35 @@ +#!/usr/bin/python -u +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() +ctxt=None +if doc.name != "test.xml": + print("document name error") + sys.exit(1) +root = doc.children +if root.name != "foo": + print("root element name error") + sys.exit(1) +doc.freeDoc() +i = 10000 +while i > 0: + ctxt = libxml2.createPushParser(None, "<foo", 4, "test.xml") + ctxt.parseChunk("/>", 2, 1) + doc = ctxt.doc() + doc.freeDoc() + i = i -1 +ctxt=None + +# 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/libxml2/python/tests/pushSAX.py b/libxml2/python/tests/pushSAX.py new file mode 100755 index 0000000..48f6e82 --- /dev/null +++ b/libxml2/python/tests/pushSAX.py @@ -0,0 +1,64 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +log = "" + +class callback: + def startDocument(self): + global log + log = log + "startDocument:" + + def endDocument(self): + global log + log = log + "endDocument:" + + def startElement(self, tag, attrs): + global log + log = log + "startElement %s %s:" % (tag, attrs) + + def endElement(self, tag): + global log + log = log + "endElement %s:" % (tag) + + def characters(self, data): + global log + log = log + "characters: %s:" % (data) + + def warning(self, msg): + global log + log = log + "warning: %s:" % (msg) + + def error(self, msg): + global log + log = log + "error: %s:" % (msg) + + def fatalError(self, msg): + global log + log = log + "fatalError: %s:" % (msg) + +handler = callback() + +ctxt = libxml2.createPushParser(handler, "<foo", 4, "test.xml") +chunk = " url='tst'>b" +ctxt.parseChunk(chunk, len(chunk), 0) +chunk = "ar</foo>" +ctxt.parseChunk(chunk, len(chunk), 1) +ctxt=None + +reference = "startDocument:startElement foo {'url': 'tst'}:characters: bar:endElement foo:endDocument:" +if log != reference: + print("Error got: %s" % log) + print("Exprected: %s" % reference) + sys.exit(1) + +# 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/libxml2/python/tests/pushSAXhtml.py b/libxml2/python/tests/pushSAXhtml.py new file mode 100755 index 0000000..159d308 --- /dev/null +++ b/libxml2/python/tests/pushSAXhtml.py @@ -0,0 +1,65 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +log = "" + +class callback: + def startDocument(self): + global log + log = log + "startDocument:" + + def endDocument(self): + global log + log = log + "endDocument:" + + def startElement(self, tag, attrs): + global log + log = log + "startElement %s %s:" % (tag, attrs) + + def endElement(self, tag): + global log + log = log + "endElement %s:" % (tag) + + def characters(self, data): + global log + log = log + "characters: %s:" % (data) + + def warning(self, msg): + global log + log = log + "warning: %s:" % (msg) + + def error(self, msg): + global log + log = log + "error: %s:" % (msg) + + def fatalError(self, msg): + global log + log = log + "fatalError: %s:" % (msg) + +handler = callback() + +ctxt = libxml2.htmlCreatePushParser(handler, "<foo", 4, "test.xml") +chunk = " url='tst'>b" +ctxt.htmlParseChunk(chunk, len(chunk), 0) +chunk = "ar</foo>" +ctxt.htmlParseChunk(chunk, len(chunk), 1) +ctxt=None + +reference = """startDocument:startElement html None:startElement body None:startElement foo {'url': 'tst'}:error: Tag foo invalid +:characters: bar:endElement foo:endElement body:endElement html:endDocument:""" +if log != reference: + print("Error got: %s" % log) + print("Exprected: %s" % reference) + sys.exit(1) + +# 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/libxml2/python/tests/reader.py b/libxml2/python/tests/reader.py new file mode 100755 index 0000000..173ce66 --- /dev/null +++ b/libxml2/python/tests/reader.py @@ -0,0 +1,446 @@ +#!/usr/bin/python -u +# -*- coding: ISO-8859-1 -*- +# +# this tests the basic APIs of the XmlTextReader interface +# +import libxml2 +import sys +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# Memory debug specific +libxml2.debugMemory(1) + +f = str_io("""<a><b b1="b1"/><c>content of c</c></a>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test1") +ret = reader.Read() +if ret != 1: + print("test1: Error reading to first element") + sys.exit(1) +if reader.Name() != "a" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 1 or reader.HasAttributes() != 0: + print("test1: Error reading the first element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test1: Error reading to second element") + sys.exit(1) +if reader.Name() != "b" or reader.IsEmptyElement() != 1 or \ + reader.NodeType() != 1 or reader.HasAttributes() != 1: + print("test1: Error reading the second element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test1: Error reading to third element") + sys.exit(1) +if reader.Name() != "c" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 1 or reader.HasAttributes() != 0: + print("test1: Error reading the third element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test1: Error reading to text node") + sys.exit(1) +if reader.Name() != "#text" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 3 or reader.HasAttributes() != 0 or \ + reader.Value() != "content of c": + print("test1: Error reading the text node") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test1: Error reading to end of third element") + sys.exit(1) +if reader.Name() != "c" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 15 or reader.HasAttributes() != 0: + print("test1: Error reading the end of third element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test1: Error reading to end of first element") + sys.exit(1) +if reader.Name() != "a" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 15 or reader.HasAttributes() != 0: + print("test1: Error reading the end of first element") + sys.exit(1) +ret = reader.Read() +if ret != 0: + print("test1: Error reading to end of document") + sys.exit(1) + +# +# example from the XmlTextReader docs +# +f = str_io("""<test xmlns:dt="urn:datatypes" dt:type="int"/>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test2") + +ret = reader.Read() +if ret != 1: + print("Error reading test element") + sys.exit(1) +if reader.GetAttributeNo(0) != "urn:datatypes" or \ + reader.GetAttributeNo(1) != "int" or \ + reader.GetAttributeNs("type", "urn:datatypes") != "int" or \ + reader.GetAttribute("dt:type") != "int": + print("error reading test attributes") + sys.exit(1) + +# +# example from the XmlTextReader docs +# +f = str_io("""<root xmlns:a="urn:456"> +<item> +<ref href="a:b"/> +</item> +</root>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test3") + +ret = reader.Read() +while ret == 1: + if reader.Name() == "ref": + if reader.LookupNamespace("a") != "urn:456": + print("error resolving namespace prefix") + sys.exit(1) + break + ret = reader.Read() +if ret != 1: + print("Error finding the ref element") + sys.exit(1) + +# +# Home made example for the various attribute access functions +# +f = str_io("""<testattr xmlns="urn:1" xmlns:a="urn:2" b="b" a:b="a:b"/>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test4") +ret = reader.Read() +if ret != 1: + print("Error reading the testattr element") + sys.exit(1) +# +# Attribute exploration by index +# +if reader.MoveToAttributeNo(0) != 1: + print("Failed moveToAttribute(0)") + sys.exit(1) +if reader.Value() != "urn:1": + print("Failed to read attribute(0)") + sys.exit(1) +if reader.Name() != "xmlns": + print("Failed to read attribute(0) name") + sys.exit(1) +if reader.MoveToAttributeNo(1) != 1: + print("Failed moveToAttribute(1)") + sys.exit(1) +if reader.Value() != "urn:2": + print("Failed to read attribute(1)") + sys.exit(1) +if reader.Name() != "xmlns:a": + print("Failed to read attribute(1) name") + sys.exit(1) +if reader.MoveToAttributeNo(2) != 1: + print("Failed moveToAttribute(2)") + sys.exit(1) +if reader.Value() != "b": + print("Failed to read attribute(2)") + sys.exit(1) +if reader.Name() != "b": + print("Failed to read attribute(2) name") + sys.exit(1) +if reader.MoveToAttributeNo(3) != 1: + print("Failed moveToAttribute(3)") + sys.exit(1) +if reader.Value() != "a:b": + print("Failed to read attribute(3)") + sys.exit(1) +if reader.Name() != "a:b": + print("Failed to read attribute(3) name") + sys.exit(1) +# +# Attribute exploration by name +# +if reader.MoveToAttribute("xmlns") != 1: + print("Failed moveToAttribute('xmlns')") + sys.exit(1) +if reader.Value() != "urn:1": + print("Failed to read attribute('xmlns')") + sys.exit(1) +if reader.MoveToAttribute("xmlns:a") != 1: + print("Failed moveToAttribute('xmlns')") + sys.exit(1) +if reader.Value() != "urn:2": + print("Failed to read attribute('xmlns:a')") + sys.exit(1) +if reader.MoveToAttribute("b") != 1: + print("Failed moveToAttribute('b')") + sys.exit(1) +if reader.Value() != "b": + print("Failed to read attribute('b')") + sys.exit(1) +if reader.MoveToAttribute("a:b") != 1: + print("Failed moveToAttribute('a:b')") + sys.exit(1) +if reader.Value() != "a:b": + print("Failed to read attribute('a:b')") + sys.exit(1) +if reader.MoveToAttributeNs("b", "urn:2") != 1: + print("Failed moveToAttribute('b', 'urn:2')") + sys.exit(1) +if reader.Value() != "a:b": + print("Failed to read attribute('b', 'urn:2')") + sys.exit(1) +# +# Go back and read in sequence +# +if reader.MoveToElement() != 1: + print("Failed to move back to element") + sys.exit(1) +if reader.MoveToFirstAttribute() != 1: + print("Failed to move to first attribute") + sys.exit(1) +if reader.Value() != "urn:1": + print("Failed to read attribute(0)") + sys.exit(1) +if reader.Name() != "xmlns": + print("Failed to read attribute(0) name") + sys.exit(1) +if reader.MoveToNextAttribute() != 1: + print("Failed to move to next attribute") + sys.exit(1) +if reader.Value() != "urn:2": + print("Failed to read attribute(1)") + sys.exit(1) +if reader.Name() != "xmlns:a": + print("Failed to read attribute(1) name") + sys.exit(1) +if reader.MoveToNextAttribute() != 1: + print("Failed to move to next attribute") + sys.exit(1) +if reader.Value() != "b": + print("Failed to read attribute(2)") + sys.exit(1) +if reader.Name() != "b": + print("Failed to read attribute(2) name") + sys.exit(1) +if reader.MoveToNextAttribute() != 1: + print("Failed to move to next attribute") + sys.exit(1) +if reader.Value() != "a:b": + print("Failed to read attribute(3)") + sys.exit(1) +if reader.Name() != "a:b": + print("Failed to read attribute(3) name") + sys.exit(1) +if reader.MoveToNextAttribute() != 0: + print("Failed to detect last attribute") + sys.exit(1) + + +# +# a couple of tests for namespace nodes +# +f = str_io("""<a xmlns="http://example.com/foo"/>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test6") +ret = reader.Read() +if ret != 1: + print("test6: failed to Read()") + sys.exit(1) +ret = reader.MoveToFirstAttribute() +if ret != 1: + print("test6: failed to MoveToFirstAttribute()") + sys.exit(1) +if reader.NamespaceUri() != "http://www.w3.org/2000/xmlns/" or \ + reader.LocalName() != "xmlns" or reader.Name() != "xmlns" or \ + reader.Value() != "http://example.com/foo" or reader.NodeType() != 2: + print("test6: failed to read the namespace node") + sys.exit(1) + +f = str_io("""<a xmlns:prefix="http://example.com/foo"/>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test7") +ret = reader.Read() +if ret != 1: + print("test7: failed to Read()") + sys.exit(1) +ret = reader.MoveToFirstAttribute() +if ret != 1: + print("test7: failed to MoveToFirstAttribute()") + sys.exit(1) +if reader.NamespaceUri() != "http://www.w3.org/2000/xmlns/" or \ + reader.LocalName() != "prefix" or reader.Name() != "xmlns:prefix" or \ + reader.Value() != "http://example.com/foo" or reader.NodeType() != 2: + print("test7: failed to read the namespace node") + sys.exit(1) + +# +# Test for a limit case: +# +f = str_io("""<a/>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test8") +ret = reader.Read() +if ret != 1: + print("test8: failed to read the node") + sys.exit(1) +if reader.Name() != "a" or reader.IsEmptyElement() != 1: + print("test8: failed to analyze the node") + sys.exit(1) +ret = reader.Read() +if ret != 0: + print("test8: failed to detect the EOF") + sys.exit(1) + +# +# Another test provided by Stéphane Bidoul and checked with C# +# +def tst_reader(s): + f = str_io(s) + input = libxml2.inputBuffer(f) + reader = input.newTextReader("tst") + res = "" + while reader.Read(): + res=res + "%s (%s) [%s] %d %d\n" % (reader.NodeType(),reader.Name(), + reader.Value(), reader.IsEmptyElement(), + reader.Depth()) + if reader.NodeType() == 1: # Element + while reader.MoveToNextAttribute(): + res = res + "-- %s (%s) [%s] %d %d\n" % (reader.NodeType(), + reader.Name(),reader.Value(), + reader.IsEmptyElement(), reader.Depth()) + return res + +doc="""<a><b b1="b1"/><c>content of c</c></a>""" +expect="""1 (a) [None] 0 0 +1 (b) [None] 1 1 +-- 2 (b1) [b1] 0 2 +1 (c) [None] 0 1 +3 (#text) [content of c] 0 2 +15 (c) [None] 0 1 +15 (a) [None] 0 0 +""" +res = tst_reader(doc) +if res != expect: + print("test5 failed") + print(res) + sys.exit(1) + +doc="""<test><b/><c/></test>""" +expect="""1 (test) [None] 0 0 +1 (b) [None] 1 1 +1 (c) [None] 1 1 +15 (test) [None] 0 0 +""" +res = tst_reader(doc) +if res != expect: + print("test9 failed") + print(res) + sys.exit(1) + +doc="""<a><b>bbb</b><c>ccc</c></a>""" +expect="""1 (a) [None] 0 0 +1 (b) [None] 0 1 +3 (#text) [bbb] 0 2 +15 (b) [None] 0 1 +1 (c) [None] 0 1 +3 (#text) [ccc] 0 2 +15 (c) [None] 0 1 +15 (a) [None] 0 0 +""" +res = tst_reader(doc) +if res != expect: + print("test10 failed") + print(res) + sys.exit(1) + +doc="""<test a="a"/>""" +expect="""1 (test) [None] 1 0 +-- 2 (a) [a] 0 1 +""" +res = tst_reader(doc) +if res != expect: + print("test11 failed") + print(res) + sys.exit(1) + +doc="""<test><a>aaa</a><b/></test>""" +expect="""1 (test) [None] 0 0 +1 (a) [None] 0 1 +3 (#text) [aaa] 0 2 +15 (a) [None] 0 1 +1 (b) [None] 1 1 +15 (test) [None] 0 0 +""" +res = tst_reader(doc) +if res != expect: + print("test12 failed") + print(res) + sys.exit(1) + +doc="""<test><p></p></test>""" +expect="""1 (test) [None] 0 0 +1 (p) [None] 0 1 +15 (p) [None] 0 1 +15 (test) [None] 0 0 +""" +res = tst_reader(doc) +if res != expect: + print("test13 failed") + print(res) + sys.exit(1) + +doc="""<p></p>""" +expect="""1 (p) [None] 0 0 +15 (p) [None] 0 0 +""" +res = tst_reader(doc) +if res != expect: + print("test14 failed") + print(res) + sys.exit(1) + +# +# test from bug #108801 +# +doc="""<?xml version="1.0" standalone="no"?> +<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" + "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [ +]> + +<article> +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +</article> +""" +expect="""10 (article) [None] 0 0 +1 (article) [None] 0 0 +3 (#text) [ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +] 0 1 +15 (article) [None] 0 0 +""" +res = tst_reader(doc) +if res != expect: + print("test15 failed") + print(res) + sys.exit(1) + +# +# cleanup for memory allocation counting +# +del f +del input +del reader + +# 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/libxml2/python/tests/reader2.py b/libxml2/python/tests/reader2.py new file mode 100755 index 0000000..8570575 --- /dev/null +++ b/libxml2/python/tests/reader2.py @@ -0,0 +1,265 @@ +#!/usr/bin/python -u +# +# this tests the DTD validation with the XmlTextReader interface +# +import sys +import glob +import string +import libxml2 +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# Memory debug specific +libxml2.debugMemory(1) + +err="" +expect="""../../test/valid/rss.xml:177: element rss: validity error : Element rss does not carry attribute version +</rss> + ^ +../../test/valid/xlink.xml:450: element termdef: validity error : ID dt-arc already defined + <p><termdef id="dt-arc" term="Arc">An <ter + ^ +../../test/valid/xlink.xml:530: validity error : attribute def line 199 references an unknown ID "dt-xlg" + +^ +""" +def callback(ctx, str): + global err + err = err + "%s" % (str) +libxml2.registerErrorHandler(callback, "") + +valid_files = glob.glob("../../test/valid/*.x*") +valid_files.sort() +for file in valid_files: + if file.find("t8") != -1: + continue + if file == "../../test/valid/rss.xml": + continue + if file == "../../test/valid/xlink.xml": + continue + reader = libxml2.newTextReaderFilename(file) + #print "%s:" % (file) + reader.SetParserProp(libxml2.PARSER_VALIDATE, 1) + ret = reader.Read() + while ret == 1: + ret = reader.Read() + if ret != 0: + print("Error parsing and validating %s" % (file)) + #sys.exit(1) + +if err != expect: + print(err) + +# +# another separate test based on Stephane Bidoul one +# +s = """ +<!DOCTYPE test [ +<!ELEMENT test (x,b)> +<!ELEMENT x (c)> +<!ELEMENT b (#PCDATA)> +<!ELEMENT c (#PCDATA)> +<!ENTITY x "<x><c>xxx</c></x>"> +]> +<test> + &x; + <b>bbb</b> +</test> +""" +expect="""10,test +1,test +14,#text +1,x +1,c +3,#text +15,c +15,x +14,#text +1,b +3,#text +15,b +14,#text +15,test +""" +res="" +err="" + +input = libxml2.inputBuffer(str_io(s)) +reader = input.newTextReader("test2") +reader.SetParserProp(libxml2.PARSER_LOADDTD,1) +reader.SetParserProp(libxml2.PARSER_DEFAULTATTRS,1) +reader.SetParserProp(libxml2.PARSER_SUBST_ENTITIES,1) +reader.SetParserProp(libxml2.PARSER_VALIDATE,1) +while reader.Read() == 1: + res = res + "%s,%s\n" % (reader.NodeType(),reader.Name()) + +if res != expect: + print("test2 failed: unexpected output") + print(res) + sys.exit(1) +if err != "": + print("test2 failed: validation error found") + print(err) + sys.exit(1) + +# +# Another test for external entity parsing and validation +# + +s = """<!DOCTYPE test [ +<!ELEMENT test (x)> +<!ELEMENT x (#PCDATA)> +<!ENTITY e SYSTEM "tst.ent"> +]> +<test> + &e; +</test> +""" +tst_ent = """<x>hello</x>""" +expect="""10 test +1 test +14 #text +1 x +3 #text +15 x +14 #text +15 test +""" +res="" + +def myResolver(URL, ID, ctxt): + if URL == "tst.ent": + return(str_io(tst_ent)) + return None + +libxml2.setEntityLoader(myResolver) + +input = libxml2.inputBuffer(str_io(s)) +reader = input.newTextReader("test3") +reader.SetParserProp(libxml2.PARSER_LOADDTD,1) +reader.SetParserProp(libxml2.PARSER_DEFAULTATTRS,1) +reader.SetParserProp(libxml2.PARSER_SUBST_ENTITIES,1) +reader.SetParserProp(libxml2.PARSER_VALIDATE,1) +while reader.Read() == 1: + res = res + "%s %s\n" % (reader.NodeType(),reader.Name()) + +if res != expect: + print("test3 failed: unexpected output") + print(res) + sys.exit(1) +if err != "": + print("test3 failed: validation error found") + print(err) + sys.exit(1) + +# +# Another test for recursive entity parsing, validation, and replacement of +# entities, making sure the entity ref node doesn't show up in that case +# + +s = """<!DOCTYPE test [ +<!ELEMENT test (x, x)> +<!ELEMENT x (y)> +<!ELEMENT y (#PCDATA)> +<!ENTITY x "<x>&y;</x>"> +<!ENTITY y "<y>yyy</y>"> +]> +<test> + &x; + &x; +</test>""" +expect="""10 test 0 +1 test 0 +14 #text 1 +1 x 1 +1 y 2 +3 #text 3 +15 y 2 +15 x 1 +14 #text 1 +1 x 1 +1 y 2 +3 #text 3 +15 y 2 +15 x 1 +14 #text 1 +15 test 0 +""" +res="" +err="" + +input = libxml2.inputBuffer(str_io(s)) +reader = input.newTextReader("test4") +reader.SetParserProp(libxml2.PARSER_LOADDTD,1) +reader.SetParserProp(libxml2.PARSER_DEFAULTATTRS,1) +reader.SetParserProp(libxml2.PARSER_SUBST_ENTITIES,1) +reader.SetParserProp(libxml2.PARSER_VALIDATE,1) +while reader.Read() == 1: + res = res + "%s %s %d\n" % (reader.NodeType(),reader.Name(),reader.Depth()) + +if res != expect: + print("test4 failed: unexpected output") + print(res) + sys.exit(1) +if err != "": + print("test4 failed: validation error found") + print(err) + sys.exit(1) + +# +# The same test but without entity substitution this time +# + +s = """<!DOCTYPE test [ +<!ELEMENT test (x, x)> +<!ELEMENT x (y)> +<!ELEMENT y (#PCDATA)> +<!ENTITY x "<x>&y;</x>"> +<!ENTITY y "<y>yyy</y>"> +]> +<test> + &x; + &x; +</test>""" +expect="""10 test 0 +1 test 0 +14 #text 1 +5 x 1 +14 #text 1 +5 x 1 +14 #text 1 +15 test 0 +""" +res="" +err="" + +input = libxml2.inputBuffer(str_io(s)) +reader = input.newTextReader("test5") +reader.SetParserProp(libxml2.PARSER_VALIDATE,1) +while reader.Read() == 1: + res = res + "%s %s %d\n" % (reader.NodeType(),reader.Name(),reader.Depth()) + +if res != expect: + print("test5 failed: unexpected output") + print(res) +if err != "": + print("test5 failed: validation error found") + print(err) + +# +# cleanup +# +del input +del reader + +# 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/libxml2/python/tests/reader3.py b/libxml2/python/tests/reader3.py new file mode 100755 index 0000000..4302b6c --- /dev/null +++ b/libxml2/python/tests/reader3.py @@ -0,0 +1,160 @@ +#!/usr/bin/python -u +# +# this tests the entities substitutions with the XmlTextReader interface +# +import sys +import libxml2 +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +docstr="""<?xml version='1.0'?> +<!DOCTYPE doc [ +<!ENTITY tst "<p>test</p>"> +]> +<doc>&tst;</doc>""" + +# Memory debug specific +libxml2.debugMemory(1) + +# +# First test, normal don't substitute entities. +# +f = str_io(docstr) +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test_noent") +ret = reader.Read() +if ret != 1: + print("Error reading to root") + sys.exit(1) +if reader.Name() == "doc" or reader.NodeType() == 10: + ret = reader.Read() +if ret != 1: + print("Error reading to root") + sys.exit(1) +if reader.Name() != "doc" or reader.NodeType() != 1: + print("test_normal: Error reading the root element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_normal: Error reading to the entity") + sys.exit(1) +if reader.Name() != "tst" or reader.NodeType() != 5: + print("test_normal: Error reading the entity") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_normal: Error reading to the end of root") + sys.exit(1) +if reader.Name() != "doc" or reader.NodeType() != 15: + print("test_normal: Error reading the end of the root element") + sys.exit(1) +ret = reader.Read() +if ret != 0: + print("test_normal: Error detecting the end") + sys.exit(1) + +# +# Second test, completely substitute the entities. +# +f = str_io(docstr) +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test_noent") +reader.SetParserProp(libxml2.PARSER_SUBST_ENTITIES, 1) +ret = reader.Read() +if ret != 1: + print("Error reading to root") + sys.exit(1) +if reader.Name() == "doc" or reader.NodeType() == 10: + ret = reader.Read() +if ret != 1: + print("Error reading to root") + sys.exit(1) +if reader.Name() != "doc" or reader.NodeType() != 1: + print("test_noent: Error reading the root element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_noent: Error reading to the entity content") + sys.exit(1) +if reader.Name() != "p" or reader.NodeType() != 1: + print("test_noent: Error reading the p element from entity") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_noent: Error reading to the text node") + sys.exit(1) +if reader.NodeType() != 3 or reader.Value() != "test": + print("test_noent: Error reading the text node") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_noent: Error reading to the end of p element") + sys.exit(1) +if reader.Name() != "p" or reader.NodeType() != 15: + print("test_noent: Error reading the end of the p element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_noent: Error reading to the end of root") + sys.exit(1) +if reader.Name() != "doc" or reader.NodeType() != 15: + print("test_noent: Error reading the end of the root element") + sys.exit(1) +ret = reader.Read() +if ret != 0: + print("test_noent: Error detecting the end") + sys.exit(1) + +# +# third test, crazy stuff about empty element in external parsed entities +# +s = """<!DOCTYPE struct [ +<!ENTITY simplestruct2.ent SYSTEM "simplestruct2.ent"> +]> +<struct>&simplestruct2.ent;</struct> +""" +expect="""10 struct 0 0 +1 struct 0 0 +1 descr 1 1 +15 struct 0 0 +""" +res="" +simplestruct2_ent="""<descr/>""" + +def myResolver(URL, ID, ctxt): + if URL == "simplestruct2.ent": + return(str_io(simplestruct2_ent)) + return None + +libxml2.setEntityLoader(myResolver) + +input = libxml2.inputBuffer(str_io(s)) +reader = input.newTextReader("test3") +reader.SetParserProp(libxml2.PARSER_SUBST_ENTITIES,1) +while reader.Read() == 1: + res = res + "%s %s %d %d\n" % (reader.NodeType(),reader.Name(), + reader.Depth(),reader.IsEmptyElement()) + +if res != expect: + print("test3 failed: unexpected output") + print(res) + sys.exit(1) + +# +# cleanup +# +del f +del input +del reader + +# 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/libxml2/python/tests/reader4.py b/libxml2/python/tests/reader4.py new file mode 100755 index 0000000..0bb3e3f --- /dev/null +++ b/libxml2/python/tests/reader4.py @@ -0,0 +1,50 @@ +#!/usr/bin/python -u +# +# this tests the basic APIs of the XmlTextReader interface +# +import libxml2 +import sys +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# Memory debug specific +libxml2.debugMemory(1) + +def tst_reader(s): + f = str_io(s) + input = libxml2.inputBuffer(f) + reader = input.newTextReader("tst") + res = "" + while reader.Read(): + res=res + "%s (%s) [%s] %d\n" % (reader.NodeType(),reader.Name(), + reader.Value(), reader.IsEmptyElement()) + if reader.NodeType() == 1: # Element + while reader.MoveToNextAttribute(): + res = res + "-- %s (%s) [%s]\n" % (reader.NodeType(), + reader.Name(),reader.Value()) + return res + +expect="""1 (test) [None] 0 +1 (b) [None] 1 +1 (c) [None] 1 +15 (test) [None] 0 +""" + +res = tst_reader("""<test><b/><c/></test>""") + +if res != expect: + print("Did not get the expected error message:") + print(res) + sys.exit(1) + +# 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/libxml2/python/tests/reader5.py b/libxml2/python/tests/reader5.py new file mode 100755 index 0000000..82d0dae --- /dev/null +++ b/libxml2/python/tests/reader5.py @@ -0,0 +1,47 @@ +#!/usr/bin/python -u +# +# this tests the Expand() API of the xmlTextReader interface +# this extract the Dragon bibliography entries from the XML specification +# +import libxml2 +import sys + +# Memory debug specific +libxml2.debugMemory(1) + +expect="""<bibl id="Aho" key="Aho/Ullman">Aho, Alfred V., +Ravi Sethi, and Jeffrey D. Ullman. +<emph>Compilers: Principles, Techniques, and Tools</emph>. +Reading: Addison-Wesley, 1986, rpt. corr. 1988.</bibl>""" + +f = open('../../test/valid/REC-xml-19980210.xml', 'rb') +input = libxml2.inputBuffer(f) +reader = input.newTextReader("REC") +res="" +while reader.Read() > 0: + while reader.Name() == 'bibl': + node = reader.Expand() # expand the subtree + if node.xpathEval("@id = 'Aho'"): # use XPath on it + res = res + node.serialize() + if reader.Next() != 1: # skip the subtree + break; + +if res != expect: + print("Error: didn't get the expected output") + print("got '%s'" % (res)) + print("expected '%s'" % (expect)) + + +# +# cleanup +# +del input +del reader + +# 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/libxml2/python/tests/reader6.py b/libxml2/python/tests/reader6.py new file mode 100755 index 0000000..ef33b18 --- /dev/null +++ b/libxml2/python/tests/reader6.py @@ -0,0 +1,128 @@ +#!/usr/bin/python -u +# +# this tests the entities substitutions with the XmlTextReader interface +# +import sys +import libxml2 +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +schema="""<element name="foo" xmlns="http://relaxng.org/ns/structure/1.0" + datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> + <oneOrMore> + <element name="label"> + <text/> + </element> + <optional> + <element name="opt"> + <empty/> + </element> + </optional> + <element name="item"> + <data type="byte"/> + </element> + </oneOrMore> +</element> +""" +# Memory debug specific +libxml2.debugMemory(1) + +# +# Parse the Relax NG Schemas +# +rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema)) +rngs = rngp.relaxNGParse() +del rngp + +# +# Parse and validate the correct document +# +docstr="""<foo> +<label>some text</label> +<item>100</item> +</foo>""" + +f = str_io(docstr) +input = libxml2.inputBuffer(f) +reader = input.newTextReader("correct") +reader.RelaxNGSetSchema(rngs) +ret = reader.Read() +while ret == 1: + ret = reader.Read() + +if ret != 0: + print("Error parsing the document") + sys.exit(1) + +if reader.IsValid() != 1: + print("Document failed to validate") + sys.exit(1) + +# +# Parse and validate the incorrect document +# +docstr="""<foo> +<label>some text</label> +<item>1000</item> +</foo>""" + +err="" +# RNG errors are not as good as before , TODO +#expect="""RNG validity error: file error line 3 element text +#Type byte doesn't allow value '1000' +#RNG validity error: file error line 3 element text +#Error validating datatype byte +#RNG validity error: file error line 3 element text +#Element item failed to validate content +#""" +expect="""Type byte doesn't allow value '1000' +Error validating datatype byte +Element item failed to validate content +""" + +def callback(ctx, str): + global err + err = err + "%s" % (str) +libxml2.registerErrorHandler(callback, "") + +f = str_io(docstr) +input = libxml2.inputBuffer(f) +reader = input.newTextReader("error") +reader.RelaxNGSetSchema(rngs) +ret = reader.Read() +while ret == 1: + ret = reader.Read() + +if ret != 0: + print("Error parsing the document") + sys.exit(1) + +if reader.IsValid() != 0: + print("Document failed to detect the validation error") + sys.exit(1) + +if err != expect: + print("Did not get the expected error message:") + print(err) + sys.exit(1) + +# +# cleanup +# +del f +del input +del reader +del rngs +libxml2.relaxNGCleanupTypes() + +# 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/libxml2/python/tests/reader7.py b/libxml2/python/tests/reader7.py new file mode 100755 index 0000000..c88e370 --- /dev/null +++ b/libxml2/python/tests/reader7.py @@ -0,0 +1,101 @@ +#!/usr/bin/python -u +# +# this tests the entities substitutions with the XmlTextReader interface +# +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +result = "" +def processNode(reader): + global result + + result = result + "%d %d %s %d\n" % (reader.Depth(), reader.NodeType(), + reader.Name(), reader.IsEmptyElement()) + +# +# Parse a document testing the readerForxxx API +# +docstr="""<foo> +<label>some text</label> +<item>100</item> +</foo>""" +expect="""0 1 foo 0 +1 14 #text 0 +1 1 label 0 +2 3 #text 0 +1 15 label 0 +1 14 #text 0 +1 1 item 0 +2 3 #text 0 +1 15 item 0 +1 14 #text 0 +0 15 foo 0 +""" +result = "" + +reader = libxml2.readerForDoc(docstr, "test1", None, 0) +ret = reader.Read() +while ret == 1: + processNode(reader) + ret = reader.Read() + +if ret != 0: + print("Error parsing the document test1") + sys.exit(1) + +if result != expect: + print("Unexpected result for test1") + print(result) + sys.exit(1) + +# +# Reuse the reader for another document testing the ReaderNewxxx API +# +docstr="""<foo> +<label>some text</label> +<item>1000</item> +</foo>""" +expect="""0 1 foo 0 +1 14 #text 0 +1 1 label 0 +2 3 #text 0 +1 15 label 0 +1 14 #text 0 +1 1 item 0 +2 3 #text 0 +1 15 item 0 +1 14 #text 0 +0 15 foo 0 +""" +result = "" + +reader.NewDoc(docstr, "test2", None, 0) +ret = reader.Read() +while ret == 1: + processNode(reader) + ret = reader.Read() + +if ret != 0: + print("Error parsing the document test2") + sys.exit(1) + +if result != expect: + print("Unexpected result for test2") + print(result) + sys.exit(1) + +# +# cleanup +# +del reader + +# 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/libxml2/python/tests/reader8.py b/libxml2/python/tests/reader8.py new file mode 100755 index 0000000..de2dcd6 --- /dev/null +++ b/libxml2/python/tests/reader8.py @@ -0,0 +1,36 @@ +#!/usr/bin/python -u +# +# this tests the entities substitutions with the XmlTextReader interface +# +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +# +# Parse a document testing the Close() API +# +docstr="""<foo> +<label>some text</label> +<item>100</item> +</foo>""" + +reader = libxml2.readerForDoc(docstr, "test1", None, 0) +ret = reader.Read() +ret = reader.Read() +ret = reader.Close() + +if ret != 0: + print("Error closing the document test1") + sys.exit(1) + +del reader + +# 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/libxml2/python/tests/readererr.py b/libxml2/python/tests/readererr.py new file mode 100755 index 0000000..c8ceba5 --- /dev/null +++ b/libxml2/python/tests/readererr.py @@ -0,0 +1,56 @@ +#!/usr/bin/python -u +# +# this tests the basic APIs of the XmlTextReader interface +# +import libxml2 +import sys +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# Memory debug specific +libxml2.debugMemory(1) + +expect="""--> (3) test1:1:xmlns: URI foo is not absolute +--> (4) test1:1:Opening and ending tag mismatch: c line 1 and a +""" +err="" +def myErrorHandler(arg,msg,severity,locator): + global err + err = err + "%s (%d) %s:%d:%s" % (arg,severity,locator.BaseURI(),locator.LineNumber(),msg) + +f = str_io("""<a xmlns="foo"><b b1="b1"/><c>content of c</a>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test1") +reader.SetErrorHandler(myErrorHandler,"-->") +while reader.Read() == 1: + pass + +if err != expect: + print("error") + print("received %s" %(err)) + print("expected %s" %(expect)) + sys.exit(1) + +reader.SetErrorHandler(None,None) +if reader.GetErrorHandler() != (None,None): + print("GetErrorHandler failed") + sys.exit(1) + +# +# cleanup for memory allocation counting +# +del f +del input +del reader + +# 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/libxml2/python/tests/readernext.py b/libxml2/python/tests/readernext.py new file mode 100755 index 0000000..fcb9ae3 --- /dev/null +++ b/libxml2/python/tests/readernext.py @@ -0,0 +1,86 @@ +#!/usr/bin/python -u +# -*- coding: ISO-8859-1 -*- +# +# this tests the next API of the XmlTextReader interface +# +import libxml2 +import sys +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# Memory debug specific +libxml2.debugMemory(1) + +f = str_io("""<a><b><c /></b><d>content of d</d></a>""") +input = libxml2.inputBuffer(f) +reader = input.newTextReader("test_next") +ret = reader.Read() +if ret != 1: + print("test_next: Error reading to first element") + sys.exit(1) +if reader.Name() != "a" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 1 or reader.HasAttributes() != 0: + print("test_next: Error reading the first element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_next: Error reading to second element") + sys.exit(1) +if reader.Name() != "b" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 1 or reader.HasAttributes() != 0: + print("test_next: Error reading the second element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_next: Error reading to third element") + sys.exit(1) +if reader.Name() != "c" or reader.NodeType() != 1 or \ + reader.HasAttributes() != 0: + print("test_next: Error reading the third element") + sys.exit(1) +ret = reader.Read() +if ret != 1: + print("test_next: Error reading to end of third element") + sys.exit(1) +if reader.Name() != "b" or reader.NodeType() != 15: + print("test_next: Error reading to end of second element") + sys.exit(1) +ret = reader.Next() +if ret != 1: + print("test_next: Error moving to third element") + sys.exit(1) +if reader.Name() != "d" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 1 or reader.HasAttributes() != 0: + print("test_next: Error reading third element") + sys.exit(1) +ret = reader.Next() +if ret != 1: + print("test_next: Error reading to end of first element") + sys.exit(1) +if reader.Name() != "a" or reader.IsEmptyElement() != 0 or \ + reader.NodeType() != 15 or reader.HasAttributes() != 0: + print("test_next: Error reading the end of first element") + sys.exit(1) +ret = reader.Read() +if ret != 0: + print("test_next: Error reading to end of document") + sys.exit(1) + +# +# cleanup for memory allocation counting +# +del f +del input +del reader + +# 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/libxml2/python/tests/regexp.py b/libxml2/python/tests/regexp.py new file mode 100755 index 0000000..a03e459 --- /dev/null +++ b/libxml2/python/tests/regexp.py @@ -0,0 +1,32 @@ +#!/usr/bin/python -u +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +re = libxml2.regexpCompile("a|b") +if re.regexpExec("a") != 1: + print("error checking 'a'") + sys.exit(1) +if re.regexpExec("b") != 1: + print("error checking 'b'") + sys.exit(1) +if re.regexpExec("ab") != 0: + print("error checking 'ab'") + sys.exit(1) +if re.regexpExec("") != 0: + print("error checking 'ab'") + sys.exit(1) +if re.regexpIsDeterminist() != 1: + print("error checking determinism") + sys.exit(1) +del re + + +# 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/libxml2/python/tests/relaxng.py b/libxml2/python/tests/relaxng.py new file mode 100755 index 0000000..fa3d327 --- /dev/null +++ b/libxml2/python/tests/relaxng.py @@ -0,0 +1,48 @@ +#!/usr/bin/python -u +import libxml2 +import sys + +# Memory debug specific +libxml2.debugMemory(1) + +schema="""<?xml version="1.0"?> +<element name="foo" + xmlns="http://relaxng.org/ns/structure/1.0" + xmlns:a="http://relaxng.org/ns/annotation/1.0" + xmlns:ex1="http://www.example.com/n1" + xmlns:ex2="http://www.example.com/n2"> + <a:documentation>A foo element.</a:documentation> + <element name="ex1:bar1"> + <empty/> + </element> + <element name="ex2:bar2"> + <empty/> + </element> +</element> +""" +instance="""<?xml version="1.0"?> +<foo><pre1:bar1 xmlns:pre1="http://www.example.com/n1"/><pre2:bar2 xmlns:pre2="http://www.example.com/n2"/></foo>""" + +rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema)) +rngs = rngp.relaxNGParse() +ctxt = rngs.relaxNGNewValidCtxt() +doc = libxml2.parseDoc(instance) +ret = doc.relaxNGValidateDoc(ctxt) +if ret != 0: + print("error doing RelaxNG validation") + sys.exit(1) + +doc.freeDoc() +del rngp +del rngs +del ctxt +libxml2.relaxNGCleanupTypes() + +# 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/libxml2/python/tests/resolver.py b/libxml2/python/tests/resolver.py new file mode 100755 index 0000000..6f21f52 --- /dev/null +++ b/libxml2/python/tests/resolver.py @@ -0,0 +1,44 @@ +#!/usr/bin/python -u +import sys +import libxml2 +try: + import StringIO + str_io = StringIO.StringIO +except: + import io + str_io = io.StringIO + +# Memory debug specific +libxml2.debugMemory(1) + +def myResolver(URL, ID, ctxt): + return(str_io("<foo/>")) + +libxml2.setEntityLoader(myResolver) + +doc = libxml2.parseFile("doesnotexist.xml") +root = doc.children +if root.name != "foo": + print("root element name error") + sys.exit(1) +doc.freeDoc() + +i = 0 +while i < 5000: + doc = libxml2.parseFile("doesnotexist.xml") + root = doc.children + if root.name != "foo": + print("root element name error") + sys.exit(1) + doc.freeDoc() + i = i + 1 + + +# 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/libxml2/python/tests/schema.py b/libxml2/python/tests/schema.py new file mode 100755 index 0000000..8089272 --- /dev/null +++ b/libxml2/python/tests/schema.py @@ -0,0 +1,52 @@ +#!/usr/bin/python -u +import libxml2 +import sys + +# Memory debug specific +libxml2.debugMemory(1) + +schema="""<?xml version="1.0" encoding="iso-8859-1"?> +<schema xmlns = "http://www.w3.org/2001/XMLSchema"> + <element name = "Customer"> + <complexType> + <sequence> + <element name = "FirstName" type = "string" /> + <element name = "MiddleInitial" type = "string" /> + <element name = "LastName" type = "string" /> + </sequence> + <attribute name = "customerID" type = "integer" /> + </complexType> + </element> +</schema>""" + +instance="""<?xml version="1.0" encoding="iso-8859-1"?> +<Customer customerID = "24332"> + <FirstName>Raymond</FirstName> + <MiddleInitial>G</MiddleInitial> + <LastName>Bayliss</LastName> +</Customer> +""" + +ctxt_parser = libxml2.schemaNewMemParserCtxt(schema, len(schema)) +ctxt_schema = ctxt_parser.schemaParse() +ctxt_valid = ctxt_schema.schemaNewValidCtxt() +doc = libxml2.parseDoc(instance) +ret = doc.schemaValidateDoc(ctxt_valid) +if ret != 0: + print("error doing schema validation") + sys.exit(1) + +doc.freeDoc() +del ctxt_parser +del ctxt_schema +del ctxt_valid +libxml2.schemaCleanupTypes() + +# 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/libxml2/python/tests/serialize.py b/libxml2/python/tests/serialize.py new file mode 100755 index 0000000..80b901a --- /dev/null +++ b/libxml2/python/tests/serialize.py @@ -0,0 +1,150 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +# +# Testing XML document serialization +# +doc = libxml2.parseDoc("""<root><foo>hello</foo></root>""") +str = doc.serialize() +if str != """<?xml version="1.0"?> +<root><foo>hello</foo></root> +""": + print("error serializing XML document 1") + sys.exit(1) +str = doc.serialize("iso-8859-1") +if str != """<?xml version="1.0" encoding="iso-8859-1"?> +<root><foo>hello</foo></root> +""": + print("error serializing XML document 2") + sys.exit(1) +str = doc.serialize(format=1) +if str != """<?xml version="1.0"?> +<root> + <foo>hello</foo> +</root> +""": + print("error serializing XML document 3") + sys.exit(1) +str = doc.serialize("iso-8859-1", 1) +if str != """<?xml version="1.0" encoding="iso-8859-1"?> +<root> + <foo>hello</foo> +</root> +""": + print("error serializing XML document 4") + sys.exit(1) + +# +# Test serializing a subnode +# +root = doc.getRootElement() +str = root.serialize() +if str != """<root><foo>hello</foo></root>""": + print("error serializing XML root 1") + sys.exit(1) +str = root.serialize("iso-8859-1") +if str != """<root><foo>hello</foo></root>""": + print("error serializing XML root 2") + sys.exit(1) +str = root.serialize(format=1) +if str != """<root> + <foo>hello</foo> +</root>""": + print("error serializing XML root 3") + sys.exit(1) +str = root.serialize("iso-8859-1", 1) +if str != """<root> + <foo>hello</foo> +</root>""": + print("error serializing XML root 4") + sys.exit(1) +doc.freeDoc() + +# +# Testing HTML document serialization +# +doc = libxml2.htmlParseDoc("""<html><head><title>Hello</title><body><p>hello</body></html>""", None) +str = doc.serialize() +if str != """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> +<html><head><title>Hello</title></head><body><p>hello</p></body></html> +""": + print("error serializing HTML document 1") + sys.exit(1) +str = doc.serialize("ISO-8859-1") +if str != """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> +<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Hello</title></head><body><p>hello</p></body></html> +""": + print("error serializing HTML document 2") + sys.exit(1) +str = doc.serialize(format=1) +if str != """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<title>Hello</title> +</head> +<body><p>hello</p></body> +</html> +""": + print("error serializing HTML document 3") + sys.exit(1) +str = doc.serialize("iso-8859-1", 1) +if str != """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd"> +<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<title>Hello</title> +</head> +<body><p>hello</p></body> +</html> +""": + print("error serializing HTML document 4") + sys.exit(1) + +# +# Test serializing a subnode +# +doc.htmlSetMetaEncoding(None) +root = doc.getRootElement() +str = root.serialize() +if str != """<html><head><title>Hello</title></head><body><p>hello</p></body></html>""": + print("error serializing HTML root 1") + sys.exit(1) +str = root.serialize("ISO-8859-1") +if str != """<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>Hello</title></head><body><p>hello</p></body></html>""": + print("error serializing HTML root 2") + sys.exit(1) +str = root.serialize(format=1) +if str != """<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<title>Hello</title> +</head> +<body><p>hello</p></body> +</html>""": + print("error serializing HTML root 3") + sys.exit(1) +str = root.serialize("iso-8859-1", 1) +if str != """<html> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> +<title>Hello</title> +</head> +<body><p>hello</p></body> +</html>""": + print("error serializing HTML root 4") + sys.exit(1) + +doc.freeDoc() + +# 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/libxml2/python/tests/sync.py b/libxml2/python/tests/sync.py new file mode 100755 index 0000000..5a8609e --- /dev/null +++ b/libxml2/python/tests/sync.py @@ -0,0 +1,138 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +log = "" + +class callback: + def startDocument(self): + global log + log = log + "startDocument:" + + def endDocument(self): + global log + log = log + "endDocument:" + + def startElement(self, tag, attrs): + global log + log = log + "startElement %s %s:" % (tag, attrs) + + def endElement(self, tag): + global log + log = log + "endElement %s:" % (tag) + + def characters(self, data): + global log + log = log + "characters: %s:" % (data) + + def warning(self, msg): + global log + log = log + "warning: %s:" % (msg) + + def error(self, msg): + global log + log = log + "error: %s:" % (msg) + + def fatalError(self, msg): + global log + log = log + "fatalError: %s:" % (msg) + +handler = callback() + +log="" +chunk="""<foo><bar2/>""" +ctxt = libxml2.createPushParser(handler, None, 0, "test.xml") +ctxt.parseChunk(chunk, len(chunk), 0) +ctxt=None + +reference = "startDocument:startElement foo None:startElement bar2 None:endElement bar2:" +if log != reference: + print("Error got: %s" % log) + print("Expected: %s" % reference) + sys.exit(1) + +log="" +chunk="""<foo><bar2></bar2>""" +ctxt = libxml2.createPushParser(handler, None, 0, "test.xml") +ctxt.parseChunk(chunk, len(chunk), 0) +ctxt=None + +reference = "startDocument:startElement foo None:startElement bar2 None:endElement bar2:" +if log != reference: + print("Error got: %s" % log) + print("Expected: %s" % reference) + sys.exit(1) + +log="" +chunk="""<foo><bar2>""" +ctxt = libxml2.createPushParser(handler, None, 0, "test.xml") +ctxt.parseChunk(chunk, len(chunk), 0) +ctxt=None + +reference = "startDocument:startElement foo None:startElement bar2 None:" +if log != reference: + print("Error got: %s" % log) + print("Expected: %s" % reference) + sys.exit(1) + +log="" +chunk="""<foo><bar2 a="1" b='2' />""" +ctxt = libxml2.createPushParser(handler, None, 0, "test.xml") +ctxt.parseChunk(chunk, len(chunk), 0) +ctxt=None + +reference1 = "startDocument:startElement foo None:startElement bar2 {'a': '1', 'b': '2'}:endElement bar2:" +reference2 = "startDocument:startElement foo None:startElement bar2 {'b': '2', 'a': '1'}:endElement bar2:" +if log not in (reference1, reference2): + print("Error got: %s" % log) + print("Expected: %s" % reference) + sys.exit(1) + +log="" +chunk="""<foo><bar2 a="1" b='2' >""" +ctxt = libxml2.createPushParser(handler, None, 0, "test.xml") +ctxt.parseChunk(chunk, len(chunk), 0) +ctxt=None + +reference1 = "startDocument:startElement foo None:startElement bar2 {'a': '1', 'b': '2'}:" +reference2 = "startDocument:startElement foo None:startElement bar2 {'b': '2', 'a': '1'}:" +if log not in (reference1, reference2): + print("Error got: %s" % log) + print("Expected: %s" % reference) + sys.exit(1) + +log="" +chunk="""<foo><bar2 a="1" b='2' ></bar2>""" +ctxt = libxml2.createPushParser(handler, None, 0, "test.xml") +ctxt.parseChunk(chunk, len(chunk), 0) +ctxt=None + +reference1 = "startDocument:startElement foo None:startElement bar2 {'a': '1', 'b': '2'}:endElement bar2:" +reference2 = "startDocument:startElement foo None:startElement bar2 {'b': '2', 'a': '1'}:endElement bar2:" +if log not in (reference1, reference2): + print("Error got: %s" % log) + print("Expected: %s" % reference) + sys.exit(1) + +log="" +chunk="""<foo><bar2 a="b='1' />""" +ctxt = libxml2.createPushParser(handler, None, 0, "test.xml") +ctxt.parseChunk(chunk, len(chunk), 0) +ctxt=None + +reference = "startDocument:startElement foo None:" +if log != reference: + print("Error got: %s" % log) + print("Expected: %s" % reference) + sys.exit(1) + +# 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/libxml2/python/tests/test.dtd b/libxml2/python/tests/test.dtd new file mode 100644 index 0000000..b61b438 --- /dev/null +++ b/libxml2/python/tests/test.dtd @@ -0,0 +1 @@ +<!ELEMENT foo EMPTY> diff --git a/libxml2/python/tests/thread2.py b/libxml2/python/tests/thread2.py new file mode 100755 index 0000000..2749eb0 --- /dev/null +++ b/libxml2/python/tests/thread2.py @@ -0,0 +1,99 @@ +#!/usr/bin/python -u +import string, sys, time +try: + from _thread import get_ident +except: + from thread import get_ident +from threading import Thread, Lock + +import libxml2 + +THREADS_COUNT = 15 + +failed = 0 + +class ErrorHandler: + + def __init__(self): + self.errors = [] + self.lock = Lock() + + def handler(self,ctx,str): + self.lock.acquire() + self.errors.append(str) + self.lock.release() + +def getLineNumbersDefault(): + old = libxml2.lineNumbersDefault(0) + libxml2.lineNumbersDefault(old) + return old + +def test(expectedLineNumbersDefault): + time.sleep(1) + global failed + # check a per thread-global + if expectedLineNumbersDefault != getLineNumbersDefault(): + failed = 1 + print("FAILED to obtain correct value for " \ + "lineNumbersDefault in thread %d" % get_ident()) + # check ther global error handler + # (which is NOT per-thread in the python bindings) + try: + doc = libxml2.parseFile("bad.xml") + except: + pass + else: + assert "failed" + +# global error handler +eh = ErrorHandler() +libxml2.registerErrorHandler(eh.handler,"") + +# set on the main thread only +libxml2.lineNumbersDefault(1) +test(1) +ec = len(eh.errors) +if ec == 0: + print("FAILED: should have obtained errors") + sys.exit(1) + +ts = [] +for i in range(THREADS_COUNT): + # expect 0 for lineNumbersDefault because + # the new value has been set on the main thread only + ts.append(Thread(target=test,args=(0,))) +for t in ts: + t.start() +for t in ts: + t.join() + +if len(eh.errors) != ec+THREADS_COUNT*ec: + print("FAILED: did not obtain the correct number of errors") + sys.exit(1) + +# set lineNumbersDefault for future new threads +libxml2.thrDefLineNumbersDefaultValue(1) +ts = [] +for i in range(THREADS_COUNT): + # expect 1 for lineNumbersDefault + ts.append(Thread(target=test,args=(1,))) +for t in ts: + t.start() +for t in ts: + t.join() + +if len(eh.errors) != ec+THREADS_COUNT*ec*2: + print("FAILED: did not obtain the correct number of errors") + sys.exit(1) + +if failed: + print("FAILED") + sys.exit(1) + +# 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/libxml2/python/tests/tst.py b/libxml2/python/tests/tst.py new file mode 100755 index 0000000..57a7318 --- /dev/null +++ b/libxml2/python/tests/tst.py @@ -0,0 +1,28 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +doc = libxml2.parseFile("tst.xml") +if doc.name != "tst.xml": + print("doc.name failed") + sys.exit(1) +root = doc.children +if root.name != "doc": + print("root.name failed") + sys.exit(1) +child = root.children +if child.name != "foo": + print("child.name failed") + sys.exit(1) +doc.freeDoc() + +# 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/libxml2/python/tests/tst.xml b/libxml2/python/tests/tst.xml new file mode 100644 index 0000000..751d46d --- /dev/null +++ b/libxml2/python/tests/tst.xml @@ -0,0 +1 @@ +<doc><foo>bar</foo></doc> diff --git a/libxml2/python/tests/tstLastError.py b/libxml2/python/tests/tstLastError.py new file mode 100755 index 0000000..d5f9be7 --- /dev/null +++ b/libxml2/python/tests/tstLastError.py @@ -0,0 +1,82 @@ +#!/usr/bin/python -u +import sys, unittest + +import libxml2 + +class TestCase(unittest.TestCase): + + def runTest(self): + self.test1() + self.test2() + + 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),)) + else: + print("OK") + + 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=libxml2.XML_FROM_IO, + code=libxml2.XML_IO_LOAD_ERROR, + message='failed to load external entity "dummy.xml"\n', + level=libxml2.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=libxml2.XML_FROM_PARSER, + code=libxml2.XML_ERR_TAG_NOT_FINISHED, + message='Premature end of data in tag x line 1\n', + level=libxml2.XML_ERR_FATAL, + file='dummy.xml', + line=3) + +if __name__ == "__main__": + test = TestCase() + test.setUp() + test.test1() + test.test2() + test.tearDown() diff --git a/libxml2/python/tests/tstURI.py b/libxml2/python/tests/tstURI.py new file mode 100755 index 0000000..e4d58af --- /dev/null +++ b/libxml2/python/tests/tstURI.py @@ -0,0 +1,41 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +uri = libxml2.parseURI("http://example.org:8088/foo/bar?query=simple#fragid") +if uri.scheme() != 'http': + print("Error parsing URI: wrong scheme") + sys.exit(1) +if uri.server() != 'example.org': + print("Error parsing URI: wrong server") + sys.exit(1) +if uri.port() != 8088: + print("Error parsing URI: wrong port") + sys.exit(1) +if uri.path() != '/foo/bar': + print("Error parsing URI: wrong path") + sys.exit(1) +if uri.query() != 'query=simple': + print("Error parsing URI: wrong query") + sys.exit(1) +if uri.fragment() != 'fragid': + print("Error parsing URI: wrong query") + sys.exit(1) +uri.setScheme("https") +uri.setPort(223) +uri.setFragment(None) +result=uri.saveUri() +if result != "https://example.org:223/foo/bar?query=simple": + print("Error modifying or saving the URI") +uri = None + +# 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/libxml2/python/tests/tstmem.py b/libxml2/python/tests/tstmem.py new file mode 100755 index 0000000..6b34cf3 --- /dev/null +++ b/libxml2/python/tests/tstmem.py @@ -0,0 +1,36 @@ +#!/usr/bin/python -u +import libxml2 +import libxml2mod +import sys + +def error(msg, data): + pass + +# Memory debug specific +libxml2.debugMemory(1) + +dtd="""<!ELEMENT foo EMPTY>""" +instance="""<?xml version="1.0"?> +<foo></foo>""" + +dtd = libxml2.parseDTD(None, 'test.dtd') +ctxt = libxml2.newValidCtxt() +libxml2mod.xmlSetValidErrors(ctxt._o, error, error) +doc = libxml2.parseDoc(instance) +ret = doc.validateDtd(ctxt, dtd) +if ret != 1: + print("error doing DTD validation") + sys.exit(1) + +doc.freeDoc() +dtd.freeDtd() +del dtd +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/libxml2/python/tests/tstxpath.py b/libxml2/python/tests/tstxpath.py new file mode 100755 index 0000000..0ba5a6d --- /dev/null +++ b/libxml2/python/tests/tstxpath.py @@ -0,0 +1,63 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +#memory debug specific +libxml2.debugMemory(1) + +called = "" + +def foo(ctx, x): + global called + + # + # test that access to the XPath evaluation contexts + # + pctxt = libxml2.xpathParserContext(_obj=ctx) + ctxt = pctxt.context() + called = ctxt.function() + return x + 1 + +def bar(ctxt, x): + return "%d" % (x + 2) + +doc = libxml2.parseFile("tst.xml") +ctxt = doc.xpathNewContext() +res = ctxt.xpathEval("//*") +if len(res) != 2: + print("xpath query: wrong node set size") + sys.exit(1) +if res[0].name != "doc" or res[1].name != "foo": + print("xpath query: wrong node set value") + sys.exit(1) +libxml2.registerXPathFunction(ctxt._o, "foo", None, foo) +libxml2.registerXPathFunction(ctxt._o, "bar", None, bar) +i = 10000 +while i > 0: + res = ctxt.xpathEval("foo(1)") + if res != 2: + print("xpath extension failure") + sys.exit(1) + i = i - 1 +i = 10000 +while i > 0: + res = ctxt.xpathEval("bar(1)") + if res != "3": + print("xpath extension failure got %s expecting '3'") + sys.exit(1) + i = i - 1 +doc.freeDoc() +ctxt.xpathFreeContext() + +if called != "foo": + print("xpath function: failed to access the context") + print("xpath function: %s" % (called)) + sys.exit(1) + +#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/libxml2/python/tests/valid.xml b/libxml2/python/tests/valid.xml new file mode 100644 index 0000000..8a7f679 --- /dev/null +++ b/libxml2/python/tests/valid.xml @@ -0,0 +1,4 @@ +<!DOCTYPE doc [ +<!ELEMENT doc EMPTY> +]> +<doc/> diff --git a/libxml2/python/tests/validDTD.py b/libxml2/python/tests/validDTD.py new file mode 100755 index 0000000..4b03b8e --- /dev/null +++ b/libxml2/python/tests/validDTD.py @@ -0,0 +1,59 @@ +#!/usr/bin/python -u +import libxml2 +import sys + +ARG = 'test string' + +class ErrorHandler: + + def __init__(self): + self.errors = [] + + def handler(self, msg, data): + if data != ARG: + raise Exception("Error handler did not receive correct argument") + self.errors.append(msg) + + +# Memory debug specific +libxml2.debugMemory(1) + +dtd="""<!ELEMENT foo EMPTY>""" +valid="""<?xml version="1.0"?> +<foo></foo>""" + +invalid="""<?xml version="1.0"?> +<foo><bar/></foo>""" + +dtd = libxml2.parseDTD(None, 'test.dtd') +ctxt = libxml2.newValidCtxt() +e = ErrorHandler() +ctxt.setValidityErrorHandler(e.handler, e.handler, ARG) + +# Test valid document +doc = libxml2.parseDoc(valid) +ret = doc.validateDtd(ctxt, dtd) +if ret != 1 or e.errors: + print("error doing DTD validation") + sys.exit(1) +doc.freeDoc() + +# Test invalid document +doc = libxml2.parseDoc(invalid) +ret = doc.validateDtd(ctxt, dtd) +if ret != 0 or not e.errors: + print("Error: document supposed to be invalid") +doc.freeDoc() + +dtd.freeDtd() +del dtd +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/libxml2/python/tests/validRNG.py b/libxml2/python/tests/validRNG.py new file mode 100755 index 0000000..57f13a4 --- /dev/null +++ b/libxml2/python/tests/validRNG.py @@ -0,0 +1,76 @@ +#!/usr/bin/python -u +import libxml2 +import sys + +ARG = 'test string' + +class ErrorHandler: + + def __init__(self): + self.errors = [] + + def handler(self, msg, data): + if data != ARG: + raise Exception("Error handler did not receive correct argument") + self.errors.append(msg) + +# Memory debug specific +libxml2.debugMemory(1) + +schema="""<?xml version="1.0"?> +<element name="foo" + xmlns="http://relaxng.org/ns/structure/1.0" + xmlns:a="http://relaxng.org/ns/annotation/1.0" + xmlns:ex1="http://www.example.com/n1" + xmlns:ex2="http://www.example.com/n2"> + <a:documentation>A foo element.</a:documentation> + <element name="ex1:bar1"> + <empty/> + </element> + <element name="ex2:bar2"> + <empty/> + </element> +</element> +""" + +valid="""<?xml version="1.0"?> +<foo><pre1:bar1 xmlns:pre1="http://www.example.com/n1"/><pre2:bar2 xmlns:pre2="http://www.example.com/n2"/></foo>""" + +invalid="""<?xml version="1.0"?> +<foo><pre1:bar1 xmlns:pre1="http://www.example.com/n1">bad</pre1:bar1><pre2:bar2 xmlns:pre2="http://www.example.com/n2"/></foo>""" + +rngp = libxml2.relaxNGNewMemParserCtxt(schema, len(schema)) +rngs = rngp.relaxNGParse() +ctxt = rngs.relaxNGNewValidCtxt() +e = ErrorHandler() +ctxt.setValidityErrorHandler(e.handler, e.handler, ARG) + +# Test valid document +doc = libxml2.parseDoc(valid) +ret = doc.relaxNGValidateDoc(ctxt) +if ret != 0 or e.errors: + print("error doing RelaxNG validation") + sys.exit(1) +doc.freeDoc() + +# Test invalid document +doc = libxml2.parseDoc(invalid) +ret = doc.relaxNGValidateDoc(ctxt) +if ret == 0 or not e.errors: + print("Error: document supposed to be RelaxNG invalid") + sys.exit(1) +doc.freeDoc() + +del rngp +del rngs +del ctxt +libxml2.relaxNGCleanupTypes() + +# 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/libxml2/python/tests/validSchemas.py b/libxml2/python/tests/validSchemas.py new file mode 100755 index 0000000..cc543f3 --- /dev/null +++ b/libxml2/python/tests/validSchemas.py @@ -0,0 +1,83 @@ +#!/usr/bin/python -u +import libxml2 +import sys + +ARG = 'test string' + +class ErrorHandler: + + def __init__(self): + self.errors = [] + + def handler(self, msg, data): + if data != ARG: + raise Exception("Error handler did not receive correct argument") + self.errors.append(msg) + +# Memory debug specific +libxml2.debugMemory(1) + +schema="""<?xml version="1.0" encoding="iso-8859-1"?> +<schema xmlns = "http://www.w3.org/2001/XMLSchema"> + <element name = "Customer"> + <complexType> + <sequence> + <element name = "FirstName" type = "string" /> + <element name = "MiddleInitial" type = "string" /> + <element name = "LastName" type = "string" /> + </sequence> + <attribute name = "customerID" type = "integer" /> + </complexType> + </element> +</schema>""" + +valid="""<?xml version="1.0" encoding="iso-8859-1"?> +<Customer customerID = "24332"> + <FirstName>Raymond</FirstName> + <MiddleInitial>G</MiddleInitial> + <LastName>Bayliss</LastName> +</Customer> +""" + +invalid="""<?xml version="1.0" encoding="iso-8859-1"?> +<Customer customerID = "24332"> + <MiddleInitial>G</MiddleInitial> + <LastName>Bayliss</LastName> +</Customer> +""" + +e = ErrorHandler() +ctxt_parser = libxml2.schemaNewMemParserCtxt(schema, len(schema)) +ctxt_schema = ctxt_parser.schemaParse() +ctxt_valid = ctxt_schema.schemaNewValidCtxt() +ctxt_valid.setValidityErrorHandler(e.handler, e.handler, ARG) + +# Test valid document +doc = libxml2.parseDoc(valid) +ret = doc.schemaValidateDoc(ctxt_valid) +if ret != 0 or e.errors: + print("error doing schema validation") + sys.exit(1) +doc.freeDoc() + +# Test invalid document +doc = libxml2.parseDoc(invalid) +ret = doc.schemaValidateDoc(ctxt_valid) +if ret == 0 or not e.errors: + print("Error: document supposer to be schema invalid") + sys.exit(1) +doc.freeDoc() + +del ctxt_parser +del ctxt_schema +del ctxt_valid +libxml2.schemaCleanupTypes() + +# 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/libxml2/python/tests/validate.py b/libxml2/python/tests/validate.py new file mode 100755 index 0000000..16c0386 --- /dev/null +++ b/libxml2/python/tests/validate.py @@ -0,0 +1,82 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +ctxt = libxml2.createFileParserCtxt("valid.xml") +ctxt.validate(1) +ctxt.parseDocument() +doc = ctxt.doc() +valid = ctxt.isValid() + +if doc.name != "valid.xml": + print("doc.name failed") + sys.exit(1) +root = doc.children +if root.name != "doc": + print("root.name failed") + sys.exit(1) +if valid != 1: + print("validity chec failed") + sys.exit(1) +doc.freeDoc() + +i = 1000 +while i > 0: + ctxt = libxml2.createFileParserCtxt("valid.xml") + ctxt.validate(1) + ctxt.parseDocument() + doc = ctxt.doc() + valid = ctxt.isValid() + doc.freeDoc() + if valid != 1: + print("validity check failed") + sys.exit(1) + i = i - 1 + +#desactivate error messages from the validation +def noerr(ctx, str): + pass + +libxml2.registerErrorHandler(noerr, None) + +ctxt = libxml2.createFileParserCtxt("invalid.xml") +ctxt.validate(1) +ctxt.parseDocument() +doc = ctxt.doc() +valid = ctxt.isValid() +if doc.name != "invalid.xml": + print("doc.name failed") + sys.exit(1) +root = doc.children +if root.name != "doc": + print("root.name failed") + sys.exit(1) +if valid != 0: + print("validity chec failed") + sys.exit(1) +doc.freeDoc() + +i = 1000 +while i > 0: + ctxt = libxml2.createFileParserCtxt("invalid.xml") + ctxt.validate(1) + ctxt.parseDocument() + doc = ctxt.doc() + valid = ctxt.isValid() + doc.freeDoc() + if valid != 0: + print("validity check failed") + sys.exit(1) + i = i - 1 +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/libxml2/python/tests/walker.py b/libxml2/python/tests/walker.py new file mode 100755 index 0000000..47f0557 --- /dev/null +++ b/libxml2/python/tests/walker.py @@ -0,0 +1,144 @@ +#!/usr/bin/python -u +# +# this tests the entities substitutions with the XmlTextReader interface +# +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +result = "" +def processNode(reader): + global result + + result = result + "%d %d %s %d\n" % (reader.Depth(), reader.NodeType(), + reader.Name(), reader.IsEmptyElement()) + +# +# Parse a document testing the readerForxxx API +# +docstr="""<foo> +<label>some text</label> +<item>100</item> +</foo>""" +expect="""0 1 foo 0 +1 14 #text 0 +1 1 label 0 +2 3 #text 0 +1 15 label 0 +1 14 #text 0 +1 1 item 0 +2 3 #text 0 +1 15 item 0 +1 14 #text 0 +0 15 foo 0 +""" +result = "" + +doc = libxml2.parseDoc(docstr) +reader = doc.readerWalker(); +ret = reader.Read() +while ret == 1: + processNode(reader) + ret = reader.Read() + +if ret != 0: + print("Error parsing the document test1") + sys.exit(1) + +if result != expect: + print("Unexpected result for test1") + print(result) + sys.exit(1) + +doc.freeDoc() + +# +# Reuse the reader for another document testing the ReaderNewWalker API +# +docstr="""<foo> +<label>some text</label> +<item>1000</item> +</foo>""" +expect="""0 1 foo 0 +1 14 #text 0 +1 1 label 0 +2 3 #text 0 +1 15 label 0 +1 14 #text 0 +1 1 item 0 +2 3 #text 0 +1 15 item 0 +1 14 #text 0 +0 15 foo 0 +""" +result = "" + +doc = libxml2.parseDoc(docstr) +reader.NewWalker(doc) + +ret = reader.Read() +while ret == 1: + processNode(reader) + ret = reader.Read() + +if ret != 0: + print("Error parsing the document test2") + sys.exit(1) + +if result != expect: + print("Unexpected result for test2") + print(result) + sys.exit(1) + +doc.freeDoc() + +# +# Reuse the reader for another document testing the ReaderNewxxx API +# +docstr="""<foo> +<label>some text</label> +<item>1000</item> +</foo>""" +expect="""0 1 foo 0 +1 14 #text 0 +1 1 label 0 +2 3 #text 0 +1 15 label 0 +1 14 #text 0 +1 1 item 0 +2 3 #text 0 +1 15 item 0 +1 14 #text 0 +0 15 foo 0 +""" +result = "" + +reader.NewDoc(docstr, "test3", None, 0) +ret = reader.Read() +while ret == 1: + processNode(reader) + ret = reader.Read() + +if ret != 0: + print("Error parsing the document test3") + sys.exit(1) + +if result != expect: + print("Unexpected result for test3") + print(result) + sys.exit(1) + +# +# cleanup +# +del reader + +# 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/libxml2/python/tests/xpath.py b/libxml2/python/tests/xpath.py new file mode 100755 index 0000000..72e6c9d --- /dev/null +++ b/libxml2/python/tests/xpath.py @@ -0,0 +1,51 @@ +#!/usr/bin/python -u +# +# this test exercise the XPath basic engine, parser, etc, and +# allows to detect memory leaks +# +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +doc = libxml2.parseFile("tst.xml") +if doc.name != "tst.xml": + print("doc.name error") + sys.exit(1); + +ctxt = doc.xpathNewContext() +res = ctxt.xpathEval("//*") +if len(res) != 2: + print("xpath query: wrong node set size") + sys.exit(1) +if res[0].name != "doc" or res[1].name != "foo": + print("xpath query: wrong node set value") + sys.exit(1) +ctxt.setContextNode(res[0]) +res = ctxt.xpathEval("foo") +if len(res) != 1: + print("xpath query: wrong node set size") + sys.exit(1) +if res[0].name != "foo": + print("xpath query: wrong node set value") + sys.exit(1) +doc.freeDoc() +ctxt.xpathFreeContext() +i = 1000 +while i > 0: + doc = libxml2.parseFile("tst.xml") + ctxt = doc.xpathNewContext() + res = ctxt.xpathEval("//*") + doc.freeDoc() + ctxt.xpathFreeContext() + i = i -1 +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/libxml2/python/tests/xpathext.py b/libxml2/python/tests/xpathext.py new file mode 100755 index 0000000..b83d283 --- /dev/null +++ b/libxml2/python/tests/xpathext.py @@ -0,0 +1,49 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +# Memory debug specific +libxml2.debugMemory(1) + +def foo(ctx, x): + return x + 1 + +def bar(ctx, x): + return "%d" % (x + 2) + +doc = libxml2.parseFile("tst.xml") +ctxt = doc.xpathNewContext() +res = ctxt.xpathEval("//*") +if len(res) != 2: + print("xpath query: wrong node set size") + sys.exit(1) +if res[0].name != "doc" or res[1].name != "foo": + print("xpath query: wrong node set value") + sys.exit(1) + +libxml2.registerXPathFunction(ctxt._o, "foo", None, foo) +libxml2.registerXPathFunction(ctxt._o, "bar", None, bar) +i = 10000 +while i > 0: + res = ctxt.xpathEval("foo(1)") + if res != 2: + print("xpath extension failure") + sys.exit(1) + i = i - 1 +i = 10000 +while i > 0: + res = ctxt.xpathEval("bar(1)") + if res != "3": + print("xpath extension failure got %s expecting '3'") + sys.exit(1) + i = i - 1 +doc.freeDoc() +ctxt.xpathFreeContext() + +# 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/libxml2/python/tests/xpathleak.py b/libxml2/python/tests/xpathleak.py new file mode 100755 index 0000000..33ab61c --- /dev/null +++ b/libxml2/python/tests/xpathleak.py @@ -0,0 +1,65 @@ +#!/usr/bin/python +import sys, libxml2 + +libxml2.debugMemory(True) + +expect="""--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +--> Invalid expression +--> xmlXPathEval: evaluation failed +""" +err="" +def callback(ctx, str): + global err + + err = err + "%s %s" % (ctx, str) + +libxml2.registerErrorHandler(callback, "-->") + +doc = libxml2.parseDoc("<fish/>") +ctxt = doc.xpathNewContext() +ctxt.setContextNode(doc) +badexprs = ( + ":false()", "bad:()", "bad(:)", ":bad(:)", "bad:(:)", "bad:bad(:)", + "a:/b", "/c:/d", "//e:/f", "g://h" + ) +for expr in badexprs: + try: + ctxt.xpathEval(expr) + except libxml2.xpathError: + pass + else: + print("Unexpectedly legal expression:", expr) +ctxt.xpathFreeContext() +doc.freeDoc() + +if err != expect: + print("error") + print("received %s" %(err)) + print("expected %s" %(expect)) + sys.exit(1) + +libxml2.cleanupParser() +leakedbytes = libxml2.debugMemory(True) +if leakedbytes == 0: + print("OK") +else: + print("Memory leak", leakedbytes, "bytes") + # drop file to .memdump file in cwd, but won't work if not compiled in + libxml2.dumpMemory() diff --git a/libxml2/python/tests/xpathns.py b/libxml2/python/tests/xpathns.py new file mode 100755 index 0000000..379535e --- /dev/null +++ b/libxml2/python/tests/xpathns.py @@ -0,0 +1,29 @@ +#!/usr/bin/python -u +# +import libxml2 + +expect=' xmlns:a="urn:whatevar"' + +# Memory debug specific +libxml2.debugMemory(1) + +d = libxml2.parseDoc("<a:a xmlns:a='urn:whatevar'/>") +res="" +for n in d.xpathEval("//namespace::*"): + res = res + n.serialize() +d.freeDoc() + +if res != expect: + print("test5 failed: unexpected output") + print(res) +del res +del d +del n +# 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/libxml2/python/tests/xpathret.py b/libxml2/python/tests/xpathret.py new file mode 100755 index 0000000..11c8b32 --- /dev/null +++ b/libxml2/python/tests/xpathret.py @@ -0,0 +1,57 @@ +#!/usr/bin/python -u +import sys +import libxml2 + +#memory debug specific +libxml2.debugMemory(1) + +# +# A document hosting the nodes returned from the extension function +# +mydoc = libxml2.newDoc("1.0") + +def foo(ctx, str): + global mydoc + + # + # test returning a node set works as expected + # + parent = mydoc.newDocNode(None, 'p', None) + mydoc.addChild(parent) + node = mydoc.newDocText(str) + parent.addChild(node) + return [parent] + +doc = libxml2.parseFile("tst.xml") +ctxt = doc.xpathNewContext() +libxml2.registerXPathFunction(ctxt._o, "foo", None, foo) +res = ctxt.xpathEval("foo('hello')") +if type(res) != type([]): + print("Failed to return a nodeset") + sys.exit(1) +if len(res) != 1: + print("Unexpected nodeset size") + sys.exit(1) +node = res[0] +if node.name != 'p': + print("Unexpected nodeset element result") + sys.exit(1) +node = node.children +if node.type != 'text': + print("Unexpected nodeset element children type") + sys.exit(1) +if node.content != 'hello': + print("Unexpected nodeset element children content") + sys.exit(1) + +doc.freeDoc() +mydoc.freeDoc() +ctxt.xpathFreeContext() + +#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/libxml2/python/types.c b/libxml2/python/types.c new file mode 100644 index 0000000..f237677 --- /dev/null +++ b/libxml2/python/types.c @@ -0,0 +1,957 @@ +/* + * types.c: converter functions between the internal representation + * and the Python objects + * + * See Copyright for the status of this software. + * + * daniel@veillard.com + */ +#include "libxml_wrap.h" +#include <libxml/xpathInternals.h> + +#if PY_MAJOR_VERSION >= 3 +#define PY_IMPORT_STRING_SIZE PyUnicode_FromStringAndSize +#define PY_IMPORT_STRING PyUnicode_FromString +#define PY_IMPORT_INT PyLong_FromLong +#else +#define PY_IMPORT_STRING_SIZE PyString_FromStringAndSize +#define PY_IMPORT_STRING PyString_FromString +#define PY_IMPORT_INT PyInt_FromLong +#endif + +#if PY_MAJOR_VERSION >= 3 +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> + +FILE * +libxml_PyFileGet(PyObject *f) { + int fd, flags; + FILE *res; + const char *mode; + + fd = PyObject_AsFileDescriptor(f); + if (!_PyVerify_fd(fd)) + return(NULL); + /* + * Get the flags on the fd to understand how it was opened + */ + flags = fcntl(fd, F_GETFL, 0); + switch (flags & O_ACCMODE) { + case O_RDWR: + if (flags & O_APPEND) + mode = "a+"; + else + mode = "rw"; + break; + case O_RDONLY: + if (flags & O_APPEND) + mode = "r+"; + else + mode = "r"; + break; + case O_WRONLY: + if (flags & O_APPEND) + mode = "a"; + else + mode = "w"; + break; + default: + return(NULL); + } + + /* + * the FILE struct gets a new fd, so that it can be closed + * independently of the file descriptor given. The risk though is + * lack of sync. So at the python level sync must be implemented + * before and after a conversion took place. No way around it + * in the Python3 infrastructure ! + * The duplicated fd and FILE * will be released in the subsequent + * call to libxml_PyFileRelease() which must be genrated accodingly + */ + fd = dup(fd); + if (fd == -1) + return(NULL); + res = fdopen(fd, mode); + if (res == NULL) { + close(fd); + return(NULL); + } + return(res); +} + +void libxml_PyFileRelease(FILE *f) { + if (f != NULL) + fclose(f); +} +#endif + +PyObject * +libxml_intWrap(int val) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_intWrap: val = %d\n", val); +#endif + ret = PY_IMPORT_INT((long) val); + return (ret); +} + +PyObject * +libxml_longWrap(long val) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_longWrap: val = %ld\n", val); +#endif + ret = PyLong_FromLong(val); + return (ret); +} + +PyObject * +libxml_doubleWrap(double val) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_doubleWrap: val = %f\n", val); +#endif + ret = PyFloat_FromDouble((double) val); + return (ret); +} + +PyObject * +libxml_charPtrWrap(char *str) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlcharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PY_IMPORT_STRING(str); + xmlFree(str); + return (ret); +} + +PyObject * +libxml_charPtrConstWrap(const char *str) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlcharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PY_IMPORT_STRING(str); + return (ret); +} + +PyObject * +libxml_xmlCharPtrWrap(xmlChar * str) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlCharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PY_IMPORT_STRING((char *) str); + xmlFree(str); + return (ret); +} + +PyObject * +libxml_xmlCharPtrConstWrap(const xmlChar * str) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlCharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PY_IMPORT_STRING((char *) str); + return (ret); +} + +PyObject * +libxml_constcharPtrWrap(const char *str) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlcharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PY_IMPORT_STRING(str); + return (ret); +} + +PyObject * +libxml_constxmlCharPtrWrap(const xmlChar * str) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlCharPtrWrap: str = %s\n", str); +#endif + if (str == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PY_IMPORT_STRING((char *) str); + return (ret); +} + +PyObject * +libxml_xmlDocPtrWrap(xmlDocPtr doc) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlDocPtrWrap: doc = %p\n", doc); +#endif + if (doc == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + /* TODO: look at deallocation */ + ret = PyCapsule_New((void *) doc, (char *) "xmlDocPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlNodePtrWrap(xmlNodePtr node) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlNodePtrWrap: node = %p\n", node); +#endif + if (node == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *) node, (char *) "xmlNodePtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlURIPtrWrap(xmlURIPtr uri) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlURIPtrWrap: uri = %p\n", uri); +#endif + if (uri == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *) uri, (char *) "xmlURIPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlNsPtrWrap(xmlNsPtr ns) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlNsPtrWrap: node = %p\n", ns); +#endif + if (ns == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *) ns, (char *) "xmlNsPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlAttrPtrWrap(xmlAttrPtr attr) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlAttrNodePtrWrap: attr = %p\n", attr); +#endif + if (attr == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *) attr, (char *) "xmlAttrPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlAttributePtrWrap(xmlAttributePtr attr) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlAttributePtrWrap: attr = %p\n", attr); +#endif + if (attr == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *) attr, (char *) "xmlAttributePtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlElementPtrWrap(xmlElementPtr elem) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlElementNodePtrWrap: elem = %p\n", elem); +#endif + if (elem == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *) elem, (char *) "xmlElementPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlXPathContextPtrWrap(xmlXPathContextPtr ctxt) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlXPathContextPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *) ctxt, (char *) "xmlXPathContextPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlXPathParserContextPtrWrap(xmlXPathParserContextPtr ctxt) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlXPathParserContextPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = PyCapsule_New((void *)ctxt, (char *)"xmlXPathParserContextPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlParserCtxtPtrWrap(xmlParserCtxtPtr ctxt) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlParserCtxtPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + + ret = PyCapsule_New((void *) ctxt, (char *) "xmlParserCtxtPtr", NULL); + return (ret); +} + +/** + * libxml_xmlXPathDestructNsNode: + * cap: xmlNsPtr namespace node capsule object + * + * This function is called if and when a namespace node returned in + * an XPath node set is to be destroyed. That's the only kind of + * object returned in node set not directly linked to the original + * xmlDoc document, see xmlXPathNodeSetDupNs. + */ +#if PY_VERSION_HEX < 0x02070000 +static void +libxml_xmlXPathDestructNsNode(void *cap, void *desc ATTRIBUTE_UNUSED) +#else +static void +libxml_xmlXPathDestructNsNode(PyObject *cap) +#endif +{ +#ifdef DEBUG + fprintf(stderr, "libxml_xmlXPathDestructNsNode called %p\n", cap); +#endif +#if PY_VERSION_HEX < 0x02070000 + xmlXPathNodeSetFreeNs((xmlNsPtr) cap); +#else + xmlXPathNodeSetFreeNs((xmlNsPtr) PyCapsule_GetPointer(cap, "xmlNsPtr")); +#endif +} + +PyObject * +libxml_xmlXPathObjectPtrWrap(xmlXPathObjectPtr obj) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlXPathObjectPtrWrap: ctxt = %p\n", obj); +#endif + if (obj == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + switch (obj->type) { + case XPATH_XSLT_TREE: { + if ((obj->nodesetval == NULL) || + (obj->nodesetval->nodeNr == 0) || + (obj->nodesetval->nodeTab == NULL)) { + ret = PyList_New(0); + } else { + int i, len = 0; + xmlNodePtr node; + + node = obj->nodesetval->nodeTab[0]->children; + while (node != NULL) { + len++; + node = node->next; + } + ret = PyList_New(len); + node = obj->nodesetval->nodeTab[0]->children; + for (i = 0;i < len;i++) { + PyList_SetItem(ret, i, libxml_xmlNodePtrWrap(node)); + node = node->next; + } + } + /* + * Return now, do not free the object passed down + */ + return (ret); + } + case XPATH_NODESET: + if ((obj->nodesetval == NULL) + || (obj->nodesetval->nodeNr == 0)) { + ret = PyList_New(0); + } else { + int i; + xmlNodePtr node; + + ret = PyList_New(obj->nodesetval->nodeNr); + for (i = 0; i < obj->nodesetval->nodeNr; i++) { + node = obj->nodesetval->nodeTab[i]; + if (node->type == XML_NAMESPACE_DECL) { + PyObject *ns = PyCapsule_New((void *) node, + (char *) "xmlNsPtr", + libxml_xmlXPathDestructNsNode); + PyList_SetItem(ret, i, ns); + /* make sure the xmlNsPtr is not destroyed now */ + obj->nodesetval->nodeTab[i] = NULL; + } else { + PyList_SetItem(ret, i, libxml_xmlNodePtrWrap(node)); + } + } + } + break; + case XPATH_BOOLEAN: + ret = PY_IMPORT_INT((long) obj->boolval); + break; + case XPATH_NUMBER: + ret = PyFloat_FromDouble(obj->floatval); + break; + case XPATH_STRING: + ret = PY_IMPORT_STRING((char *) obj->stringval); + break; + case XPATH_POINT: + { + PyObject *node; + PyObject *indexIntoNode; + PyObject *tuple; + + node = libxml_xmlNodePtrWrap(obj->user); + indexIntoNode = PY_IMPORT_INT((long) obj->index); + + tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, node); + PyTuple_SetItem(tuple, 1, indexIntoNode); + + ret = tuple; + break; + } + case XPATH_RANGE: + { + unsigned short bCollapsedRange; + + bCollapsedRange = ( (obj->user2 == NULL) || + ((obj->user2 == obj->user) && (obj->index == obj->index2)) ); + if ( bCollapsedRange ) { + PyObject *node; + PyObject *indexIntoNode; + PyObject *tuple; + PyObject *list; + + list = PyList_New(1); + + node = libxml_xmlNodePtrWrap(obj->user); + indexIntoNode = PY_IMPORT_INT((long) obj->index); + + tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, node); + PyTuple_SetItem(tuple, 1, indexIntoNode); + + PyList_SetItem(list, 0, tuple); + + ret = list; + } else { + PyObject *node; + PyObject *indexIntoNode; + PyObject *tuple; + PyObject *list; + + list = PyList_New(2); + + node = libxml_xmlNodePtrWrap(obj->user); + indexIntoNode = PY_IMPORT_INT((long) obj->index); + + tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, node); + PyTuple_SetItem(tuple, 1, indexIntoNode); + + PyList_SetItem(list, 0, tuple); + + node = libxml_xmlNodePtrWrap(obj->user2); + indexIntoNode = PY_IMPORT_INT((long) obj->index2); + + tuple = PyTuple_New(2); + PyTuple_SetItem(tuple, 0, node); + PyTuple_SetItem(tuple, 1, indexIntoNode); + + PyList_SetItem(list, 1, tuple); + + ret = list; + } + break; + } + case XPATH_LOCATIONSET: + { + xmlLocationSetPtr set; + + set = obj->user; + if ( set && set->locNr > 0 ) { + int i; + PyObject *list; + + list = PyList_New(set->locNr); + + for (i=0; i<set->locNr; i++) { + xmlXPathObjectPtr setobj; + PyObject *pyobj; + + setobj = set->locTab[i]; /*xmlXPathObjectPtr setobj*/ + + pyobj = libxml_xmlXPathObjectPtrWrap(setobj); + /* xmlXPathFreeObject(setobj) is called */ + set->locTab[i] = NULL; + + PyList_SetItem(list, i, pyobj); + } + set->locNr = 0; + ret = list; + } else { + Py_INCREF(Py_None); + ret = Py_None; + } + break; + } + default: +#ifdef DEBUG + printf("Unable to convert XPath object type %d\n", obj->type); +#endif + Py_INCREF(Py_None); + ret = Py_None; + } + xmlXPathFreeObject(obj); + return (ret); +} + +xmlXPathObjectPtr +libxml_xmlXPathObjectPtrConvert(PyObject *obj) +{ + xmlXPathObjectPtr ret = NULL; + +#ifdef DEBUG + printf("libxml_xmlXPathObjectPtrConvert: obj = %p\n", obj); +#endif + if (obj == NULL) { + return (NULL); + } + if PyFloat_Check (obj) { + ret = xmlXPathNewFloat((double) PyFloat_AS_DOUBLE(obj)); + } else if PyLong_Check(obj) { +#ifdef PyLong_AS_LONG + ret = xmlXPathNewFloat((double) PyLong_AS_LONG(obj)); +#else + ret = xmlXPathNewFloat((double) PyInt_AS_LONG(obj)); +#endif +#ifdef PyBool_Check + } else if PyBool_Check (obj) { + + if (obj == Py_True) { + ret = xmlXPathNewBoolean(1); + } + else { + ret = xmlXPathNewBoolean(0); + } +#endif + } else if PyBytes_Check (obj) { + xmlChar *str; + + str = xmlStrndup((const xmlChar *) PyBytes_AS_STRING(obj), + PyBytes_GET_SIZE(obj)); + ret = xmlXPathWrapString(str); +#ifdef PyUnicode_Check + } else if PyUnicode_Check (obj) { +#if PY_VERSION_HEX >= 0x03030000 + xmlChar *str; + const char *tmp; + Py_ssize_t size; + + /* tmp doesn't need to be deallocated */ + tmp = PyUnicode_AsUTF8AndSize(obj, &size); + str = xmlStrndup((const xmlChar *) tmp, (int) size); + ret = xmlXPathWrapString(str); +#else + xmlChar *str = NULL; + PyObject *b; + + b = PyUnicode_AsUTF8String(obj); + if (b != NULL) { + str = xmlStrndup((const xmlChar *) PyBytes_AS_STRING(b), + PyBytes_GET_SIZE(b)); + Py_DECREF(b); + } + ret = xmlXPathWrapString(str); +#endif +#endif + } else if PyList_Check (obj) { + int i; + PyObject *node; + xmlNodePtr cur; + xmlNodeSetPtr set; + + set = xmlXPathNodeSetCreate(NULL); + + for (i = 0; i < PyList_Size(obj); i++) { + node = PyList_GetItem(obj, i); + if ((node == NULL) || (node->ob_type == NULL)) + continue; + + cur = NULL; + if (PyCapsule_CheckExact(node)) { +#ifdef DEBUG + printf("Got a Capsule\n"); +#endif + cur = PyxmlNode_Get(node); + } else if ((PyObject_HasAttrString(node, (char *) "_o")) && + (PyObject_HasAttrString(node, (char *) "get_doc"))) { + PyObject *wrapper; + + wrapper = PyObject_GetAttrString(node, (char *) "_o"); + if (wrapper != NULL) + cur = PyxmlNode_Get(wrapper); + } else { +#ifdef DEBUG + printf("Unknown object in Python return list\n"); +#endif + } + if (cur != NULL) { + xmlXPathNodeSetAdd(set, cur); + } + } + ret = xmlXPathWrapNodeSet(set); + } else { +#ifdef DEBUG + printf("Unable to convert Python Object to XPath"); +#endif + } + return (ret); +} + +PyObject * +libxml_xmlValidCtxtPtrWrap(xmlValidCtxtPtr valid) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlValidCtxtPtrWrap: valid = %p\n", valid); +#endif + if (valid == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + + ret = + PyCapsule_New((void *) valid, + (char *) "xmlValidCtxtPtr", NULL); + + return (ret); +} + +PyObject * +libxml_xmlCatalogPtrWrap(xmlCatalogPtr catal) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlNodePtrWrap: catal = %p\n", catal); +#endif + if (catal == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) catal, + (char *) "xmlCatalogPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlOutputBufferPtrWrap(xmlOutputBufferPtr buffer) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlOutputBufferPtrWrap: buffer = %p\n", buffer); +#endif + if (buffer == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) buffer, + (char *) "xmlOutputBufferPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlParserInputBufferPtrWrap(xmlParserInputBufferPtr buffer) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlParserInputBufferPtrWrap: buffer = %p\n", buffer); +#endif + if (buffer == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) buffer, + (char *) "xmlParserInputBufferPtr", NULL); + return (ret); +} + +#ifdef LIBXML_REGEXP_ENABLED +PyObject * +libxml_xmlRegexpPtrWrap(xmlRegexpPtr regexp) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlRegexpPtrWrap: regexp = %p\n", regexp); +#endif + if (regexp == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) regexp, + (char *) "xmlRegexpPtr", NULL); + return (ret); +} +#endif /* LIBXML_REGEXP_ENABLED */ + +#ifdef LIBXML_READER_ENABLED +PyObject * +libxml_xmlTextReaderPtrWrap(xmlTextReaderPtr reader) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlTextReaderPtrWrap: reader = %p\n", reader); +#endif + if (reader == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) 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 = + PyCapsule_New((void *) locator, + (char *) "xmlTextReaderLocatorPtr", NULL); + return (ret); +} +#endif /* LIBXML_READER_ENABLED */ + +#ifdef LIBXML_SCHEMAS_ENABLED +PyObject * +libxml_xmlRelaxNGPtrWrap(xmlRelaxNGPtr ctxt) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlRelaxNGPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) ctxt, + (char *) "xmlRelaxNGPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlRelaxNGParserCtxtPtrWrap(xmlRelaxNGParserCtxtPtr ctxt) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlRelaxNGParserCtxtPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) ctxt, + (char *) "xmlRelaxNGParserCtxtPtr", NULL); + return (ret); +} +PyObject * +libxml_xmlRelaxNGValidCtxtPtrWrap(xmlRelaxNGValidCtxtPtr valid) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlRelaxNGValidCtxtPtrWrap: valid = %p\n", valid); +#endif + if (valid == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) valid, + (char *) "xmlRelaxNGValidCtxtPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlSchemaPtrWrap(xmlSchemaPtr ctxt) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlSchemaPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) ctxt, + (char *) "xmlSchemaPtr", NULL); + return (ret); +} + +PyObject * +libxml_xmlSchemaParserCtxtPtrWrap(xmlSchemaParserCtxtPtr ctxt) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlSchemaParserCtxtPtrWrap: ctxt = %p\n", ctxt); +#endif + if (ctxt == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + ret = + PyCapsule_New((void *) ctxt, + (char *) "xmlSchemaParserCtxtPtr", NULL); + + return (ret); +} + +PyObject * +libxml_xmlSchemaValidCtxtPtrWrap(xmlSchemaValidCtxtPtr valid) +{ + PyObject *ret; + +#ifdef DEBUG + printf("libxml_xmlSchemaValidCtxtPtrWrap: valid = %p\n", valid); +#endif + if (valid == NULL) { + Py_INCREF(Py_None); + return (Py_None); + } + + ret = + PyCapsule_New((void *) valid, + (char *) "xmlSchemaValidCtxtPtr", NULL); + + 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 = PyCapsule_New((void *) error, (char *) "xmlErrorPtr", NULL); + return (ret); +} |