Skip to content
This repository

don't preserve fixConsole output in json #1206

Merged
merged 3 commits into from about 2 years ago

4 participants

Min RK Hans Meine Brian E. Granger Fernando Perez
Min RK
Owner

move utils.fixConsole calls closer to display, so they don't clobber the actual data stored in the notebook.

also store nonexistent input_prompt_number as null, rather than  , which is what should be drawn, not the actual value.

We need to be careful with this one, because currently we do write HTML-escaped text to the notebook (see #1205), so this will change the output. So if you open a notebook written by 0.12, plaintext output will be double-escaped until you run it again. The fixed output seems to display correctly in the old/bad code.

Let's not merge until we check if there's an easy way to avoid escaping to HTML a second time.

fixes #1205

Min RK
Owner

Just kidding - I wasn't diligent with my browser-refresh when switching branches. Bad notebooks (written by 0.12) loaded in this branch will see the HTML markup as plaintext, because the content will be double-escaped on load. Good notebooks (written by this branch) will be loaded unescaped ([0;31m in tact, output uncolored) in 0.12.

Of course, everything should be safe, so the only thing needed when moving from one to the other is to re-run the notebook to generate the outputs with/without the bug so it matches the running frontend.

I don't know if this is fixable, because the escaped HTML output is always perfectly valid as plaintext, so it's impossible to be certain about whether an extra escape should be made.

Note that this is not related to the notebook format, but a bug in the notebook frontend, which puts the wrong information in plaintext fields. However, one way to 'fix' the problem is to define this bug into the current notebook format, and increment the format with no changes. We then edit v2, and patch it to perform the escape/unescape around the files themselves. I do not think this is worth it, but it is an option.

Pinging @ellisonbg as notebook format coder-in-chief.

Hans Meine

Would it be a possible third option to increment the nb format without adding the conversion code for now? Then, if someone decides that it is worth the extra code (and possibly volunteers to contribute it), one could already know which notebooks need which treatment.

Min RK
Owner

incrementing the nbformat without adding the conversion code would mean that all your notebooks would immediately be considered unreadable, because they are the old format.

Min RK
Owner

I suppose that's imprecise - If we define the conversion as a no-op initially, then they would be readable and the old notebooks would be identifiable as you describe. New notebooks would be rejected as unreadable by IPython 0.12, and there's no way that updating the nbformat version would not cause that to happen.

The principal reason I am reluctant to fix this via the nbformat is that it is a frontend bug, and really has nothing to do with the notebook format, as evidenced by the fact that the fixes reside purely in javascript.

I would consider this the highest priority bug found in 0.12 so far, and the principal motivation for an 0.12.1 bugfix release, depending on which approach we chose.

Brian E. Granger
Owner
Brian E. Granger
Owner

I will try to have a look at this one.

Fernando Perez
Owner

I also agree that we shouldn't do a format number change unless it's really necessary, and in this case it isn't. Changing the nb format number has a pretty high cost, and we're starting to have a lot of users in the field, so we shouldn't make things hard for them without careful consideration of the benefit.

It's a bummer that this one is going to produce some glitches on load across this merge point, but there doesn't seem to be a way to avoid that...

Min RK
Owner

@ellisonbg @fperez any further thoughts on this one? I think it's the biggest bug in 0.12, and the fix has been outstanding for a over a month.

@fperez - there is a way to avoid the glitches - defining the bug into the notebook format, and incrementing the version, but I would put the costs of that (unreadability of 0.12.1 notebooks in 0.12) as much higher than the glitches themselves, which are trivially resolved by rerunning the notebook (works both directions).

I would vote for fixing the bug in the frontend (as done here), and pushing out 0.12.1 as a critical bugfix ASAP.

Fernando Perez
Owner

Should we apply this to master and cherry-pick it onto 0.12.1? I don't like cutting 0.12.1 in the middle of a bunch of other work that's being done, the notebook UI has changed significantly, etc. So I think our options are:

  • cherry pick just this onto 0.12 and call it 0.12.1. Obviously we'd also apply it to master.
  • keep going but try to stabilize very quickly so we can release 0.13 soon.

I actually think I'd prefer the latter. The notebook is so much nicer with the menus and the codemirror fixes we just merged, that I think it more than warrants being called 0.13. We could consider making a release in a 2-3 week timeframe, which would also be ideal for PyCon. Thoughts?

Hans Meine

Great idea. I also think that the new notebook is much nicer (polished) than the 0.12 one, and that releasing it soon and as 0.13 would be warranted.

BTW: In case someone thinks that the polished appearance would be diminished by doubly-escaped HTML, how about detecting this and adding a notification (similar to modern browser’s "do you want to save this password?" bars at the top) suggesting to the user to re-evaluate all cells? Again, I am not suggesting that it’s worth it, but just wanted to throw in the thought.

Brian E. Granger
Owner
Fernando Perez
Owner
Min RK
Owner

If we are doing 0.13 by March, maybe it doesn't make sense anymore to do 0.12.1, which we probably should have done a month ago, closer to when the issue came up.

Fernando Perez
Owner
Brian E. Granger
Owner
Min RK
Owner

Reviewed on IRC by @ellisonbg, and merging now.

Min RK minrk merged commit f3ee404 into from January 26, 2012
Min RK minrk closed this January 26, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
16  IPython/frontend/html/notebook/static/js/codecell.js
@@ -15,7 +15,7 @@ var IPython = (function (IPython) {
15 15
 
16 16
     var CodeCell = function (notebook) {
17 17
         this.code_mirror = null;
18  
-        this.input_prompt_number = ' ';
  18
+        this.input_prompt_number = null;
19 19
         this.is_completing = false;
20 20
         this.completion_cursor = null;
21 21
         this.outputs = [];
@@ -598,7 +598,9 @@ var IPython = (function (IPython) {
598 598
             if (last.output_type == 'stream' && json.stream == last.stream){
599 599
                 // latest output was in the same stream,
600 600
                 // so append directly into its pre tag
601  
-                this.element.find('div.'+subclass).last().find('pre').append(json.text);
  601
+                // escape ANSI & HTML specials:
  602
+                var text = utils.fixConsole(json.text);
  603
+                this.element.find('div.'+subclass).last().find('pre').append(text);
602 604
                 return;
603 605
             }
604 606
         }
@@ -647,6 +649,8 @@ var IPython = (function (IPython) {
647 649
 
648 650
     CodeCell.prototype.append_text = function (data, element, extra_class) {
649 651
         var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
  652
+        // escape ANSI & HTML specials in plaintext:
  653
+        data = utils.fixConsole(data);
650 654
         if (extra_class){
651 655
             toinsert.addClass(extra_class);
652 656
         }
@@ -753,9 +757,9 @@ var IPython = (function (IPython) {
753 757
     };
754 758
 
755 759
     CodeCell.prototype.set_input_prompt = function (number) {
756  
-        var n = number || '&nbsp;';
757  
-        this.input_prompt_number = n;
758  
-        this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
  760
+        this.input_prompt_number = number;
  761
+        var ns = number || "&nbsp;";
  762
+        this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
759 763
     };
760 764
 
761 765
 
@@ -817,7 +821,7 @@ var IPython = (function (IPython) {
817 821
         var data = {};
818 822
         data.input = this.get_code();
819 823
         data.cell_type = 'code';
820  
-        if (this.input_prompt_number !== ' ') {
  824
+        if (this.input_prompt_number) {
821 825
             data.prompt_number = this.input_prompt_number;
822 826
         };
823 827
         var outputs = [];
10  IPython/frontend/html/notebook/static/js/notebook.js
@@ -816,7 +816,7 @@ var IPython = (function (IPython) {
816 816
         var json = {};
817 817
         json.output_type = msg_type;
818 818
         if (msg_type === "stream") {
819  
-            json.text = utils.fixConsole(content.data);
  819
+            json.text = content.data;
820 820
             json.stream = content.name;
821 821
         } else if (msg_type === "display_data") {
822 822
             json = this.convert_mime_types(json, content.data);
@@ -826,11 +826,7 @@ var IPython = (function (IPython) {
826 826
         } else if (msg_type === "pyerr") {
827 827
             json.ename = content.ename;
828 828
             json.evalue = content.evalue;
829  
-            var traceback = [];
830  
-            for (var i=0; i<content.traceback.length; i++) {
831  
-                traceback.push(utils.fixConsole(content.traceback[i]));
832  
-            }
833  
-            json.traceback = traceback;
  829
+            json.traceback = content.traceback;
834 830
         };
835 831
         cell.append_output(json);
836 832
         this.dirty = true;
@@ -839,7 +835,7 @@ var IPython = (function (IPython) {
839 835
 
840 836
     Notebook.prototype.convert_mime_types = function (json, data) {
841 837
         if (data['text/plain'] !== undefined) {
842  
-            json.text = utils.fixConsole(data['text/plain']);
  838
+            json.text = data['text/plain'];
843 839
         };
844 840
         if (data['text/html'] !== undefined) {
845 841
             json.html = data['text/html'];
13  docs/examples/notebooks/00_notebook_tour.ipynb
@@ -40,7 +40,7 @@
40 40
        "output_type": "pyout", 
41 41
        "prompt_number": 1, 
42 42
        "text": [
43  
-        "u&apos;/home/fperez/ipython/ipython/docs/examples/notebooks&apos;"
  43
+        "u'/home/fperez/ipython/ipython/docs/examples/notebooks'"
44 44
        ]
45 45
       }
46 46
      ], 
@@ -157,7 +157,7 @@
157 157
        "output_type": "stream", 
158 158
        "stream": "stderr", 
159 159
        "text": [
160  
-        "ERROR: File &#96;non_existent_file.py&#96; not found."
  160
+        "ERROR: File `non_existent_file.py` not found."
161 161
        ]
162 162
       }
163 163
      ], 
@@ -178,9 +178,9 @@
178 178
        "evalue": "integer division or modulo by zero", 
179 179
        "output_type": "pyerr", 
180 180
        "traceback": [
181  
-        "<span class=\"ansired\">---------------------------------------------------------------------------</span>\n<span class=\"ansired\">ZeroDivisionError</span>                         Traceback (most recent call last)", 
182  
-        "<span class=\"ansigreen\">/home/fperez/ipython/ipython/docs/examples/notebooks/&lt;ipython-input-7-dc39888fd1d2&gt;</span> in <span class=\"ansicyan\">&lt;module&gt;</span><span class=\"ansiblue\">()</span>\n<span class=\"ansigreen\">      1</span> x <span class=\"ansiyellow\">=</span> <span class=\"ansicyan\">1</span><span class=\"ansiyellow\"></span>\n<span class=\"ansigreen\">      2</span> y <span class=\"ansiyellow\">=</span> <span class=\"ansicyan\">4</span><span class=\"ansiyellow\"></span>\n<span class=\"ansigreen\">----&gt; 3</span><span class=\"ansiyellow\"> </span>z <span class=\"ansiyellow\">=</span> y<span class=\"ansiyellow\">/</span><span class=\"ansiyellow\">(</span><span class=\"ansicyan\">1</span><span class=\"ansiyellow\">-</span>x<span class=\"ansiyellow\">)</span><span class=\"ansiyellow\"></span>\n", 
183  
-        "<span class=\"ansired\">ZeroDivisionError</span>: integer division or modulo by zero"
  181
+        "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m\n\u001b[0;31mZeroDivisionError\u001b[0m                         Traceback (most recent call last)", 
  182
+        "\u001b[0;32m/home/fperez/ipython/ipython/docs/examples/notebooks/<ipython-input-7-dc39888fd1d2>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m1\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m4\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mz\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0my\u001b[0m\u001b[0;34m/\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0mx\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 
  183
+        "\u001b[0;31mZeroDivisionError\u001b[0m: integer division or modulo by zero"
184 184
        ]
185 185
       }
186 186
      ], 
@@ -915,8 +915,7 @@
915 915
      "collapsed": true, 
916 916
      "input": [], 
917 917
      "language": "python", 
918  
-     "outputs": [], 
919  
-     "prompt_number": "&nbsp;"
  918
+     "outputs": []
920 919
     }
921 920
    ]
922 921
   }
46  docs/examples/notebooks/01_notebook_introduction.ipynb
@@ -43,13 +43,13 @@
43 43
      "outputs": [
44 44
       {
45 45
        "output_type": "pyout", 
46  
-       "prompt_number": 4, 
  46
+       "prompt_number": 1, 
47 47
        "text": [
48  
-        "&apos;This is the new IPython notebook&apos;"
  48
+        "'This is the new IPython notebook'"
49 49
        ]
50 50
       }
51 51
      ], 
52  
-     "prompt_number": 4
  52
+     "prompt_number": 1
53 53
     }, 
54 54
     {
55 55
      "cell_type": "markdown", 
@@ -82,7 +82,7 @@
82 82
        ]
83 83
       }
84 84
      ], 
85  
-     "prompt_number": 3
  85
+     "prompt_number": 2
86 86
     }, 
87 87
     {
88 88
      "cell_type": "markdown", 
@@ -113,7 +113,7 @@
113 113
        ]
114 114
       }
115 115
      ], 
116  
-     "prompt_number": 11
  116
+     "prompt_number": 3
117 117
     }, 
118 118
     {
119 119
      "cell_type": "markdown", 
@@ -237,8 +237,7 @@
237 237
       "list("
238 238
      ], 
239 239
      "language": "python", 
240  
-     "outputs": [], 
241  
-     "prompt_number": "&nbsp;"
  240
+     "outputs": []
242 241
     }, 
243 242
     {
244 243
      "cell_type": "markdown", 
@@ -277,25 +276,25 @@
277 276
        "stream": "stdout", 
278 277
        "text": [
279 278
         "{", 
280  
-        "  &quot;stdin_port&quot;: 39725, ", 
281  
-        "  &quot;ip&quot;: &quot;127.0.0.1&quot;, ", 
282  
-        "  &quot;hb_port&quot;: 52883, ", 
283  
-        "  &quot;key&quot;: &quot;e7b658da-b60b-42f6-b6b0-5098f5d2e533&quot;, ", 
284  
-        "  &quot;shell_port&quot;: 51742, ", 
285  
-        "  &quot;iopub_port&quot;: 41869", 
  279
+        "  \"stdin_port\": 53970, ", 
  280
+        "  \"ip\": \"127.0.0.1\", ", 
  281
+        "  \"hb_port\": 53971, ", 
  282
+        "  \"key\": \"30daac61-6b73-4bae-a7d9-9dca538794d5\", ", 
  283
+        "  \"shell_port\": 53968, ", 
  284
+        "  \"iopub_port\": 53969", 
286 285
         "}", 
287 286
         "", 
288 287
         "Paste the above JSON into a file, and connect with:", 
289  
-        "    $&gt; ipython &lt;app&gt; --existing &lt;file&gt;", 
  288
+        "    $> ipython <app> --existing <file>", 
290 289
         "or, if you are local, you can connect with just:", 
291  
-        "    $&gt; ipython &lt;app&gt; --existing kernel-faac4917-d0e0-467a-8467-d3c4d86a3ecc.json ", 
  290
+        "    $> ipython <app> --existing kernel-dd85d1cc-c335-44f4-bed8-f1a2173a819a.json ", 
292 291
         "or even just:", 
293  
-        "    $&gt; ipython &lt;app&gt; --existing ", 
  292
+        "    $> ipython <app> --existing ", 
294 293
         "if this is the most recent IPython session you have started."
295 294
        ]
296 295
       }
297 296
      ], 
298  
-     "prompt_number": 8
  297
+     "prompt_number": 4
299 298
     }, 
300 299
     {
301 300
      "cell_type": "markdown", 
@@ -361,14 +360,14 @@
361 360
        "text": [
362 361
         "", 
363 362
         "Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].", 
364  
-        "For more information, type &apos;help(pylab)&apos;."
  363
+        "For more information, type 'help(pylab)'."
365 364
        ]
366 365
       }, 
367 366
       {
368 367
        "output_type": "pyout", 
369  
-       "prompt_number": 12, 
  368
+       "prompt_number": 5, 
370 369
        "text": [
371  
-        "[&lt;matplotlib.lines.Line2D at 0x43a2890&gt;]"
  370
+        "[<matplotlib.lines.Line2D at 0x11165bcd0>]"
372 371
        ]
373 372
       }, 
374 373
       {
@@ -376,7 +375,7 @@
376 375
        "png": "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\n"
377 376
       }
378 377
      ], 
379  
-     "prompt_number": 12
  378
+     "prompt_number": 5
380 379
     }, 
381 380
     {
382 381
      "cell_type": "markdown", 
@@ -412,10 +411,9 @@
412 411
      "collapsed": true, 
413 412
      "input": [], 
414 413
      "language": "python", 
415  
-     "outputs": [], 
416  
-     "prompt_number": "&nbsp;"
  414
+     "outputs": []
417 415
     }
418 416
    ]
419 417
   }
420 418
  ]
421  
-}
  419
+}
2  docs/examples/notebooks/formatting.ipynb
@@ -117,7 +117,7 @@
117 117
      "collapsed": true, 
118 118
      "input": [], 
119 119
      "language": "python", 
120  
-     "outputs": [], 
  120
+     "outputs": [],
121 121
      "prompt_number": "&nbsp;"
122 122
     }
123 123
    ]
13  docs/examples/notebooks/sympy.ipynb
@@ -40,7 +40,7 @@
40 40
        "text": [
41 41
         "", 
42 42
         "Welcome to pylab, a matplotlib-based Python environment [backend: module://IPython.zmq.pylab.backend_inline].", 
43  
-        "For more information, type &apos;help(pylab)&apos;."
  43
+        "For more information, type 'help(pylab)'."
44 44
        ]
45 45
       }
46 46
      ], 
@@ -123,7 +123,7 @@
123 123
        "output_type": "pyout", 
124 124
        "prompt_number": 6, 
125 125
        "text": [
126  
-        "Add(Symbol(&apos;x&apos;), Mul(Integer(2), Symbol(&apos;y&apos;)))"
  126
+        "Add(Symbol('x'), Mul(Integer(2), Symbol('y')))"
127 127
        ]
128 128
       }
129 129
      ], 
@@ -317,10 +317,11 @@
317 317
         "", 
318 318
         "  b              ", 
319 319
         " ___             ", 
320  
-        " \\  &#96;            ", 
321  
-        "  \\   \u239b n      2\u239e", 
322  
-        "  /   \u239d2  + 6\u22c5n \u23a0", 
323  
-        " /__,            ", 
  320
+        " \u2572               ",
  321
+        "  \u2572   \u239b n      2\u239e",
  322
+        "  \u2571   \u239d2  + 6\u22c5n \u23a0",
  323
+        " \u2571               ",
  324
+        " \u203e\u203e\u203e             ",
324 325
         "n = a            "
325 326
        ]
326 327
       }
3  docs/examples/notebooks/trapezoid_rule.ipynb
@@ -110,8 +110,7 @@
110 110
      "collapsed": true, 
111 111
      "input": [], 
112 112
      "language": "python", 
113  
-     "outputs": [], 
114  
-     "prompt_number": "&nbsp;"
  113
+     "outputs": []
115 114
     }
116 115
    ]
117 116
   }
151  docs/examples/parallel/helloworld.ipynb
... ...
@@ -1,65 +1,92 @@
1 1
 {
2  
-    "nbformat": 2, 
3  
-    "metadata": {
4  
-        "name": "helloworld"
  2
+ "metadata": {
  3
+  "name": "helloworld"
  4
+ }, 
  5
+ "nbformat": 2, 
  6
+ "worksheets": [
  7
+  {
  8
+   "cells": [
  9
+    {
  10
+     "cell_type": "markdown", 
  11
+     "source": [
  12
+      "# Distributed hello world", 
  13
+      "", 
  14
+      "Originally by Ken Kinder (ken at kenkinder dom com)"
  15
+     ]
5 16
     }, 
6  
-    "worksheets": [
7  
-        {
8  
-            "cells": [
9  
-                {
10  
-                    "source": "# Distributed hello world\n\nOriginally by Ken Kinder (ken at kenkinder dom com)", 
11  
-                    "cell_type": "markdown"
12  
-                }, 
13  
-                {
14  
-                    "cell_type": "code", 
15  
-                    "language": "python", 
16  
-                    "outputs": [], 
17  
-                    "collapsed": true, 
18  
-                    "prompt_number": 3, 
19  
-                    "input": "from IPython.parallel import Client"
20  
-                }, 
21  
-                {
22  
-                    "cell_type": "code", 
23  
-                    "language": "python", 
24  
-                    "outputs": [], 
25  
-                    "collapsed": true, 
26  
-                    "prompt_number": 4, 
27  
-                    "input": "rc = Client()\nview = rc.load_balanced_view()"
28  
-                }, 
29  
-                {
30  
-                    "cell_type": "code", 
31  
-                    "language": "python", 
32  
-                    "outputs": [], 
33  
-                    "collapsed": true, 
34  
-                    "prompt_number": 5, 
35  
-                    "input": "def sleep_and_echo(t, msg):\n    import time\n    time.sleep(t)\n    return msg"
36  
-                }, 
37  
-                {
38  
-                    "cell_type": "code", 
39  
-                    "language": "python", 
40  
-                    "outputs": [], 
41  
-                    "collapsed": true, 
42  
-                    "prompt_number": 6, 
43  
-                    "input": "world = view.apply_async(sleep_and_echo, 3, 'World!')\nhello = view.apply_async(sleep_and_echo, 2, 'Hello')\n"
44  
-                }, 
45  
-                {
46  
-                    "cell_type": "code", 
47  
-                    "language": "python", 
48  
-                    "outputs": [
49  
-                        {
50  
-                            "output_type": "stream", 
51  
-                            "text": "Submitted tasks: [&apos;9e533683-d54e-4588-929e-984dd3eb6dc4&apos;] [&apos;90395f15-723f-44df-a743-a5d88cdeb6a0&apos;]\nHello"
52  
-                        }, 
53  
-                        {
54  
-                            "output_type": "stream", 
55  
-                            "text": "World!"
56  
-                        }
57  
-                    ], 
58  
-                    "collapsed": false, 
59  
-                    "prompt_number": 7, 
60  
-                    "input": "print \"Submitted tasks:\", hello.msg_ids, world.msg_ids\nprint hello.get(), world.get()"
61  
-                }
62  
-            ]
63  
-        }
64  
-    ]
  17
+    {
  18
+     "cell_type": "code", 
  19
+     "collapsed": true, 
  20
+     "input": [
  21
+      "from IPython.parallel import Client"
  22
+     ], 
  23
+     "language": "python", 
  24
+     "outputs": [], 
  25
+     "prompt_number": 1
  26
+    }, 
  27
+    {
  28
+     "cell_type": "code", 
  29
+     "collapsed": true, 
  30
+     "input": [
  31
+      "rc = Client()", 
  32
+      "view = rc.load_balanced_view()"
  33
+     ], 
  34
+     "language": "python", 
  35
+     "outputs": [], 
  36
+     "prompt_number": 2
  37
+    }, 
  38
+    {
  39
+     "cell_type": "code", 
  40
+     "collapsed": true, 
  41
+     "input": [
  42
+      "def sleep_and_echo(t, msg):", 
  43
+      "    import time", 
  44
+      "    time.sleep(t)", 
  45
+      "    return msg"
  46
+     ], 
  47
+     "language": "python", 
  48
+     "outputs": [], 
  49
+     "prompt_number": 3
  50
+    }, 
  51
+    {
  52
+     "cell_type": "code", 
  53
+     "collapsed": true, 
  54
+     "input": [
  55
+      "world = view.apply_async(sleep_and_echo, 3, 'World!')", 
  56
+      "hello = view.apply_async(sleep_and_echo, 2, 'Hello')"
  57
+     ], 
  58
+     "language": "python", 
  59
+     "outputs": [], 
  60
+     "prompt_number": 4
  61
+    }, 
  62
+    {
  63
+     "cell_type": "code", 
  64
+     "collapsed": false, 
  65
+     "input": [
  66
+      "print \"Submitted tasks:\", hello.msg_ids, world.msg_ids", 
  67
+      "print hello.get(), world.get()"
  68
+     ], 
  69
+     "language": "python", 
  70
+     "outputs": [
  71
+      {
  72
+       "output_type": "stream", 
  73
+       "stream": "stdout", 
  74
+       "text": [
  75
+        "Submitted tasks: ['dd1052e0-aa75-4b25-9d35-ecbdaf6e3ed7'] ['1b46aa21-20d1-459c-bc36-2d8d03336f74']", 
  76
+        "Hello"
  77
+       ]
  78
+      }, 
  79
+      {
  80
+       "output_type": "stream", 
  81
+       "stream": "stdout", 
  82
+       "text": [
  83
+        " World!"
  84
+       ]
  85
+      }
  86
+     ], 
  87
+     "prompt_number": 5
  88
+    }
  89
+   ]
  90
+  }
  91
+ ]
65 92
 }
357  docs/examples/parallel/parallel_mpi.ipynb
... ...
@@ -1,139 +1,224 @@
1 1
 {
2  
-    "worksheets": [
3  
-        {
4  
-            "cells": [
5  
-                {
6  
-                    "source": "# Simple usage of a set of MPI engines\n\nThis example assumes you've started a cluster of N engines (4 in this example) as part\nof an MPI world.  \n\nOur documentation describes [how to create an MPI profile](http://ipython.org/ipython-doc/dev/parallel/parallel_process.html#using-ipcluster-in-mpiexec-mpirun-mode)\nand explains [basic MPI usage of the IPython cluster](http://ipython.org/ipython-doc/dev/parallel/parallel_mpi.html).\n\n\nFor the simplest possible way to start 4 engines that belong to the same MPI world, \nyou can run this in a terminal or antoher notebook:\n\n<pre>\nipcluster start --engines=MPIExecEngineSetLauncher -n 4\n</pre>\n\nNote: to run the above in a notebook, use a *new* notebook and prepend the command with `!`, but do not run\nit in *this* notebook, as this command will block until you shut down the cluster.  To stop the cluster, use \nthe 'Interrupt' button on the left, which is the equivalent of sending `Ctrl-C` to the kernel.\n\nOnce the cluster is running, we can connect to it and open a view into it:", 
7  
-                    "cell_type": "markdown"
8  
-                }, 
9  
-                {
10  
-                    "cell_type": "code", 
11  
-                    "language": "python", 
12  
-                    "outputs": [], 
13  
-                    "collapsed": true, 
14  
-                    "prompt_number": 21, 
15  
-                    "input": "from IPython.parallel import Client\nc = Client()\nview = c[:]"
16  
-                }, 
17  
-                {
18  
-                    "source": "Let's define a simple function that ", 
19  
-                    "cell_type": "markdown"
20  
-                }, 
21  
-                {
22  
-                    "cell_type": "code", 
23  
-                    "language": "python", 
24  
-                    "outputs": [], 
25  
-                    "collapsed": true, 
26  
-                    "prompt_number": 22, 
27  
-                    "input": "@view.remote(block=True)\ndef mpi_rank():\n    from mpi4py import MPI\n    comm = MPI.COMM_WORLD\n    return comm.Get_rank()"
28  
-                }, 
29  
-                {
30  
-                    "cell_type": "code", 
31  
-                    "language": "python", 
32  
-                    "outputs": [
33  
-                        {
34  
-                            "output_type": "pyout", 
35  
-                            "prompt_number": 23, 
36  
-                            "text": "[3, 0, 2, 1]"
37  
-                        }
38  
-                    ], 
39  
-                    "collapsed": false, 
40  
-                    "prompt_number": 23, 
41  
-                    "input": "mpi_rank()"
42  
-                }, 
43  
-                {
44  
-                    "source": "For interactive convenience, we load the parallel magic extensions and make this view\nthe active one for the automatic parallelism magics.\n\nThis is not necessary and in production codes likely won't be used, as the engines will \nload their own MPI codes separately.  But it makes it easy to illustrate everything from\nwithin a single notebook here.", 
45  
-                    "cell_type": "markdown"
46  
-                }, 
47  
-                {
48  
-                    "cell_type": "code", 
49  
-                    "language": "python", 
50  
-                    "outputs": [], 
51  
-                    "collapsed": true, 
52  
-                    "prompt_number": 4, 
53  
-                    "input": "%load_ext parallelmagic\nview.activate()"
54  
-                }, 
55  
-                {
56  
-                    "source": "Use the autopx magic to make the rest of this cell execute on the engines instead\nof locally", 
57  
-                    "cell_type": "markdown"
58  
-                }, 
59  
-                {
60  
-                    "cell_type": "code", 
61  
-                    "language": "python", 
62  
-                    "outputs": [], 
63  
-                    "collapsed": true, 
64  
-                    "prompt_number": 24, 
65  
-                    "input": "view.block = True"
66  
-                }, 
67  
-                {
68  
-                    "cell_type": "code", 
69  
-                    "language": "python", 
70  
-                    "outputs": [
71  
-                        {
72  
-                            "output_type": "stream", 
73  
-                            "stream": "stdout", 
74  
-                            "text": "%autopx enabled\n\n"
75  
-                        }
76  
-                    ], 
77  
-                    "collapsed": false, 
78  
-                    "prompt_number": 32, 
79  
-                    "input": "%autopx"
80  
-                }, 
81  
-                {
82  
-                    "source": "With autopx enabled, the next cell will actually execute *entirely on each engine*:", 
83  
-                    "cell_type": "markdown"
84  
-                }, 
85  
-                {
86  
-                    "cell_type": "code", 
87  
-                    "language": "python", 
88  
-                    "outputs": [], 
89  
-                    "collapsed": true, 
90  
-                    "prompt_number": 29, 
91  
-                    "input": "from mpi4py import MPI\n\ncomm = MPI.COMM_WORLD\nsize = comm.Get_size()\nrank = comm.Get_rank()\n\nif rank == 0:\n   data = [(i+1)**2 for i in range(size)]\nelse:\n   data = None\ndata = comm.scatter(data, root=0)\n\nassert data == (rank+1)**2, 'data=%s, rank=%s' % (data, rank)"
92  
-                }, 
93  
-                {
94  
-                    "source": "Though the assertion at the end of the previous block validated the code, we can now \npull the 'data' variable from all the nodes for local inspection.\nFirst, don't forget to toggle off `autopx` mode so code runs again in the notebook:\n", 
95  
-                    "cell_type": "markdown"
96  
-                }, 
97  
-                {
98  
-                    "cell_type": "code", 
99  
-                    "language": "python", 
100  
-                    "outputs": [
101  
-                        {
102  
-                            "output_type": "stream", 
103  
-                            "stream": "stdout", 
104  
-                            "text": "%autopx disabled\n\n"
105  
-                        }
106  
-                    ], 
107  
-                    "collapsed": false, 
108  
-                    "prompt_number": 33, 
109  
-                    "input": "%autopx"
110  
-                }, 
111  
-                {
112  
-                    "cell_type": "code", 
113  
-                    "language": "python", 
114  
-                    "outputs": [
115  
-                        {
116  
-                            "output_type": "pyout", 
117  
-                            "prompt_number": 34, 
118  
-                            "text": "[16, 1, 9, 4]"
119  
-                        }
120  
-                    ], 
121  
-                    "collapsed": false, 
122  
-                    "prompt_number": 34, 
123  
-                    "input": "view['data']"
124  
-                }, 
125  
-                {
126  
-                    "input": "", 
127  
-                    "cell_type": "code", 
128  
-                    "collapsed": true, 
129  
-                    "language": "python", 
130  
-                    "outputs": []
131  
-                }
132  
-            ]
133  
-        }
134  
-    ], 
135  
-    "metadata": {
136  
-        "name": "parallel_mpi"
  2
+ "metadata": {
  3
+  "name": "parallel_mpi"
  4
+ }, 
  5
+ "nbformat": 2, 
  6
+ "worksheets": [
  7
+  {
  8
+   "cells": [
  9
+    {
  10
+     "cell_type": "markdown", 
  11
+     "source": [
  12
+      "# Simple usage of a set of MPI engines", 
  13
+      "", 
  14
+      "This example assumes you've started a cluster of N engines (4 in this example) as part", 
  15
+      "of an MPI world.  ", 
  16
+      "", 
  17
+      "Our documentation describes [how to create an MPI profile](http://ipython.org/ipython-doc/dev/parallel/parallel_process.html#using-ipcluster-in-mpiexec-mpirun-mode)", 
  18
+      "and explains [basic MPI usage of the IPython cluster](http://ipython.org/ipython-doc/dev/parallel/parallel_mpi.html).", 
  19
+      "", 
  20
+      "", 
  21
+      "For the simplest possible way to start 4 engines that belong to the same MPI world, ", 
  22
+      "you can run this in a terminal or antoher notebook:", 
  23
+      "", 
  24
+      "<pre>", 
  25
+      "ipcluster start --engines=MPI -n 4", 
  26
+      "</pre>", 
  27
+      "", 
  28
+      "Note: to run the above in a notebook, use a *new* notebook and prepend the command with `!`, but do not run", 
  29
+      "it in *this* notebook, as this command will block until you shut down the cluster.  To stop the cluster, use ", 
  30
+      "the 'Interrupt' button on the left, which is the equivalent of sending `Ctrl-C` to the kernel.", 
  31
+      "", 
  32
+      "Once the cluster is running, we can connect to it and open a view into it:"
  33
+     ]
137 34
     }, 
138  
-    "nbformat": 2
  35
+    {
  36
+     "cell_type": "code", 
  37
+     "collapsed": true, 
  38
+     "input": [
  39
+      "from IPython.parallel import Client", 
  40
+      "c = Client()", 
  41
+      "view = c[:]"
  42
+     ], 
  43
+     "language": "python", 
  44
+     "outputs": [], 
  45
+     "prompt_number": 21
  46
+    }, 
  47
+    {
  48
+     "cell_type": "markdown", 
  49
+     "source": [
  50
+      "Let's define a simple function that gets the MPI rank from each engine."
  51
+     ]
  52
+    }, 
  53
+    {
  54
+     "cell_type": "code", 
  55
+     "collapsed": true, 
  56
+     "input": [
  57
+      "@view.remote(block=True)", 
  58
+      "def mpi_rank():", 
  59
+      "    from mpi4py import MPI", 
  60
+      "    comm = MPI.COMM_WORLD", 
  61
+      "    return comm.Get_rank()"
  62
+     ], 
  63
+     "language": "python", 
  64
+     "outputs": [], 
  65
+     "prompt_number": 22
  66
+    }, 
  67
+    {
  68
+     "cell_type": "code", 
  69
+     "collapsed": false, 
  70
+     "input": [
  71
+      "mpi_rank()"
  72
+     ], 
  73
+     "language": "python", 
  74
+     "outputs": [
  75
+      {
  76
+       "output_type": "pyout", 
  77
+       "prompt_number": 23, 
  78
+       "text": [
  79
+        "[3, 0, 2, 1]"
  80
+       ]
  81
+      }
  82
+     ], 
  83
+     "prompt_number": 23
  84
+    }, 
  85
+    {
  86
+     "cell_type": "markdown", 
  87
+     "source": [
  88
+      "For interactive convenience, we load the parallel magic extensions and make this view", 
  89
+      "the active one for the automatic parallelism magics.", 
  90
+      "", 
  91
+      "This is not necessary and in production codes likely won't be used, as the engines will ", 
  92
+      "load their own MPI codes separately.  But it makes it easy to illustrate everything from", 
  93
+      "within a single notebook here."
  94
+     ]
  95
+    }, 
  96
+    {
  97
+     "cell_type": "code", 
  98
+     "collapsed": true, 
  99
+     "input": [
  100
+      "%load_ext parallelmagic", 
  101
+      "view.activate()"
  102
+     ], 
  103
+     "language": "python", 
  104
+     "outputs": [], 
  105
+     "prompt_number": 4
  106
+    }, 
  107
+    {
  108
+     "cell_type": "markdown", 
  109
+     "source": [
  110
+      "Use the autopx magic to make the rest of this cell execute on the engines instead", 
  111
+      "of locally"
  112
+     ]
  113
+    }, 
  114
+    {
  115
+     "cell_type": "code", 
  116
+     "collapsed": true, 
  117
+     "input": [
  118
+      "view.block = True"
  119
+     ], 
  120
+     "language": "python", 
  121
+     "outputs": [], 
  122
+     "prompt_number": 24
  123
+    }, 
  124
+    {
  125
+     "cell_type": "code", 
  126
+     "collapsed": false, 
  127
+     "input": [
  128
+      "%autopx"
  129
+     ], 
  130
+     "language": "python", 
  131
+     "outputs": [
  132
+      {
  133
+       "output_type": "stream", 
  134
+       "stream": "stdout", 
  135
+       "text": [
  136
+        "%autopx enabled"
  137
+       ]
  138
+      }
  139
+     ], 
  140
+     "prompt_number": 32
  141
+    }, 
  142
+    {
  143
+     "cell_type": "markdown", 
  144
+     "source": [
  145
+      "With autopx enabled, the next cell will actually execute *entirely on each engine*:"
  146
+     ]
  147
+    }, 
  148
+    {
  149
+     "cell_type": "code", 
  150
+     "collapsed": true, 
  151
+     "input": [
  152
+      "from mpi4py import MPI", 
  153
+      "", 
  154
+      "comm = MPI.COMM_WORLD", 
  155
+      "size = comm.Get_size()", 
  156
+      "rank = comm.Get_rank()", 
  157
+      "", 
  158
+      "if rank == 0:", 
  159
+      "   data = [(i+1)**2 for i in range(size)]", 
  160
+      "else:", 
  161
+      "   data = None", 
  162
+      "data = comm.scatter(data, root=0)", 
  163
+      "", 
  164
+      "assert data == (rank+1)**2, 'data=%s, rank=%s' % (data, rank)"
  165
+     ], 
  166
+     "language": "python", 
  167
+     "outputs": [], 
  168
+     "prompt_number": 29
  169
+    }, 
  170
+    {
  171
+     "cell_type": "markdown", 
  172
+     "source": [
  173
+      "Though the assertion at the end of the previous block validated the code, we can now ", 
  174
+      "pull the 'data' variable from all the nodes for local inspection.", 
  175
+      "First, don't forget to toggle off `autopx` mode so code runs again in the notebook:"
  176
+     ]
  177
+    }, 
  178
+    {
  179
+     "cell_type": "code", 
  180
+     "collapsed": false, 
  181
+     "input": [
  182
+      "%autopx"
  183
+     ], 
  184
+     "language": "python", 
  185
+     "outputs": [
  186
+      {
  187
+       "output_type": "stream", 
  188
+       "stream": "stdout", 
  189
+       "text": [
  190
+        "%autopx disabled"
  191
+       ]
  192
+      }
  193
+     ], 
  194
+     "prompt_number": 33
  195
+    }, 
  196
+    {
  197
+     "cell_type": "code", 
  198
+     "collapsed": false, 
  199
+     "input": [
  200
+      "view['data']"
  201
+     ], 
  202
+     "language": "python", 
  203
+     "outputs": [
  204
+      {
  205
+       "output_type": "pyout", 
  206
+       "prompt_number": 34, 
  207
+       "text": [
  208
+        "[16, 1, 9, 4]"
  209
+       ]
  210
+      }
  211
+     ], 
  212
+     "prompt_number": 34
  213
+    }, 
  214
+    {
  215
+     "cell_type": "code", 
  216
+     "collapsed": true, 
  217
+     "input": [], 
  218
+     "language": "python", 
  219
+     "outputs": []
  220
+    }
  221
+   ]
  222
+  }
  223
+ ]
139 224
 }
174  docs/examples/parallel/taskmap.ipynb
... ...
@@ -1,71 +1,109 @@
1 1
 {
2  
-    "nbformat": 2, 
3  
-    "metadata": {
4  
-        "name": "taskmap"
  2
+ "metadata": {
  3
+  "name": "taskmap"
  4
+ }, 
  5
+ "nbformat": 2, 
  6
+ "worksheets": [
  7
+  {
  8
+   "cells": [
  9
+    {
  10
+     "cell_type": "markdown", 
  11
+     "source": [
  12
+      "# Load balanced map and parallel function decorator"
  13
+     ]
5 14
     }, 
6  
-    "worksheets": [
7  
-        {
8  
-            "cells": [
9  
-                {
10  
-                    "source": "# Load balanced map and parallel function decorator", 
11  
-                    "cell_type": "markdown"
12  
-                }, 
13  
-                {
14  
-                    "cell_type": "code", 
15  
-                    "language": "python", 
16  
-                    "outputs": [], 
17  
-                    "collapsed": true, 
18  
-                    "prompt_number": 4, 
19  
-                    "input": "from IPython.parallel import Client"
20  
-                }, 
21  
-                {
22  
-                    "cell_type": "code", 
23  
-                    "language": "python", 
24  
-                    "outputs": [], 
25  
-                    "collapsed": true, 
26  
-                    "prompt_number": 5, 
27  
-                    "input": "rc = Client()\nv = rc.load_balanced_view()"
28  
-                }, 
29  
-                {
30  
-                    "cell_type": "code", 
31  
-                    "language": "python", 
32  
-                    "outputs": [
33  
-                        {
34  
-                            "output_type": "stream", 
35  
-                            "text": "Simple, default map:  [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]"
36  
-                        }
37  
-                    ], 
38  
-                    "collapsed": false, 
39  
-                    "prompt_number": 6, 
40  
-                    "input": "result = v.map(lambda x: 2*x, range(10))\nprint \"Simple, default map: \", list(result)"
41  
-                }, 
42  
-                {
43  
-                    "cell_type": "code", 
44  
-                    "language": "python", 
45  
-                    "outputs": [
46  
-                        {
47  
-                            "output_type": "stream", 
48  
-                            "text": "Submitted tasks, got ids:  [&apos;2a25ff3f-f0d0-4428-909a-3fe808ca61f9&apos;, &apos;edd42168-fac2-4b3f-a696-ce61b37aa71d&apos;, &apos;8a548908-7812-44e6-a8b1-68e941bee608&apos;, &apos;26435a77-fe86-49b6-b59f-de864d59c99f&apos;, &apos;6750c7b4-2168-49ec-bcc4-feb1e17c5e53&apos;, &apos;117240d1-5dfc-4783-948f-e9523b2b2f6a&apos;, &apos;6de16d46-f2e2-49bd-8180-e43d1d875529&apos;, &apos;3d372b84-0c68-4315-92c8-a080c68478b7&apos;, &apos;43acedae-e35c-4a17-87f0-9e5e672500f7&apos;, &apos;eb71dd1f-9500-4375-875d-c2c42999848c&apos;]\nUsing a mapper:  [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]"
49  
-                        }
50  
-                    ], 
51  
-                    "collapsed": false, 
52  
-                    "prompt_number": 7, 
53  
-                    "input": "ar = v.map_async(lambda x: 2*x, range(10))\nprint \"Submitted tasks, got ids: \", ar.msg_ids\nresult = ar.get()\nprint \"Using a mapper: \", result"
54  
-                }, 
55  
-                {
56  
-                    "cell_type": "code", 
57  
-                    "language": "python", 
58  
-                    "outputs": [
59  
-                        {
60  
-                            "output_type": "stream", 
61  
-                            "text": "Using a parallel function:  [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]"
62  
-                        }
63  
-                    ], 
64  
-                    "collapsed": false, 
65  
-                    "prompt_number": 8, 
66