Add hooke.compat.minidom to fix Python's XML generation issue5752.
authorW. Trevor King <wking@drexel.edu>
Mon, 2 Aug 2010 16:37:37 +0000 (12:37 -0400)
committerW. Trevor King <wking@drexel.edu>
Mon, 2 Aug 2010 16:37:37 +0000 (12:37 -0400)
xml.dom.minidom.Element.writexml doesn't escape some special
characters (e.g. '\n').  See
  http://bugs.python.org/issue5752

importing hooke.compat.minidom fixes this bug dynamically.

hooke/compat/minidom.py [new file with mode: 0644]
hooke/playlist.py

diff --git a/hooke/compat/minidom.py b/hooke/compat/minidom.py
new file mode 100644 (file)
index 0000000..9d9f1a2
--- /dev/null
@@ -0,0 +1,50 @@
+# Copyright
+
+"""Dynamically patch :mod:`xml.dom.minidom`'s attribute value escaping.
+
+:meth:`xml.dom.minidom.Element.setAttribute` doesn't preform some
+character escaping (see the `Python bug`_ and `XML specs`_).
+Importing this module applies the suggested patch dynamically.
+
+.. _Python bug: http://bugs.python.org/issue5752
+.. _XML specs:
+  http://www.w3.org/TR/2000/WD-xml-c14n-20000119.html#charescaping
+"""
+
+import xml.dom.minidom
+
+
+def _write_data(writer, data, isAttrib=False):
+    "Writes datachars to writer."
+    if isAttrib:
+        data = data.replace("\r", "&#xD;").replace("\n", "&#xA;")
+        data = data.replace("\t", "&#x9;")
+    writer.write(data)
+xml.dom.minidom._write_data = _write_data
+
+def writexml(self, writer, indent="", addindent="", newl=""):
+    # indent = current indentation
+    # addindent = indentation to add to higher levels
+    # newl = newline string
+    writer.write(indent+"<" + self.tagName)
+
+    attrs = self._get_attributes()
+    a_names = attrs.keys()
+    a_names.sort()
+
+    for a_name in a_names:
+        writer.write(" %s=\"" % a_name)
+        _write_data(writer, attrs[a_name].value, isAttrib=True)
+        writer.write("\"")
+    if self.childNodes:
+        writer.write(">%s"%(newl))
+        for node in self.childNodes:
+            node.writexml(writer,indent+addindent,addindent,newl)
+        writer.write("%s</%s>%s" % (indent,self.tagName,newl))
+    else:
+        writer.write("/>%s"%(newl))
+# For an introduction to overriding instance methods, see
+#   http://irrepupavel.com/documents/python/instancemethod/
+instancemethod = type(xml.dom.minidom.Element.writexml)
+xml.dom.minidom.Element.writexml = instancemethod(
+    writexml, None, xml.dom.minidom.Element)
index 8efac4770568a3a6923c83a138317349494ad7d8..b58e9b6fa7f49be199780279bb8dc358f4ff2fe7 100644 (file)
@@ -27,6 +27,7 @@ import os.path
 import xml.dom.minidom
 
 from . import curve as curve
+from .compat import minidom as minidom  # dynamically patch xml.sax.minidom
 
 
 class NoteIndexList (list):
@@ -185,20 +186,20 @@ class FilePlaylist (Playlist):
         >>> c.info['note'] = 'The first curve'
         >>> p.append(c)
         >>> c = curve.Curve(os.path.join(root_path, 'to', 'curve', 'two'))
-        >>> c.info['note'] = 'The second curve'
+        >>> c.info['note'] = 'The second curve\\nwith endlines'
         >>> p.append(c)
         >>> print p.flatten() # doctest: +NORMALIZE_WHITESPACE +REPORT_UDIFF
         <?xml version="1.0" encoding="utf-8"?>
         <playlist index="0" note="An example playlist" version="0.1">
-            <curve note="The first curve" path="curve/one"/>
-            <curve note="The second curve" path="curve/two"/>
+           <curve note="The first curve" path="curve/one"/>
+           <curve note="The second curve&#xA;with endlines" path="curve/two"/>
         </playlist>
         <BLANKLINE>
         >>> print p.flatten(absolute_paths=True) # doctest: +NORMALIZE_WHITESPACE +REPORT_UDIFF
         <?xml version="1.0" encoding="utf-8"?>
         <playlist index="0" note="An example playlist" version="0.1">
-            <curve note="The first curve" path="/path/to/curve/one"/>
-            <curve note="The second curve" path="/path/to/curve/two"/>
+           <curve note="The first curve" path="/path/to/curve/one"/>
+           <curve note="The second curve&#xA;with endlines" path="/path/to/curve/two"/>
         </playlist>
         <BLANKLINE>
         """
@@ -224,7 +225,7 @@ class FilePlaylist (Playlist):
             for key,value in curve.info.items():
                 if key in self._ignored_keys:
                     continue
-                curve_element.setAttribute(key, str(value))
+                curve_element.setAttribute(key,str(value))
         string = doc.toprettyxml(encoding='utf-8')
         root.unlink() # break circular references for garbage collection
         return string
@@ -259,7 +260,7 @@ class FilePlaylist (Playlist):
         >>> string = '''<?xml version="1.0" encoding="utf-8"?>
         ... <playlist index="1" note="An example playlist" version="0.1">
         ...     <curve note="The first curve" path="../curve/one"/>
-        ...     <curve note="The second curve" path="../curve/two"/>
+        ...     <curve note="The second curve&#xA;with endlines" path="../curve/two"/>
         ... </playlist>
         ... '''
         >>> p = FilePlaylist(drivers=[],
@@ -273,6 +274,8 @@ class FilePlaylist (Playlist):
         ...     print curve.path
         path/to/curve/one
         path/to/curve/two
+        >>> p[-1].info['note']
+        u'The second curve\\nwith endlines'
         """
         doc = xml.dom.minidom.parseString(string)
         self._from_xml_doc(doc, identify=identify)