parallelmagic.py
202 lines
| 6.5 KiB
| text/x-python
|
PythonLexer
Brian Granger
|
r2312 | #!/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 | ||||
Brian Granger
|
r2738 | from IPython.core.plugin import Plugin | ||
Brian Granger
|
r2732 | from IPython.utils.traitlets import Bool, Any, Instance | ||
Brian Granger
|
r2312 | from IPython.utils.autoattr import auto_attr | ||
Fernando Perez
|
r2415 | from IPython.testing import decorators as testdec | ||
Brian Granger
|
r2312 | |||
#----------------------------------------------------------------------------- | ||||
# Definitions of magic functions for use with IPython | ||||
#----------------------------------------------------------------------------- | ||||
NO_ACTIVE_MULTIENGINE_CLIENT = """ | ||||
Use activate() on a MultiEngineClient object to activate it for magics. | ||||
""" | ||||
Brian Granger
|
r2738 | class ParalleMagic(Plugin): | ||
Brian Granger
|
r2312 | """A component to manage the %result, %px and %autopx magics.""" | ||
active_multiengine_client = Any() | ||||
verbose = Bool(False, config=True) | ||||
Brian Granger
|
r2760 | shell = Instance('IPython.core.interactiveshell.InteractiveShellABC') | ||
Brian Granger
|
r2312 | |||
Brian Granger
|
r2740 | def __init__(self, shell=None, config=None): | ||
super(ParalleMagic, self).__init__(shell=shell, config=config) | ||||
Brian Granger
|
r2312 | self._define_magics() | ||
# A flag showing if autopx is activated or not | ||||
self.autopx = False | ||||
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) | ||||
Fernando Perez
|
r2415 | @testdec.skip_doctest | ||
Brian Granger
|
r2312 | 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]: | ||||
<Results List> | ||||
[0] In [6]: a = 10 | ||||
[1] In [6]: a = 10 | ||||
In [22]: %result 6 | ||||
Out[22]: | ||||
<Results List> | ||||
[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 | ||||
Fernando Perez
|
r2415 | @testdec.skip_doctest | ||
Brian Granger
|
r2312 | 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]: | ||||
<Results List> | ||||
[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 | ||||
Fernando Perez
|
r2415 | @testdec.skip_doctest | ||
Brian Granger
|
r2312 | 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 | ||||
<Results List> | ||||
[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): | ||||
Fernando Perez
|
r3096 | """Enable %autopx mode by saving the original run_source and installing | ||
pxrun_source. | ||||
Brian Granger
|
r2312 | """ | ||
if self.active_multiengine_client is None: | ||||
print NO_ACTIVE_MULTIENGINE_CLIENT | ||||
return | ||||
Fernando Perez
|
r3096 | self._original_run_source = self.shell.run_source | ||
self.shell.run_source = new.instancemethod( | ||||
self.pxrun_source, self.shell, self.shell.__class__ | ||||
Brian Granger
|
r2312 | ) | ||
self.autopx = True | ||||
print "%autopx enabled" | ||||
def _disable_autopx(self): | ||||
Fernando Perez
|
r3096 | """Disable %autopx by restoring the original InteractiveShell.run_source. | ||
""" | ||||
Brian Granger
|
r2312 | if self.autopx: | ||
Fernando Perez
|
r3096 | self.shell.run_source = self._original_run_source | ||
Brian Granger
|
r2312 | self.autopx = False | ||
print "%autopx disabled" | ||||
Fernando Perez
|
r3096 | def pxrun_source(self, ipself, source, filename="<input>", symbol="single"): | ||
"""A parallel replacement for InteractiveShell.run_source.""" | ||||
Brian Granger
|
r2312 | |||
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: | ||||
Brian Granger
|
r2740 | plugin = ParalleMagic(shell=ip, config=ip.config) | ||
Brian Granger
|
r2738 | ip.plugin_manager.register_plugin('parallel_magic', plugin) | ||
Brian Granger
|
r2312 | _loaded = True | ||