From 2b04cb38d98a54778777133fdb77adfb42b8ee69 Mon Sep 17 00:00:00 2001
From: Javi Merino <javi.merino@arm.com>
Date: Fri, 17 Apr 2015 16:18:23 +0100
Subject: [PATCH 1/3] Don't break prematurely when running a cell on an ipython
 kernel

The kernel may go idle before it processes the next input, which break
the while=True loop in run_cell() early.  Wait for an acknowledgement
of the input we've sent to the kernel before considering an idle
message to mean that the cell has been parsed.
---
 wlauto/utils/ipython.py | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/wlauto/utils/ipython.py b/wlauto/utils/ipython.py
index 6ac4d323..68f5ab84 100644
--- a/wlauto/utils/ipython.py
+++ b/wlauto/utils/ipython.py
@@ -52,6 +52,7 @@ def run_cell(kernel_client, cell):
     """Run a cell of a notebook in an ipython kernel and return its output"""
     kernel_client.execute(cell.input)
 
+    input_acknowledged = False
     outs = []
     while True:
         msg = kernel_client.get_iopub_msg()
@@ -61,11 +62,12 @@ def run_cell(kernel_client, cell):
         out = NotebookNode(output_type=msg_type)
 
         if msg_type == "status":
-            if content["execution_state"] == "idle":
+            if content["execution_state"] == "idle" and input_acknowledged:
                 break
             else:
                 continue
         elif msg_type == "pyin":
+            input_acknowledged = True
             continue
         elif msg_type == "stream":
             out.stream = content["name"]

From d12f5c65e1c5e184c7e6c1e21d1a72ffaf636822 Mon Sep 17 00:00:00 2001
From: Javi Merino <javi.merino@arm.com>
Date: Fri, 17 Apr 2015 18:50:31 +0100
Subject: [PATCH 2/3] Factor out the parsing of a valid cell run in the generic
 ipython implementation

run_cell() becomes more complicated when we add ipython version 3
support which upsets pylint because there are "too many
branches (15/12)".  Factor out part of the function to make pylint
happy.
---
 wlauto/utils/ipython.py | 58 ++++++++++++++++++++++-------------------
 1 file changed, 31 insertions(+), 27 deletions(-)

diff --git a/wlauto/utils/ipython.py b/wlauto/utils/ipython.py
index 68f5ab84..beacbb95 100644
--- a/wlauto/utils/ipython.py
+++ b/wlauto/utils/ipython.py
@@ -48,6 +48,32 @@ elif IPython:
     import_error_str = 'Unsupported IPython version {}'.format(IPython_ver_str)
 
 
+def parse_valid_output(msg):
+    """Parse a valid result from an execution of a cell in an ipython kernel"""
+    msg_type = msg["msg_type"]
+    content = msg["content"]
+    out = NotebookNode(output_type=msg_type)
+
+    if msg_type == "stream":
+        out.stream = content["name"]
+        out.text = content["data"]
+    elif msg_type in ("display_data", "pyout"):
+        for mime, data in content["data"].iteritems():
+            if mime == "text/plain":
+                attr = "text"
+            else:
+                attr = mime.split("/")[-1]
+            setattr(out, attr, data)
+    elif msg_type == "pyerr":
+        out.ename = content["ename"]
+        out.evalue = content["evalue"]
+        out.traceback = content["traceback"]
+    else:
+        raise ValueError("Unknown msg_type {}".format(msg_type))
+
+    return out
+
+
 def run_cell(kernel_client, cell):
     """Run a cell of a notebook in an ipython kernel and return its output"""
     kernel_client.execute(cell.input)
@@ -57,36 +83,14 @@ def run_cell(kernel_client, cell):
     while True:
         msg = kernel_client.get_iopub_msg()
 
-        msg_type = msg["msg_type"]
-        content = msg["content"]
-        out = NotebookNode(output_type=msg_type)
-
-        if msg_type == "status":
-            if content["execution_state"] == "idle" and input_acknowledged:
+        if msg["msg_type"] == "status":
+            if msg["content"]["execution_state"] == "idle" and input_acknowledged:
                 break
-            else:
-                continue
-        elif msg_type == "pyin":
+        elif msg["msg_type"] == "pyin":
             input_acknowledged = True
-            continue
-        elif msg_type == "stream":
-            out.stream = content["name"]
-            out.text = content["data"]
-        elif msg_type in ("display_data", "pyout"):
-            for mime, data in content["data"].iteritems():
-                if mime == "text/plain":
-                    attr = "text"
-                else:
-                    attr = mime.split("/")[-1]
-                setattr(out, attr, data)
-        elif msg_type == "pyerr":
-            out.ename = content["ename"]
-            out.evalue = content["evalue"]
-            out.traceback = content["traceback"]
         else:
-            raise ValueError("Unknown msg_type {}".format(msg_type))
-
-        outs.append(out)
+            out = parse_valid_output(msg)
+            outs.append(out)
 
     return outs
 

From e30386ce4a01c7b03a341850239b0d0c39b2b80e Mon Sep 17 00:00:00 2001
From: Javi Merino <javi.merino@arm.com>
Date: Thu, 16 Apr 2015 19:24:50 +0100
Subject: [PATCH 3/3] Add ipython version 3 support for the generic ipython
 support

---
 wlauto/utils/ipython.py | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/wlauto/utils/ipython.py b/wlauto/utils/ipython.py
index beacbb95..dfe84f98 100644
--- a/wlauto/utils/ipython.py
+++ b/wlauto/utils/ipython.py
@@ -28,20 +28,37 @@ except ImportError as import_error:
     else:
         import_error_str = import_error.message
 
+# The current code generates notebooks version 3
+NBFORMAT_VERSION = 3
+
 if IPython and (IPython.version_info[0] == 2):
     import IPython.kernel
     import IPython.nbformat.v3
 
     def read_notebook(notebook_in):
-        return IPython.nbformat.v3.reads_json(notebook_in)
+        return IPython.nbformat.v3.reads_json(notebook_in)  # pylint: disable=E1101
 
     def write_notebook(notebook, fout):
-        IPython.nbformat.v3.nbjson.JSONWriter().write(notebook, fout)
+        IPython.nbformat.v3.nbjson.JSONWriter().write(notebook, fout)  # pylint: disable=E1101
 
-    NotebookNode = IPython.nbformat.v3.NotebookNode
+    NotebookNode = IPython.nbformat.v3.NotebookNode  # pylint: disable=E1101
 
     IPYTHON_NBCONVERT = ['ipython', 'nbconvert', '--to=latex', '--post=PDF']
 
+elif IPython and (IPython.version_info[0] == 3):
+    import IPython.kernel
+    import IPython.nbformat
+
+    def read_notebook(notebook_in):
+        return IPython.nbformat.reads(notebook_in, NBFORMAT_VERSION)  # pylint: disable=E1101
+
+    def write_notebook(notebook, fout):
+        IPython.nbformat.write(notebook, fout)  # pylint: disable=E1101
+
+    NotebookNode = IPython.nbformat.NotebookNode  # pylint: disable=E1101
+
+    IPYTHON_NBCONVERT = ['ipython', 'nbconvert', '--to=pdf']
+
 elif IPython:
     # Unsupported IPython version
     IPython_ver_str = ".".join([str(n) for n in IPython.version_info])
@@ -51,12 +68,20 @@ elif IPython:
 def parse_valid_output(msg):
     """Parse a valid result from an execution of a cell in an ipython kernel"""
     msg_type = msg["msg_type"]
+    if msg_type == 'error':
+        msg_type = 'pyerr'
+    elif msg_type == 'execute_result':
+        msg_type = 'pyout'
+
     content = msg["content"]
     out = NotebookNode(output_type=msg_type)
 
     if msg_type == "stream":
         out.stream = content["name"]
-        out.text = content["data"]
+        try:
+            out.text = content['data']
+        except KeyError:
+            out.text = content['text']
     elif msg_type in ("display_data", "pyout"):
         for mime, data in content["data"].iteritems():
             if mime == "text/plain":
@@ -86,7 +111,7 @@ def run_cell(kernel_client, cell):
         if msg["msg_type"] == "status":
             if msg["content"]["execution_state"] == "idle" and input_acknowledged:
                 break
-        elif msg["msg_type"] == "pyin":
+        elif msg["msg_type"] in ('pyin', 'execute_input'):
             input_acknowledged = True
         else:
             out = parse_valid_output(msg)
@@ -111,7 +136,7 @@ def run_notebook(notebook):
             cell.outputs = run_cell(kernel_client, cell)
 
             cell.prompt_number = prompt_number
-            if cell.outputs:
+            if cell.outputs and cell.outputs[0]['output_type'] == 'pyout':
                 cell.outputs[0]["prompt_number"] = prompt_number
 
     kernel_manager.shutdown_kernel()