From 31c15b8f73713c934f7b0c64ae6fc602bea7d99c 2011-04-11 18:27:28
From: MinRK <benjaminrk@gmail.com>
Date: 2011-04-11 18:27:28
Subject: [PATCH] fix %autopx in scripts by calling run_code for each ast node

Previously, there was no call to override if %autopx was called inside
a single cell (e.g. %run script.ipy). This splits run_ast_nodes, so that
each node gets a separate call to run_code.

reviewed changes:
* remove old use of 'new'
* fix interactiveshell.run_code docstring

Additional change:
automatically load parallelmagic extension on view.activate()

---

diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py
index 89fa687..72c4de9 100644
--- a/IPython/core/interactiveshell.py
+++ b/IPython/core/interactiveshell.py
@@ -2211,15 +2211,20 @@ class InteractiveShell(Configurable, Magic):
             
         exec_count = self.execution_count
         if to_run_exec:
-            mod = ast.Module(to_run_exec)
-            self.code_to_run = code = self.compile(mod, cell_name, "exec")
-            if self.run_code(code) == 1:
-                return
-                
+            for i, node in enumerate(to_run_exec):
+                mod = ast.Module([node])
+                self.code_to_run = code = self.compile(mod, cell_name+str(i), "exec")
+                if self.run_code(code) == 1:
+                    return 1
+
         if to_run_interactive:
-            mod = ast.Interactive(to_run_interactive)
-            self.code_to_run = code = self.compile(mod, cell_name, "single")
-            return self.run_code(code)
+            for i, node in enumerate(to_run_interactive):
+                mod = ast.Interactive([node])
+                self.code_to_run = code = self.compile(mod, cell_name, "single")
+                if self.run_code(code) == 1:
+                    return 1
+
+        return 0
     
     
     # PENDING REMOVAL: this method is slated for deletion, once our new
@@ -2336,11 +2341,17 @@ class InteractiveShell(Configurable, Magic):
         When an exception occurs, self.showtraceback() is called to display a
         traceback.
 
-        Return value: a flag indicating whether the code to be run completed
-        successfully:
+        Parameters
+        ----------
+        code_obj : code object
+          A compiled code object, to be executed
+        post_execute : bool [default: True]
+          whether to call post_execute hooks after this particular execution.
 
-          - 0: successful execution.
-          - 1: an error occurred.
+        Returns
+        -------
+        0 : successful execution.
+        1 : an error occurred.
         """
 
         # Set our own excepthook in case the user code tries to call it
diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py
index ba93762..ea672a1 100755
--- a/IPython/extensions/parallelmagic.py
+++ b/IPython/extensions/parallelmagic.py
@@ -15,12 +15,10 @@
 #-----------------------------------------------------------------------------
 
 import ast
-import new
 import re
 
 from IPython.core.plugin import Plugin
 from IPython.utils.traitlets import Bool, Any, Instance
-from IPython.utils.autoattr import auto_attr
 from IPython.testing import decorators as testdec
 
 #-----------------------------------------------------------------------------
@@ -146,10 +144,12 @@ class ParalleMagic(Plugin):
             print NO_ACTIVE_VIEW
             return
 
+        # override run_cell and run_code
         self._original_run_cell = self.shell.run_cell
-        self.shell.run_cell = new.instancemethod(
-            self.pxrun_cell, self.shell, self.shell.__class__
-        )
+        self.shell.run_cell = self.pxrun_cell
+        self._original_run_code = self.shell.run_code
+        self.shell.run_code = self.pxrun_code
+
         self.autopx = True
         print "%autopx enabled"
     
@@ -158,14 +158,43 @@ class ParalleMagic(Plugin):
         """
         if self.autopx:
             self.shell.run_cell = self._original_run_cell
+            self.shell.run_code = self._original_run_code
             self.autopx = False
             print "%autopx disabled"
 
-    def pxrun_cell(self, ipself, cell, store_history=True):
+    def _maybe_display_output(self, result):
+        """Maybe display the output of a parallel result.
+
+        If self.active_view.block is True, wait for the result
+        and display the result.  Otherwise, this is a noop.
+        """
+        if self.active_view.block:
+            try:
+                result.get()
+            except:
+                self.shell.showtraceback()
+                return True
+            else:
+                targets = self.active_view.targets
+                if isinstance(targets, int):
+                    targets = [targets]
+                if targets == 'all':
+                    targets = self.active_view.client.ids
+                stdout = [s.rstrip() for s in result.stdout]
+                if any(stdout):
+                    for i,eid in enumerate(targets):
+                        print '[stdout:%i]'%eid, stdout[i]
+        return False
+
+
+    def pxrun_cell(self, cell, store_history=True):
         """drop-in replacement for InteractiveShell.run_cell.
         
         This executes code remotely, instead of in the local namespace.
+
+        See InteractiveShell.run_cell for details.
         """
+        ipself = self.shell
         raw_cell = cell
         with ipself.builtin_trap:
             cell = ipself.prefilter_manager.prefilter_lines(cell)
@@ -187,6 +216,7 @@ class ParalleMagic(Plugin):
                 ipself.execution_count += 1
                 return None
             except NameError:
+                # ignore name errors, because we don't know the remote keys
                 pass
 
         if store_history:
@@ -195,7 +225,6 @@ class ParalleMagic(Plugin):
             ipself.history_manager.store_output(ipself.execution_count)
             # Each cell is a *single* input, regardless of how many lines it has
             ipself.execution_count += 1
-        print cell
         
         if re.search(r'get_ipython\(\)\.magic\(u?"%?autopx', cell):
             self._disable_autopx()
@@ -206,23 +235,32 @@ class ParalleMagic(Plugin):
             except:
                 ipself.showtraceback()
                 return False
-            
-            if self.active_view.block:
-                try:
-                    result.get()
-                except:
-                    ipself.showtraceback()
-                else:
-                    targets = self.active_view.targets
-                    if isinstance(targets, int):
-                        targets = [targets]
-                    if targets == 'all':
-                        targets = self.active_view.client.ids
-                    stdout = [s.rstrip() for s in result.stdout]
-                    if any(stdout):
-                        for i,eid in enumerate(targets):
-                            print '[stdout:%i]'%eid, stdout[i]
+            else:
+                return self._maybe_display_output(result)
+
+    def pxrun_code(self, code_obj, post_execute=True):
+        """drop-in replacement for InteractiveShell.run_code.
+
+        This executes code remotely, instead of in the local namespace.
+
+        See InteractiveShell.run_code for details.
+        """
+        ipself = self.shell
+        # check code object for the autopx magic
+        if 'get_ipython' in code_obj.co_names and 'magic' in code_obj.co_names and \
+            any( [ isinstance(c, basestring) and 'autopx' in c for c in code_obj.co_consts ]):
+            self._disable_autopx()
             return False
+        else:
+            try:
+                result = self.active_view.execute(code_obj, block=False)
+            except:
+                ipself.showtraceback()
+                return False
+            else:
+                return self._maybe_display_output(result)
+
+
 
 
 _loaded = False
diff --git a/IPython/parallel/client/view.py b/IPython/parallel/client/view.py
index fb6903b..647729d 100644
--- a/IPython/parallel/client/view.py
+++ b/IPython/parallel/client/view.py
@@ -765,11 +765,11 @@ class DirectView(View):
             print "The IPython parallel magics (%result, %px, %autopx) only work within IPython."
         else:
             pmagic = ip.plugin_manager.get_plugin('parallelmagic')
-            if pmagic is not None:
-                pmagic.active_view = self
-            else:
-                print "You must first load the parallelmagic extension " \
-                      "by doing '%load_ext parallelmagic'"
+            if pmagic is None:
+                ip.magic_load_ext('parallelmagic')
+                pmagic = ip.plugin_manager.get_plugin('parallelmagic')
+
+            pmagic.active_view = self
 
 
 @testdec.skip_doctest