##// END OF EJS Templates
Update parallelmagics to new magic API.
Fernando Perez -
Show More
@@ -1,325 +1,330 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 =============
3 =============
4 parallelmagic
4 parallelmagic
5 =============
5 =============
6
6
7 Magic command interface for interactive parallel work.
7 Magic command interface for interactive parallel work.
8
8
9 Usage
9 Usage
10 =====
10 =====
11
11
12 ``%autopx``
12 ``%autopx``
13
13
14 @AUTOPX_DOC@
14 @AUTOPX_DOC@
15
15
16 ``%px``
16 ``%px``
17
17
18 @PX_DOC@
18 @PX_DOC@
19
19
20 ``%result``
20 ``%result``
21
21
22 @RESULT_DOC@
22 @RESULT_DOC@
23
23
24 """
24 """
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Copyright (C) 2008-2011 The IPython Development Team
27 # Copyright (C) 2008-2011 The IPython Development Team
28 #
28 #
29 # Distributed under the terms of the BSD License. The full license is in
29 # Distributed under the terms of the BSD License. The full license is in
30 # the file COPYING, distributed as part of this software.
30 # the file COPYING, distributed as part of this software.
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Imports
34 # Imports
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 import ast
37 import ast
38 import re
38 import re
39
39
40 from IPython.core.magic import Magics, register_magics, line_magic
40 from IPython.core.plugin import Plugin
41 from IPython.core.plugin import Plugin
41 from IPython.utils.traitlets import Bool, Any, Instance
42 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.testing.skipdoctest import skip_doctest
43 from IPython.utils.traitlets import Bool, Any, Instance
43
44
44 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
45 # Definitions of magic functions for use with IPython
46 # Definitions of magic functions for use with IPython
46 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
47
48
48
49 NO_ACTIVE_VIEW = """
49 NO_ACTIVE_VIEW = """
50 Use activate() on a DirectView object to activate it for magics.
50 Use activate() on a DirectView object to activate it for magics.
51 """
51 """
52
52
53
53
54 class ParalleMagic(Plugin):
54 class ParallelTricks(Plugin):
55 """A component to manage the %result, %px and %autopx magics."""
55 """A component to manage the %result, %px and %autopx magics."""
56
56
57 active_view = Instance('IPython.parallel.client.view.DirectView')
57 active_view = Instance('IPython.parallel.client.view.DirectView')
58 verbose = Bool(False, config=True)
58 verbose = Bool(False, config=True)
59 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
59 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC')
60
60
61 def __init__(self, shell=None, config=None):
61 def __init__(self, shell=None, config=None):
62 super(ParalleMagic, self).__init__(shell=shell, config=config)
62 super(ParallelTricks, self).__init__(shell=shell, config=config)
63 self._define_magics()
63 self.shell.register_magics(ParallelMagics)
64
65
66 @register_magics
67 class ParallelMagics(Magics):
68 """A set of magics useful when controlling a parallel IPython cluster.
69 """
70 def __init__(self, shell):
71 super(ParallelMagics, self).__init__(shell)
64 # A flag showing if autopx is activated or not
72 # A flag showing if autopx is activated or not
65 self.autopx = False
73 self.autopx = False
66
74
67 def _define_magics(self):
68 """Define the magic functions."""
69 self.shell.define_magic('result', self.magic_result)
70 self.shell.define_magic('px', self.magic_px)
71 self.shell.define_magic('autopx', self.magic_autopx)
72
73 @skip_doctest
75 @skip_doctest
74 def magic_result(self, ipself, parameter_s=''):
76 @line_magic
77 def result(self, parameter_s=''):
75 """Print the result of command i on all engines..
78 """Print the result of command i on all engines..
76
79
77 To use this a :class:`DirectView` instance must be created
80 To use this a :class:`DirectView` instance must be created
78 and then activated by calling its :meth:`activate` method.
81 and then activated by calling its :meth:`activate` method.
79
82
80 Then you can do the following::
83 Then you can do the following::
81
84
82 In [23]: %result
85 In [23]: %result
83 Out[23]:
86 Out[23]:
84 <Results List>
87 <Results List>
85 [0] In [6]: a = 10
88 [0] In [6]: a = 10
86 [1] In [6]: a = 10
89 [1] In [6]: a = 10
87
90
88 In [22]: %result 6
91 In [22]: %result 6
89 Out[22]:
92 Out[22]:
90 <Results List>
93 <Results List>
91 [0] In [6]: a = 10
94 [0] In [6]: a = 10
92 [1] In [6]: a = 10
95 [1] In [6]: a = 10
93 """
96 """
94 if self.active_view is None:
97 if self.active_view is None:
95 print NO_ACTIVE_VIEW
98 print NO_ACTIVE_VIEW
96 return
99 return
97
100
98 try:
101 try:
99 index = int(parameter_s)
102 index = int(parameter_s)
100 except:
103 except:
101 index = None
104 index = None
102 result = self.active_view.get_result(index)
105 result = self.active_view.get_result(index)
103 return result
106 return result
104
107
105 @skip_doctest
108 @skip_doctest
106 def magic_px(self, ipself, parameter_s=''):
109 @line_magic
110 def px(self, parameter_s=''):
107 """Executes the given python command in parallel.
111 """Executes the given python command in parallel.
108
112
109 To use this a :class:`DirectView` instance must be created
113 To use this a :class:`DirectView` instance must be created
110 and then activated by calling its :meth:`activate` method.
114 and then activated by calling its :meth:`activate` method.
111
115
112 Then you can do the following::
116 Then you can do the following::
113
117
114 In [24]: %px a = 5
118 In [24]: %px a = 5
115 Parallel execution on engine(s): all
119 Parallel execution on engine(s): all
116 Out[24]:
120 Out[24]:
117 <Results List>
121 <Results List>
118 [0] In [7]: a = 5
122 [0] In [7]: a = 5
119 [1] In [7]: a = 5
123 [1] In [7]: a = 5
120 """
124 """
121
125
122 if self.active_view is None:
126 if self.active_view is None:
123 print NO_ACTIVE_VIEW
127 print NO_ACTIVE_VIEW
124 return
128 return
125 print "Parallel execution on engine(s): %s" % self.active_view.targets
129 print "Parallel execution on engine(s): %s" % self.active_view.targets
126 result = self.active_view.execute(parameter_s, block=False)
130 result = self.active_view.execute(parameter_s, block=False)
127 if self.active_view.block:
131 if self.active_view.block:
128 result.get()
132 result.get()
129 self._maybe_display_output(result)
133 self._maybe_display_output(result)
130
134
131 @skip_doctest
135 @skip_doctest
132 def magic_autopx(self, ipself, parameter_s=''):
136 @line_magic
137 def autopx(self, parameter_s=''):
133 """Toggles auto parallel mode.
138 """Toggles auto parallel mode.
134
139
135 To use this a :class:`DirectView` instance must be created
140 To use this a :class:`DirectView` instance must be created
136 and then activated by calling its :meth:`activate` method. Once this
141 and then activated by calling its :meth:`activate` method. Once this
137 is called, all commands typed at the command line are send to
142 is called, all commands typed at the command line are send to
138 the engines to be executed in parallel. To control which engine
143 the engines to be executed in parallel. To control which engine
139 are used, set the ``targets`` attributed of the multiengine client
144 are used, set the ``targets`` attributed of the multiengine client
140 before entering ``%autopx`` mode.
145 before entering ``%autopx`` mode.
141
146
142 Then you can do the following::
147 Then you can do the following::
143
148
144 In [25]: %autopx
149 In [25]: %autopx
145 %autopx to enabled
150 %autopx to enabled
146
151
147 In [26]: a = 10
152 In [26]: a = 10
148 Parallel execution on engine(s): [0,1,2,3]
153 Parallel execution on engine(s): [0,1,2,3]
149 In [27]: print a
154 In [27]: print a
150 Parallel execution on engine(s): [0,1,2,3]
155 Parallel execution on engine(s): [0,1,2,3]
151 [stdout:0] 10
156 [stdout:0] 10
152 [stdout:1] 10
157 [stdout:1] 10
153 [stdout:2] 10
158 [stdout:2] 10
154 [stdout:3] 10
159 [stdout:3] 10
155
160
156
161
157 In [27]: %autopx
162 In [27]: %autopx
158 %autopx disabled
163 %autopx disabled
159 """
164 """
160 if self.autopx:
165 if self.autopx:
161 self._disable_autopx()
166 self._disable_autopx()
162 else:
167 else:
163 self._enable_autopx()
168 self._enable_autopx()
164
169
165 def _enable_autopx(self):
170 def _enable_autopx(self):
166 """Enable %autopx mode by saving the original run_cell and installing
171 """Enable %autopx mode by saving the original run_cell and installing
167 pxrun_cell.
172 pxrun_cell.
168 """
173 """
169 if self.active_view is None:
174 if self.active_view is None:
170 print NO_ACTIVE_VIEW
175 print NO_ACTIVE_VIEW
171 return
176 return
172
177
173 # override run_cell and run_code
178 # override run_cell and run_code
174 self._original_run_cell = self.shell.run_cell
179 self._original_run_cell = self.shell.run_cell
175 self.shell.run_cell = self.pxrun_cell
180 self.shell.run_cell = self.pxrun_cell
176 self._original_run_code = self.shell.run_code
181 self._original_run_code = self.shell.run_code
177 self.shell.run_code = self.pxrun_code
182 self.shell.run_code = self.pxrun_code
178
183
179 self.autopx = True
184 self.autopx = True
180 print "%autopx enabled"
185 print "%autopx enabled"
181
186
182 def _disable_autopx(self):
187 def _disable_autopx(self):
183 """Disable %autopx by restoring the original InteractiveShell.run_cell.
188 """Disable %autopx by restoring the original InteractiveShell.run_cell.
184 """
189 """
185 if self.autopx:
190 if self.autopx:
186 self.shell.run_cell = self._original_run_cell
191 self.shell.run_cell = self._original_run_cell
187 self.shell.run_code = self._original_run_code
192 self.shell.run_code = self._original_run_code
188 self.autopx = False
193 self.autopx = False
189 print "%autopx disabled"
194 print "%autopx disabled"
190
195
191 def _maybe_display_output(self, result):
196 def _maybe_display_output(self, result):
192 """Maybe display the output of a parallel result.
197 """Maybe display the output of a parallel result.
193
198
194 If self.active_view.block is True, wait for the result
199 If self.active_view.block is True, wait for the result
195 and display the result. Otherwise, this is a noop.
200 and display the result. Otherwise, this is a noop.
196 """
201 """
197 if isinstance(result.stdout, basestring):
202 if isinstance(result.stdout, basestring):
198 # single result
203 # single result
199 stdouts = [result.stdout.rstrip()]
204 stdouts = [result.stdout.rstrip()]
200 else:
205 else:
201 stdouts = [s.rstrip() for s in result.stdout]
206 stdouts = [s.rstrip() for s in result.stdout]
202
207
203 targets = self.active_view.targets
208 targets = self.active_view.targets
204 if isinstance(targets, int):
209 if isinstance(targets, int):
205 targets = [targets]
210 targets = [targets]
206 elif targets == 'all':
211 elif targets == 'all':
207 targets = self.active_view.client.ids
212 targets = self.active_view.client.ids
208
213
209 if any(stdouts):
214 if any(stdouts):
210 for eid,stdout in zip(targets, stdouts):
215 for eid,stdout in zip(targets, stdouts):
211 print '[stdout:%i]'%eid, stdout
216 print '[stdout:%i]'%eid, stdout
212
217
213
218
214 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
219 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
215 """drop-in replacement for InteractiveShell.run_cell.
220 """drop-in replacement for InteractiveShell.run_cell.
216
221
217 This executes code remotely, instead of in the local namespace.
222 This executes code remotely, instead of in the local namespace.
218
223
219 See InteractiveShell.run_cell for details.
224 See InteractiveShell.run_cell for details.
220 """
225 """
221
226
222 if (not raw_cell) or raw_cell.isspace():
227 if (not raw_cell) or raw_cell.isspace():
223 return
228 return
224
229
225 ipself = self.shell
230 ipself = self.shell
226
231
227 with ipself.builtin_trap:
232 with ipself.builtin_trap:
228 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
233 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
229
234
230 # Store raw and processed history
235 # Store raw and processed history
231 if store_history:
236 if store_history:
232 ipself.history_manager.store_inputs(ipself.execution_count,
237 ipself.history_manager.store_inputs(ipself.execution_count,
233 cell, raw_cell)
238 cell, raw_cell)
234
239
235 # ipself.logger.log(cell, raw_cell)
240 # ipself.logger.log(cell, raw_cell)
236
241
237 cell_name = ipself.compile.cache(cell, ipself.execution_count)
242 cell_name = ipself.compile.cache(cell, ipself.execution_count)
238
243
239 try:
244 try:
240 code_ast = ast.parse(cell, filename=cell_name)
245 code_ast = ast.parse(cell, filename=cell_name)
241 except (OverflowError, SyntaxError, ValueError, TypeError, MemoryError):
246 except (OverflowError, SyntaxError, ValueError, TypeError, MemoryError):
242 # Case 1
247 # Case 1
243 ipself.showsyntaxerror()
248 ipself.showsyntaxerror()
244 ipself.execution_count += 1
249 ipself.execution_count += 1
245 return None
250 return None
246 except NameError:
251 except NameError:
247 # ignore name errors, because we don't know the remote keys
252 # ignore name errors, because we don't know the remote keys
248 pass
253 pass
249
254
250 if store_history:
255 if store_history:
251 # Write output to the database. Does nothing unless
256 # Write output to the database. Does nothing unless
252 # history output logging is enabled.
257 # history output logging is enabled.
253 ipself.history_manager.store_output(ipself.execution_count)
258 ipself.history_manager.store_output(ipself.execution_count)
254 # Each cell is a *single* input, regardless of how many lines it has
259 # Each cell is a *single* input, regardless of how many lines it has
255 ipself.execution_count += 1
260 ipself.execution_count += 1
256 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
261 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
257 self._disable_autopx()
262 self._disable_autopx()
258 return False
263 return False
259 else:
264 else:
260 try:
265 try:
261 result = self.active_view.execute(cell, silent=False, block=False)
266 result = self.active_view.execute(cell, silent=False, block=False)
262 except:
267 except:
263 ipself.showtraceback()
268 ipself.showtraceback()
264 return True
269 return True
265 else:
270 else:
266 if self.active_view.block:
271 if self.active_view.block:
267 try:
272 try:
268 result.get()
273 result.get()
269 except:
274 except:
270 self.shell.showtraceback()
275 self.shell.showtraceback()
271 return True
276 return True
272 else:
277 else:
273 self._maybe_display_output(result)
278 self._maybe_display_output(result)
274 return False
279 return False
275
280
276 def pxrun_code(self, code_obj):
281 def pxrun_code(self, code_obj):
277 """drop-in replacement for InteractiveShell.run_code.
282 """drop-in replacement for InteractiveShell.run_code.
278
283
279 This executes code remotely, instead of in the local namespace.
284 This executes code remotely, instead of in the local namespace.
280
285
281 See InteractiveShell.run_code for details.
286 See InteractiveShell.run_code for details.
282 """
287 """
283 ipself = self.shell
288 ipself = self.shell
284 # check code object for the autopx magic
289 # check code object for the autopx magic
285 if 'get_ipython' in code_obj.co_names and 'magic' in code_obj.co_names and \
290 if 'get_ipython' in code_obj.co_names and 'magic' in code_obj.co_names \
286 any( [ isinstance(c, basestring) and 'autopx' in c for c in code_obj.co_consts ]):
291 and any( [ isinstance(c, basestring) and 'autopx' in c
292 for c in code_obj.co_consts ]):
287 self._disable_autopx()
293 self._disable_autopx()
288 return False
294 return False
289 else:
295 else:
290 try:
296 try:
291 result = self.active_view.execute(code_obj, block=False)
297 result = self.active_view.execute(code_obj, block=False)
292 except:
298 except:
293 ipself.showtraceback()
299 ipself.showtraceback()
294 return True
300 return True
295 else:
301 else:
296 if self.active_view.block:
302 if self.active_view.block:
297 try:
303 try:
298 result.get()
304 result.get()
299 except:
305 except:
300 self.shell.showtraceback()
306 self.shell.showtraceback()
301 return True
307 return True
302 else:
308 else:
303 self._maybe_display_output(result)
309 self._maybe_display_output(result)
304 return False
310 return False
305
311
306
312
307 __doc__ = __doc__.replace('@AUTOPX_DOC@',
313 __doc__ = __doc__.replace('@AUTOPX_DOC@',
308 " " + ParalleMagic.magic_autopx.__doc__)
314 " " + ParallelTricks.magic_autopx.__doc__)
309 __doc__ = __doc__.replace('@PX_DOC@',
315 __doc__ = __doc__.replace('@PX_DOC@',
310 " " + ParalleMagic.magic_px.__doc__)
316 " " + ParallelTricks.magic_px.__doc__)
311 __doc__ = __doc__.replace('@RESULT_DOC@',
317 __doc__ = __doc__.replace('@RESULT_DOC@',
312 " " + ParalleMagic.magic_result.__doc__)
318 " " + ParallelTricks.magic_result.__doc__)
313
319
314
320
315 _loaded = False
321 _loaded = False
316
322
317
323
318 def load_ipython_extension(ip):
324 def load_ipython_extension(ip):
319 """Load the extension in IPython."""
325 """Load the extension in IPython."""
320 global _loaded
326 global _loaded
321 if not _loaded:
327 if not _loaded:
322 plugin = ParalleMagic(shell=ip, config=ip.config)
328 plugin = ParallelTricks(shell=ip, config=ip.config)
323 ip.plugin_manager.register_plugin('parallelmagic', plugin)
329 ip.plugin_manager.register_plugin('parallelmagic', plugin)
324 _loaded = True
330 _loaded = True
325
General Comments 0
You need to be logged in to leave comments. Login now