Repository: ipython/ipython

Issue:
qtconsole ipython 0.13.2 - html/xhtml export fails
Hi,
I'm having problems trying to export qtconsole sessions to html/xhtml on my linux box(Macbook Pro). I'm running python 3.3.2.
The html export fails with:

```
'Traceback (most recent call last):
  File "/usr/lib/python3.3/site-packages/IPython/frontend/qt/console/rich_ipython_widget.py", line 71, in export_html
    super(RichIPythonWidget, self).export_html()
  File "/usr/lib/python3.3/site-packages/IPython/frontend/qt/console/console_widget.py", line 615, in export_html
    self._html_exporter.export()
  File "/usr/lib/python3.3/site-packages/IPython/frontend/qt/rich_text.py", line 80, in export
    if inline is None and IMG_RE.search(html):
TypeError: can't use a string pattern on a bytes-like object'
```

The xhtml export fails with :
'Type string doesn't support the buffer API'
Am I doing something wrong?

Cheers



Discussion:
Probably a unicode-handling bug in our code that only affects Python 3.

Isn't it an issue that #4043  may suddenly solve ? 
(4 common keywords : "png" +  (unexpected "bytes-like") + "py3k" + "to html")

nop. Qtconsole has nothing to do with ipynb files.

Hello,

On my windows pc
Trying with  http://matplotlib.org/examples/lines_bars_and_markers/line_demo_dash_control.html

With Python 2.7 / ipython 0.13.2 or ipython 1.0.0rc1, everything works (inlining or not inlining the png).

With Python 3.3 / ipython 1.0.0 (patched with 4043), it asks me where to save the .html, then  the "inline / external" question doesn't show up, then I get in the dos windows the same message 
\IPython\qt\rich_text.py", line 80, in export
    if inline is None and IMG_RE.search(html):
TypeError: can't use a string pattern on a bytes-like object

remark  

---

Trying to digg further , I found a reference to a remark from GaelVaroquaux
"# in python3, StringIO does not accept binary data" 
(https://github.com/joblib/joblib/commit/e53cc2b)

**\* privileged python 2 users behavior below ****
![qt_to_html_python2_ipython1](https://f.cloud.github.com/assets/4312421/979510/9a61d85a-070d-11e3-93d3-db9d0d67d61a.JPG)

I don't know if it can help a competent "doctor", but If I spy the "image_tag" coming to ensure_utf8(image_tag)
I see, with  print(image_tag.**class**)   
- gives  a "type 'instancemethod'" for python 2.7
- gives  a "class 'method'" for python 3.3


Resolving patch:
diff --git a/IPython/qt/console/rich_ipython_widget.py b/IPython/qt/console/rich_ipython_widget.py
--- a/IPython/qt/console/rich_ipython_widget.py
+++ b/IPython/qt/console/rich_ipython_widget.py
@@ -255,15 +255,17 @@ def _get_image_tag(self, match, path = None, format = "png"):
             except KeyError:
                 if not self._svg_warning_displayed:
                     QtGui.QMessageBox.warning(self, 'Error converting PNG to SVG.',
-                        'Cannot convert a PNG to SVG.  To fix this, add this '
+                        'Cannot convert PNG images to SVG, export with PNG figures instead. '
+                        'If you want to export matplotlib figures as SVG, add '
                         'to your ipython config:\n\n'
-                        '\tc.InlineBackendConfig.figure_format = \'svg\'\n\n'
+                        '\tc.InlineBackend.figure_format = \'svg\'\n\n'
                         'And regenerate the figures.',
                                               QtGui.QMessageBox.Ok)
                     self._svg_warning_displayed = True
-                return ("<b>Cannot convert a PNG to SVG.</b>  "
-                        "To fix this, add this to your config: "
-                        "<span>c.InlineBackendConfig.figure_format = 'svg'</span> "
+                return ("<b>Cannot convert  PNG images to SVG.</b>  "
+                        "You must export this session with PNG images. "
+                        "If you want to export matplotlib figures as SVG, add to your config "
+                        "<span>c.InlineBackend.figure_format = 'svg'</span> "
                         "and regenerate the figures.")
 
             # Not currently checking path, because it's tricky to find a
diff --git a/IPython/qt/rich_text.py b/IPython/qt/rich_text.py
--- a/IPython/qt/rich_text.py
+++ b/IPython/qt/rich_text.py
@@ -4,11 +4,12 @@
 # Imports
 #-----------------------------------------------------------------------------
 
-# Standard library imports.
+# Standard library imports
+import io
 import os
 import re
 
-# System library imports.
+# System library imports
 from IPython.external.qt import QtGui
 
 # IPython imports
@@ -69,7 +70,7 @@ def export(self):
         if dialog.exec_():
             self.filename = dialog.selectedFiles()[0]
             choice = dialog.selectedNameFilter()
-            html = self.control.document().toHtml().encode('utf-8')
+            html = py3compat.cast_unicode(self.control.document().toHtml())
 
             # Configure the exporter.
             if choice.startswith('XHTML'):
@@ -127,8 +128,8 @@ def export_html(html, filename, image_tag = None, inline = True):
 
     Parameters:
     -----------
-    html : str,
-        A utf-8 encoded Python string containing the Qt HTML to export.
+    html : unicode,
+        A Python unicode string containing the Qt HTML to export.
 
     filename : str
         The file to be saved.
@@ -143,8 +144,6 @@ def export_html(html, filename, image_tag = None, inline = True):
     """
     if image_tag is None:
         image_tag = default_image_tag
-    else:
-        image_tag = ensure_utf8(image_tag)
 
     if inline:
         path = None
@@ -154,7 +153,7 @@ def export_html(html, filename, image_tag = None, inline = True):
         if os.path.isfile(path):
             raise OSError("%s exists, but is not a directory." % path)
 
-    with open(filename, 'w') as f:
+    with io.open(filename, 'w', encoding='utf-8') as f:
         html = fix_html(html)
         f.write(IMG_RE.sub(lambda x: image_tag(x, path = path, format = "png"),
                            html))
@@ -165,8 +164,8 @@ def export_xhtml(html, filename, image_tag=None):
 
     Parameters:
     -----------
-    html : str,
-        A utf-8 encoded Python string containing the Qt HTML to export.
+    html : unicode,
+        A Python unicode string containing the Qt HTML to export.
 
     filename : str
         The file to be saved.
@@ -176,15 +175,13 @@ def export_xhtml(html, filename, image_tag=None):
     """
     if image_tag is None:
         image_tag = default_image_tag
-    else:
-        image_tag = ensure_utf8(image_tag)
 
-    with open(filename, 'w') as f:
+    with io.open(filename, 'w', encoding='utf-8') as f:
         # Hack to make xhtml header -- note that we are not doing any check for
         # valid XML.
         offset = html.find("<html>")
         assert offset > -1, 'Invalid HTML string: no <html> tag.'
-        html = ('<html xmlns="http://www.w3.org/1999/xhtml">\n'+
+        html = (u'<html xmlns="http://www.w3.org/1999/xhtml">\n'+
                 html[offset+6:])
 
         html = fix_html(html)
@@ -213,21 +210,7 @@ def default_image_tag(match, path = None, format = "png"):
     format : "png"|"svg", optional [default "png"]
         Format for returned or referenced images.
     """
-    return ''
-
-
-def ensure_utf8(image_tag):
-    """wrapper for ensuring image_tag returns utf8-encoded str on Python 2"""
-    if py3compat.PY3:
-        # nothing to do on Python 3
-        return image_tag
-    
-    def utf8_image_tag(*args, **kwargs):
-        s = image_tag(*args, **kwargs)
-        if isinstance(s, unicode):
-            s = s.encode('utf8')
-        return s
-    return utf8_image_tag
+    return u''
 
 
 def fix_html(html):
@@ -235,8 +218,8 @@ def fix_html(html):
 
     Parameters:
     -----------
-    html : str,
-        A utf-8 encoded Python string containing the Qt HTML.
+    html : unicode,
+        A Python unicode string containing the Qt HTML.
     """
     # A UTF-8 declaration is needed for proper rendering of some characters
     # (e.g., indented commands) when viewing exported HTML on a local system


Test patch: