diff --git a/IPython/config/profile/ipython_config_cluster.py b/IPython/config/profile/ipython_config_cluster.py index 3273396..fd795a4 100644 --- a/IPython/config/profile/ipython_config_cluster.py +++ b/IPython/config/profile/ipython_config_cluster.py @@ -14,4 +14,11 @@ from IPython.kernel.client import * if hasattr(c.Global, 'exec_lines'): c.Global.exec_lines.append(lines) else: - c.Global.exec_lines = [lines] \ No newline at end of file + c.Global.exec_lines = [lines] + +# Load the parallelmagic extension to enable %result, %px, %autopx magics. +if hasattr(c.Global, 'extensions'): + c.Global.extensions.append('parallelmagic') +else: + c.Global.extensions = ['parallelmagic'] + diff --git a/IPython/core/builtin_trap.py b/IPython/core/builtin_trap.py index ac59cae..8ddbcf7 100644 --- a/IPython/core/builtin_trap.py +++ b/IPython/core/builtin_trap.py @@ -86,6 +86,7 @@ class BuiltinTrap(Component): """Store ipython references in the __builtin__ namespace.""" self.add_builtin('exit', Quitter(self.shell, 'exit')) self.add_builtin('quit', Quitter(self.shell, 'quit')) + self.add_builtin('get_ipython', self.shell.get_ipython) # Recursive reload function try: diff --git a/IPython/extensions/cluster_magic.py b/IPython/extensions/cluster_magic.py deleted file mode 100644 index fb8fa37..0000000 --- a/IPython/extensions/cluster_magic.py +++ /dev/null @@ -1,189 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - -"""Magic command interface for interactive parallel work.""" - -#----------------------------------------------------------------------------- -# Copyright (C) 2008-2009 The IPython Development Team -# -# Distributed under the terms of the BSD License. The full license is in -# the file COPYING, distributed as part of this software. -#----------------------------------------------------------------------------- - -#----------------------------------------------------------------------------- -# Imports -#----------------------------------------------------------------------------- - -from IPython.core.component import Component -from IPython.utils.traitlets import Bool, Any -from IPython.utils.autoattr import auto_attr - - -#------------------------------------------------------------------------------- -# Definitions of magic functions for use with IPython -#------------------------------------------------------------------------------- - -NO_ACTIVE_MULTIENGINE_CLIENT = """ -Error: No Controller is activated -Use activate() on a MultiEngineClient object to activate it for magics. -""" - - -class ParalleMagicComponent(Component): - - active_multiengine_client = Any() - verbose = Bool(False, config=True) - - def __init__(self, parent, name=None, config=None): - super(ParalleMagicComponent, self).__init__(parent, name=name, config=config) - self._define_magics() - - # Access other components like this rather than by a regular attribute. - # This won't lookup the InteractiveShell object until it is used and - # then it is cached. This is both efficient and couples this class - # more loosely to InteractiveShell. - @auto_attr - def shell(self): - return Component.get_instances( - root=self.root, - klass='IPython.core.iplib.InteractiveShell')[0] - - def _define_magics(self): - self.shell.define_magic('result', self.magic_result) - self.shell.define_magic('px', self.magic_px) - self.shell.define_magic('autopx', self.magix_autopx) - - def magic_result(self, ipself, parameter_s=''): - """Print the result of command i on all engines of the active controller. - - To activate a controller in IPython, first create it and then call - the activate() method. - - Then you can do the following: - - >>> result # Print the latest result - Printing result... - [127.0.0.1:0] In [1]: b = 10 - [127.0.0.1:1] In [1]: b = 10 - - >>> result 0 # Print result 0 - In [14]: result 0 - Printing result... - [127.0.0.1:0] In [0]: a = 5 - [127.0.0.1:1] In [0]: a = 5 - """ - if self.active_multiengine_client is None: - print NO_ACTIVE_MULTIENGINE_CLIENT - return - - try: - index = int(parameter_s) - except: - index = None - result = self.active_multiengine_client.get_result(index) - return result - - # def magic_px(self,parameter_s=''): - # """Executes the given python command on the active IPython Controller. - # - # To activate a Controller in IPython, first create it and then call - # the activate() method. - # - # Then you can do the following: - # - # >>> %px a = 5 # Runs a = 5 on all nodes - # """ - # - # try: - # activeController = __IPYTHON__.activeController - # except AttributeError: - # print NO_ACTIVE_CONTROLLER - # else: - # print "Parallel execution on engines: %s" % activeController.targets - # result = activeController.execute(parameter_s) - # return result - # - # def pxrunsource(self, source, filename="", symbol="single"): - # - # try: - # code = self.compile(source, filename, symbol) - # except (OverflowError, SyntaxError, ValueError): - # # Case 1 - # self.showsyntaxerror(filename) - # return None - # - # if code is None: - # # Case 2 - # return True - # - # # Case 3 - # # Because autopx is enabled, we now call executeAll or disable autopx if - # # %autopx or autopx has been called - # if 'get_ipython().magic("%autopx' in source or 'get_ipython().magic("autopx' in source: - # _disable_autopx(self) - # return False - # else: - # try: - # result = self.activeController.execute(source) - # except: - # self.showtraceback() - # else: - # print result.__repr__() - # return False - # - # def magic_autopx(self, parameter_s=''): - # """Toggles auto parallel mode for the active IPython Controller. - # - # To activate a Controller in IPython, first create it and then call - # the activate() method. - # - # Then you can do the following: - # - # >>> %autopx # Now all commands are executed in parallel - # Auto Parallel Enabled - # Type %autopx to disable - # ... - # >>> %autopx # Now all commands are locally executed - # Auto Parallel Disabled - # """ - # - # if hasattr(self, 'autopx'): - # if self.autopx == True: - # _disable_autopx(self) - # else: - # _enable_autopx(self) - # else: - # _enable_autopx(self) - # - # def _enable_autopx(self): - # """Enable %autopx mode by saving the original runsource and installing - # pxrunsource. - # """ - # try: - # activeController = __IPYTHON__.activeController - # except AttributeError: - # print "No active RemoteController found, use RemoteController.activate()." - # else: - # self._original_runsource = self.runsource - # self.runsource = new.instancemethod(pxrunsource, self, self.__class__) - # self.autopx = True - # print "Auto Parallel Enabled\nType %autopx to disable" - # - # def _disable_autopx(self): - # """Disable %autopx by restoring the original runsource.""" - # if hasattr(self, 'autopx'): - # if self.autopx == True: - # self.runsource = self._original_runsource - # self.autopx = False - # print "Auto Parallel Disabled" - - -_loaded = False - -def load_ipython_extension(ip): - """Load the extension in IPython as a hook.""" - global _loaded - if not _loaded: - prd = ParalleMagicComponent(ip, name='parallel_magic') - _loaded = True - diff --git a/IPython/extensions/parallelmagic.py b/IPython/extensions/parallelmagic.py new file mode 100644 index 0000000..f72b3b6 --- /dev/null +++ b/IPython/extensions/parallelmagic.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python +# encoding: utf-8 + +"""Magic command interface for interactive parallel work.""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2008-2009 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Imports +#----------------------------------------------------------------------------- + +import new + +from IPython.core.component import Component +from IPython.utils.traitlets import Bool, Any +from IPython.utils.autoattr import auto_attr + +#----------------------------------------------------------------------------- +# Definitions of magic functions for use with IPython +#----------------------------------------------------------------------------- + + +NO_ACTIVE_MULTIENGINE_CLIENT = """ +Use activate() on a MultiEngineClient object to activate it for magics. +""" + + +class ParalleMagicComponent(Component): + """A component to manage the %result, %px and %autopx magics.""" + + active_multiengine_client = Any() + verbose = Bool(False, config=True) + + def __init__(self, parent, name=None, config=None): + super(ParalleMagicComponent, self).__init__(parent, name=name, config=config) + self._define_magics() + # A flag showing if autopx is activated or not + self.autopx = False + + # Access other components like this rather than by a regular attribute. + # This won't lookup the InteractiveShell object until it is used and + # then it is cached. This is both efficient and couples this class + # more loosely to InteractiveShell. + @auto_attr + def shell(self): + return Component.get_instances( + root=self.root, + klass='IPython.core.iplib.InteractiveShell')[0] + + def _define_magics(self): + """Define the magic functions.""" + self.shell.define_magic('result', self.magic_result) + self.shell.define_magic('px', self.magic_px) + self.shell.define_magic('autopx', self.magic_autopx) + + def magic_result(self, ipself, parameter_s=''): + """Print the result of command i on all engines.. + + To use this a :class:`MultiEngineClient` instance must be created + and then activated by calling its :meth:`activate` method. + + Then you can do the following:: + + In [23]: %result + Out[23]: + + [0] In [6]: a = 10 + [1] In [6]: a = 10 + + In [22]: %result 6 + Out[22]: + + [0] In [6]: a = 10 + [1] In [6]: a = 10 + """ + if self.active_multiengine_client is None: + print NO_ACTIVE_MULTIENGINE_CLIENT + return + + try: + index = int(parameter_s) + except: + index = None + result = self.active_multiengine_client.get_result(index) + return result + + def magic_px(self, ipself, parameter_s=''): + """Executes the given python command in parallel. + + To use this a :class:`MultiEngineClient` instance must be created + and then activated by calling its :meth:`activate` method. + + Then you can do the following:: + + In [24]: %px a = 5 + Parallel execution on engines: all + Out[24]: + + [0] In [7]: a = 5 + [1] In [7]: a = 5 + """ + + if self.active_multiengine_client is None: + print NO_ACTIVE_MULTIENGINE_CLIENT + return + print "Parallel execution on engines: %s" % self.active_multiengine_client.targets + result = self.active_multiengine_client.execute(parameter_s) + return result + + def magic_autopx(self, ipself, parameter_s=''): + """Toggles auto parallel mode. + + To use this a :class:`MultiEngineClient` instance must be created + and then activated by calling its :meth:`activate` method. Once this + is called, all commands typed at the command line are send to + the engines to be executed in parallel. To control which engine + are used, set the ``targets`` attributed of the multiengine client + before entering ``%autopx`` mode. + + Then you can do the following:: + + In [25]: %autopx + %autopx to enabled + + In [26]: a = 10 + + [0] In [8]: a = 10 + [1] In [8]: a = 10 + + + In [27]: %autopx + %autopx disabled + """ + if self.autopx: + self._disable_autopx() + else: + self._enable_autopx() + + def _enable_autopx(self): + """Enable %autopx mode by saving the original runsource and installing + pxrunsource. + """ + if self.active_multiengine_client is None: + print NO_ACTIVE_MULTIENGINE_CLIENT + return + + self._original_runsource = self.shell.runsource + self.shell.runsource = new.instancemethod( + self.pxrunsource, self.shell, self.shell.__class__ + ) + self.autopx = True + print "%autopx enabled" + + def _disable_autopx(self): + """Disable %autopx by restoring the original InteractiveShell.runsource.""" + if self.autopx: + self.shell.runsource = self._original_runsource + self.autopx = False + print "%autopx disabled" + + def pxrunsource(self, ipself, source, filename="", symbol="single"): + """A parallel replacement for InteractiveShell.runsource.""" + + try: + code = ipself.compile(source, filename, symbol) + except (OverflowError, SyntaxError, ValueError): + # Case 1 + ipself.showsyntaxerror(filename) + return None + + if code is None: + # Case 2 + return True + + # Case 3 + # Because autopx is enabled, we now call executeAll or disable autopx if + # %autopx or autopx has been called + if 'get_ipython().magic("%autopx' in source or 'get_ipython().magic("autopx' in source: + self._disable_autopx() + return False + else: + try: + result = self.active_multiengine_client.execute(source) + except: + ipself.showtraceback() + else: + print result.__repr__() + return False + + +_loaded = False + + +def load_ipython_extension(ip): + """Load the extension in IPython.""" + global _loaded + if not _loaded: + prd = ParalleMagicComponent(ip, name='parallel_magic') + _loaded = True + diff --git a/IPython/kernel/multiengineclient.py b/IPython/kernel/multiengineclient.py index 0f3b406..256c174 100644 --- a/IPython/kernel/multiengineclient.py +++ b/IPython/kernel/multiengineclient.py @@ -263,9 +263,13 @@ class InteractiveMultiEngineClient(object): """ try: - __IPYTHON__.activeController = self + # This is injected into __builtins__. + ip = get_ipython() except NameError: - print "The IPython Controller magics only work within IPython." + print "The IPython parallel magics (%result, %px, %autopx) only work within IPython." + else: + pmagic = ip.get_component('parallel_magic') + pmagic.active_multiengine_client = self def __setitem__(self, key, value): """Add a dictionary interface for pushing/pulling.