summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorscoder <none@none>2007-09-15 23:44:54 +0200
committerscoder <none@none>2007-09-15 23:44:54 +0200
commited4299b1b40e24ef83a0968505d4463ddb996b83 (patch)
tree080e69fe1a80cc6cfc7f9bce2b439823574be615 /src
parentb7b9db53ba34688b7724a8c0c2bf883dd606e500 (diff)
downloadpython-lxml-lxml-2.0alpha2.tar.gz
[svn r2884] support for selection output method on serialisationlxml-2.0alpha2
--HG-- branch : trunk
Diffstat (limited to 'src')
-rw-r--r--src/lxml/etree.pyx27
-rw-r--r--src/lxml/extensions.pxi8
-rw-r--r--src/lxml/serializer.pxi104
-rw-r--r--src/lxml/tests/test_elementtree.py72
-rw-r--r--src/lxml/tests/test_etree.py19
-rw-r--r--src/lxml/tree.pxd5
6 files changed, 209 insertions, 26 deletions
diff --git a/src/lxml/etree.pyx b/src/lxml/etree.pyx
index 420b42bd..613937ca 100644
--- a/src/lxml/etree.pyx
+++ b/src/lxml/etree.pyx
@@ -1374,11 +1374,14 @@ cdef public class _ElementTree [ type LxmlElementTreeType,
return self._doc._parser
return None
- def write(self, file, encoding=None,
+ def write(self, file, encoding=None, method="xml",
pretty_print=False, xml_declaration=None):
"""Write the tree to a file or file-like object.
-
+
Defaults to ASCII encoding and writing a declaration as needed.
+
+ The keyword argument 'method' selects the output method: 'xml' or
+ 'html'.
"""
cdef int c_write_declaration
self._assertHasRoot()
@@ -1394,7 +1397,7 @@ cdef public class _ElementTree [ type LxmlElementTreeType,
encoding = encoding.upper()
c_write_declaration = encoding not in \
('US-ASCII', 'ASCII', 'UTF8', 'UTF-8')
- _tofilelike(file, self._context_node, encoding,
+ _tofilelike(file, self._context_node, encoding, method,
c_write_declaration, 1, bool(pretty_print))
def getpath(self, _Element element not None):
@@ -2148,7 +2151,7 @@ def dump(_Element elem not None, pretty_print=True):
"""
_dumpToFile(sys.stdout, elem._c_node, bool(pretty_print))
-def tostring(element_or_tree, encoding=None,
+def tostring(element_or_tree, encoding=None, method="xml",
xml_declaration=None, pretty_print=False):
"""Serialize an element to an encoded string representation of its XML
tree.
@@ -2159,6 +2162,8 @@ def tostring(element_or_tree, encoding=None,
compatible encoding will enable a declaration by default.
The keyword argument 'pretty_print' (bool) enables formatted XML.
+
+ The keyword argument 'method' selects the output method: 'xml' or 'html'.
"""
cdef int write_declaration
cdef int c_pretty_print
@@ -2173,15 +2178,15 @@ def tostring(element_or_tree, encoding=None,
encoding = 'ASCII'
if isinstance(element_or_tree, _Element):
- return _tostring(<_Element>element_or_tree,
- encoding, write_declaration, 0, c_pretty_print)
+ return _tostring(<_Element>element_or_tree, encoding, method,
+ write_declaration, 0, c_pretty_print)
elif isinstance(element_or_tree, _ElementTree):
return _tostring((<_ElementTree>element_or_tree)._context_node,
- encoding, write_declaration, 1, c_pretty_print)
+ encoding, method, write_declaration, 1, c_pretty_print)
else:
raise TypeError, "Type '%s' cannot be serialized." % type(element_or_tree)
-def tounicode(element_or_tree, pretty_print=False):
+def tounicode(element_or_tree, method="xml", pretty_print=False):
"""Serialize an element to the Python unicode representation of its XML
tree.
@@ -2190,14 +2195,16 @@ def tounicode(element_or_tree, pretty_print=False):
further treatment.
The keyword argument 'pretty_print' (bool) enables formatted XML.
+
+ The keyword argument 'method' selects the output method: 'xml' or 'html'.
"""
cdef int c_pretty_print
c_pretty_print = bool(pretty_print)
if isinstance(element_or_tree, _Element):
- return _tounicode(<_Element>element_or_tree, 0, c_pretty_print)
+ return _tounicode(<_Element>element_or_tree, method, 0, c_pretty_print)
elif isinstance(element_or_tree, _ElementTree):
return _tounicode((<_ElementTree>element_or_tree)._context_node,
- 1, c_pretty_print)
+ method, 1, c_pretty_print)
else:
raise TypeError, "Type '%s' cannot be serialized." % type(element_or_tree)
diff --git a/src/lxml/extensions.pxi b/src/lxml/extensions.pxi
index 3c444c8d..00b05d32 100644
--- a/src/lxml/extensions.pxi
+++ b/src/lxml/extensions.pxi
@@ -363,8 +363,12 @@ cdef class _ExsltRegExp:
return firstnode
elif isinstance(firstnode, _Element):
c_text = tree.xmlNodeGetContent((<_Element>firstnode)._c_node)
- s = funicode(c_text)
- tree.xmlFree(c_text)
+ if c_text is NULL:
+ python.PyErr_NoMemory()
+ try:
+ s = funicode(c_text)
+ finally:
+ tree.xmlFree(c_text)
return s
else:
return str(firstnode)
diff --git a/src/lxml/serializer.pxi b/src/lxml/serializer.pxi
index b3b9bf31..1260afb1 100644
--- a/src/lxml/serializer.pxi
+++ b/src/lxml/serializer.pxi
@@ -1,21 +1,75 @@
# XML serialization and output functions
-cdef _tostring(_Element element, encoding,
+cdef enum _OutputMethods:
+ OUTPUT_METHOD_XML
+ OUTPUT_METHOD_HTML
+ OUTPUT_METHOD_TEXT
+
+cdef int _findOutputMethod(method) except -1:
+ if method is None:
+ return OUTPUT_METHOD_XML
+ method = method.lower()
+ if method == "xml":
+ return OUTPUT_METHOD_XML
+ if method == "html":
+ return OUTPUT_METHOD_HTML
+ if method == "text":
+ return OUTPUT_METHOD_TEXT
+ raise ValueError, "unknown output method %r" % method
+
+cdef _textToString(xmlNode* c_node, encoding):
+ cdef python.PyThreadState* state
+ cdef char* c_text
+ state = python.PyEval_SaveThread()
+ c_text = tree.xmlNodeGetContent(c_node)
+ python.PyEval_RestoreThread(state)
+ if c_text is NULL:
+ python.PyErr_NoMemory()
+
+ try:
+ if _hasTail(c_node):
+ tail = _collectText(c_node.next)
+ if tail:
+ text = c_text + tail
+ else:
+ text = c_text
+ else:
+ text = c_text
+ finally:
+ tree.xmlFree(c_text)
+
+ if encoding is None:
+ return text
+ encoding = encoding.upper()
+ if encoding == 'UTF-8' or encoding == 'ASCII':
+ return text
+
+ text = python.PyUnicode_FromEncodedObject(text, 'utf-8', 'strict')
+ return python.PyUnicode_AsEncodedString(text, encoding, 'strict')
+
+cdef _tostring(_Element element, encoding, method,
int write_xml_declaration, int write_complete_document,
int pretty_print):
- "Serialize an element to an encoded string representation of its XML tree."
+ """Serialize an element to an encoded string representation of its XML
+ tree.
+ """
cdef python.PyThreadState* state
cdef tree.xmlOutputBuffer* c_buffer
cdef tree.xmlBuffer* c_result_buffer
cdef tree.xmlCharEncodingHandler* enchandler
cdef char* c_enc
cdef char* c_version
+ cdef int c_method
if element is None:
return None
if encoding is None:
c_enc = NULL
else:
- c_enc = encoding
+ encoding = _utf8(encoding)
+ c_enc = _cstr(encoding)
+ c_method = _findOutputMethod(method)
+ if c_method == OUTPUT_METHOD_TEXT:
+ return _textToString(element._c_node, encoding)
# it is necessary to *and* find the encoding handler *and* use
# encoding during output
enchandler = tree.xmlFindCharEncodingHandler(c_enc)
@@ -29,7 +83,7 @@ cdef _tostring(_Element element, encoding,
try:
state = python.PyEval_SaveThread()
- _writeNodeToBuffer(c_buffer, element._c_node, c_enc,
+ _writeNodeToBuffer(c_buffer, element._c_node, c_enc, c_method,
write_xml_declaration, write_complete_document,
pretty_print)
tree.xmlOutputBufferFlush(c_buffer)
@@ -45,19 +99,27 @@ cdef _tostring(_Element element, encoding,
tree.xmlOutputBufferClose(c_buffer)
return result
-cdef _tounicode(_Element element, int write_complete_document, int pretty_print):
- "Serialize an element to the Python unicode representation of its XML tree."
+cdef _tounicode(_Element element, method,
+ int write_complete_document, int pretty_print):
+ """Serialize an element to the Python unicode representation of its XML
+ tree.
+ """
cdef python.PyThreadState* state
cdef tree.xmlOutputBuffer* c_buffer
cdef tree.xmlBuffer* c_result_buffer
+ cdef int c_method
if element is None:
return None
+ c_method = _findOutputMethod(method)
+ if c_method == OUTPUT_METHOD_TEXT:
+ text = _textToString(element._c_node, None)
+ return python.PyUnicode_FromEncodedObject(text, 'utf-8', 'strict')
c_buffer = tree.xmlAllocOutputBuffer(NULL)
if c_buffer is NULL:
raise LxmlError, "Failed to create output buffer"
try:
state = python.PyEval_SaveThread()
- _writeNodeToBuffer(c_buffer, element._c_node, NULL, 0,
+ _writeNodeToBuffer(c_buffer, element._c_node, NULL, c_method, 0,
write_complete_document, pretty_print)
tree.xmlOutputBufferFlush(c_buffer)
python.PyEval_RestoreThread(state)
@@ -74,14 +136,14 @@ cdef _tounicode(_Element element, int write_complete_document, int pretty_print)
return result
cdef void _writeNodeToBuffer(tree.xmlOutputBuffer* c_buffer,
- xmlNode* c_node, char* encoding,
+ xmlNode* c_node, char* encoding, int c_method,
int write_xml_declaration,
int write_complete_document,
int pretty_print):
cdef xmlDoc* c_doc
cdef xmlNode* c_nsdecl_node
c_doc = c_node.doc
- if write_xml_declaration:
+ if write_xml_declaration and c_method == OUTPUT_METHOD_XML:
_writeDeclarationToBuffer(c_buffer, c_doc.version, encoding)
# write internal DTD subset, preceding PIs/comments, etc.
@@ -101,8 +163,12 @@ cdef void _writeNodeToBuffer(tree.xmlOutputBuffer* c_buffer,
c_nsdecl_node.last = c_node.last
# write node
- tree.xmlNodeDumpOutput(c_buffer, c_doc, c_nsdecl_node, 0,
- pretty_print, encoding)
+ if c_method == OUTPUT_METHOD_XML:
+ tree.xmlNodeDumpOutput(
+ c_buffer, c_doc, c_nsdecl_node, 0, pretty_print, encoding)
+ else:
+ tree.htmlNodeDumpFormatOutput(
+ c_buffer, c_doc, c_nsdecl_node, encoding, pretty_print)
if c_nsdecl_node is not c_node:
# clean up
@@ -244,7 +310,7 @@ cdef int _writeFilelikeWriter(void* ctxt, char* c_buffer, int len):
cdef int _closeFilelikeWriter(void* ctxt):
return (<_FilelikeWriter>ctxt).close()
-cdef _tofilelike(f, _Element element, encoding,
+cdef _tofilelike(f, _Element element, encoding, method,
int write_xml_declaration, int write_doctype,
int pretty_print):
cdef python.PyThreadState* state
@@ -255,7 +321,17 @@ cdef _tofilelike(f, _Element element, encoding,
if encoding is None:
c_enc = NULL
else:
- c_enc = encoding
+ encoding = _utf8(encoding)
+ c_enc = _cstr(encoding)
+ c_method = _findOutputMethod(method)
+ if c_method == OUTPUT_METHOD_TEXT:
+ if _isString(f):
+ f = open(f, 'wb')
+ f.write(_textToString(element._c_node, encoding))
+ f.close()
+ else:
+ f.write(_textToString(element._c_node, encoding))
+ return
enchandler = tree.xmlFindCharEncodingHandler(c_enc)
if enchandler is NULL:
raise LookupError, python.PyString_FromFormat(
@@ -275,7 +351,7 @@ cdef _tofilelike(f, _Element element, encoding,
tree.xmlCharEncCloseFunc(enchandler)
raise TypeError, "File or filename expected, got '%s'" % type(f)
- _writeNodeToBuffer(c_buffer, element._c_node, c_enc,
+ _writeNodeToBuffer(c_buffer, element._c_node, c_enc, c_method,
write_xml_declaration, write_doctype, pretty_print)
tree.xmlOutputBufferClose(c_buffer)
tree.xmlCharEncCloseFunc(enchandler)
diff --git a/src/lxml/tests/test_elementtree.py b/src/lxml/tests/test_elementtree.py
index 43c44f02..f663704b 100644
--- a/src/lxml/tests/test_elementtree.py
+++ b/src/lxml/tests/test_elementtree.py
@@ -716,6 +716,47 @@ class ETreeTestCaseBase(unittest.TestCase):
self.assertEquals(
'<doc%s>This is a test.</doc%s>' % (i, i),
canonicalize(data))
+
+ def test_write_method_html(self):
+ ElementTree = self.etree.ElementTree
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ html = Element('html')
+ body = SubElement(html, 'body')
+ p = SubElement(body, 'p')
+ p.text = "html"
+ SubElement(p, 'br').tail = "test"
+
+ tree = ElementTree(element=html)
+ f = StringIO()
+ tree.write(f, method="html")
+ data = f.getvalue()
+
+ self.assertEquals('<html><body><p>html<br>test</p></body></html>',
+ data)
+
+ def test_write_method_text(self):
+ ElementTree = self.etree.ElementTree
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ a.text = "A"
+ a.tail = "tail"
+ b = SubElement(a, 'b')
+ b.text = "B"
+ b.tail = "TAIL"
+ c = SubElement(a, 'c')
+ c.text = "C"
+
+ tree = ElementTree(element=a)
+ f = StringIO()
+ tree.write(f, method="text")
+ data = f.getvalue()
+
+ self.assertEquals('ABTAILCtail',
+ data)
def test_write_fail(self):
ElementTree = self.etree.ElementTree
@@ -2099,6 +2140,37 @@ class ETreeTestCaseBase(unittest.TestCase):
self.assert_(tostring(b) == '<b/>Foo' or
tostring(b) == '<b />Foo')
+ def test_tostring_method_html(self):
+ tostring = self.etree.tostring
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ html = Element('html')
+ body = SubElement(html, 'body')
+ p = SubElement(body, 'p')
+ p.text = "html"
+ SubElement(p, 'br').tail = "test"
+
+ self.assertEquals('<html><body><p>html<br>test</p></body></html>',
+ tostring(html, method="html"))
+
+ def test_tostring_method_text(self):
+ tostring = self.etree.tostring
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ a.text = "A"
+ a.tail = "tail"
+ b = SubElement(a, 'b')
+ b.text = "B"
+ b.tail = "TAIL"
+ c = SubElement(a, 'c')
+ c.text = "C"
+
+ self.assertEquals('ABTAILCtail',
+ tostring(a, method="text"))
+
def test_iterparse(self):
iterparse = self.etree.iterparse
f = StringIO('<a><b></b><c/></a>')
diff --git a/src/lxml/tests/test_etree.py b/src/lxml/tests/test_etree.py
index 6913fc87..2b6c84ed 100644
--- a/src/lxml/tests/test_etree.py
+++ b/src/lxml/tests/test_etree.py
@@ -1761,6 +1761,25 @@ class ETreeOnlyTestCase(HelperTestCase):
result = tostring(a, pretty_print=True)
self.assertEquals(result, "<a>\n <b/>\n <c/>\n</a>")
+ def test_tostring_method_text_encoding(self):
+ tostring = self.etree.tostring
+ Element = self.etree.Element
+ SubElement = self.etree.SubElement
+
+ a = Element('a')
+ a.text = "A"
+ a.tail = "tail"
+ b = SubElement(a, 'b')
+ b.text = "B"
+ b.tail = u"Søk på nettet"
+ c = SubElement(a, 'c')
+ c.text = "C"
+
+ result = tostring(a, method="text", encoding="UTF-16")
+
+ self.assertEquals(u'ABSøk på nettetCtail'.encode("UTF-16"),
+ result)
+
def test_tounicode(self):
tounicode = self.etree.tounicode
Element = self.etree.Element
diff --git a/src/lxml/tree.pxd b/src/lxml/tree.pxd
index fd0cdb54..9b524ce5 100644
--- a/src/lxml/tree.pxd
+++ b/src/lxml/tree.pxd
@@ -234,6 +234,11 @@ cdef extern from "libxml/tree.h":
cdef char* xmlBuildURI(char* href, char* base)
cdef int xmlValidateNCName(char* value, int space)
+cdef extern from "libxml/HTMLtree.h":
+ cdef void htmlNodeDumpFormatOutput(xmlOutputBuffer* buf,
+ xmlDoc* doc, xmlNode* cur,
+ char* encoding, int format)
+
cdef extern from "libxml/valid.h":
cdef xmlAttr* xmlGetID(xmlDoc* doc, char* ID)
cdef void xmlDumpNotationTable(xmlBuffer* buffer, xmlNotationTable* table)