##// END OF EJS Templates
split `%px` and `%%parallel` line/cell magics
MinRK -
Show More
@@ -1,316 +1,339 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 The IPython Development Team
27 # Copyright (C) 2008 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.error import UsageError
40 from IPython.core.error import UsageError
41 from IPython.core.magic import Magics, magics_class, line_magic, line_cell_magic
41 from IPython.core.magic import Magics, magics_class, line_magic, cell_magic
42 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.testing.skipdoctest import skip_doctest
43
43
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 # Definitions of magic functions for use with IPython
45 # Definitions of magic functions for use with IPython
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47
47
48
48
49 NO_ACTIVE_VIEW = "Use activate() on a DirectView object to use it with magics."
49 NO_ACTIVE_VIEW = "Use activate() on a DirectView object to use it with magics."
50
50
51
51
52 @magics_class
52 @magics_class
53 class ParallelMagics(Magics):
53 class ParallelMagics(Magics):
54 """A set of magics useful when controlling a parallel IPython cluster.
54 """A set of magics useful when controlling a parallel IPython cluster.
55 """
55 """
56
56
57 # A flag showing if autopx is activated or not
57 # A flag showing if autopx is activated or not
58 _autopx = False
58 _autopx = False
59 # the current view used by the magics:
59 # the current view used by the magics:
60 active_view = None
60 active_view = None
61
61
62 @skip_doctest
62 @skip_doctest
63 @line_magic
63 @line_magic
64 def result(self, parameter_s=''):
64 def result(self, parameter_s=''):
65 """Print the result of command i on all engines.
65 """Print the result of command i on all engines.
66
66
67 To use this a :class:`DirectView` instance must be created
67 To use this a :class:`DirectView` instance must be created
68 and then activated by calling its :meth:`activate` method.
68 and then activated by calling its :meth:`activate` method.
69
69
70 This lets you recall the results of %px computations after
70 This lets you recall the results of %px computations after
71 asynchronous submission (view.block=False).
71 asynchronous submission (view.block=False).
72
72
73 Then you can do the following::
73 Then you can do the following::
74
74
75 In [23]: %px os.getpid()
75 In [23]: %px os.getpid()
76 Async parallel execution on engine(s): all
76 Async parallel execution on engine(s): all
77
77
78 In [24]: %result
78 In [24]: %result
79 [ 8] Out[10]: 60920
79 [ 8] Out[10]: 60920
80 [ 9] Out[10]: 60921
80 [ 9] Out[10]: 60921
81 [10] Out[10]: 60922
81 [10] Out[10]: 60922
82 [11] Out[10]: 60923
82 [11] Out[10]: 60923
83 """
83 """
84
84
85 if self.active_view is None:
85 if self.active_view is None:
86 raise UsageError(NO_ACTIVE_VIEW)
86 raise UsageError(NO_ACTIVE_VIEW)
87
87
88 stride = len(self.active_view)
88 stride = len(self.active_view)
89 try:
89 try:
90 index = int(parameter_s)
90 index = int(parameter_s)
91 except:
91 except:
92 index = -1
92 index = -1
93 msg_ids = self.active_view.history[stride * index:(stride * (index + 1)) or None]
93 msg_ids = self.active_view.history[stride * index:(stride * (index + 1)) or None]
94
94
95 result = self.active_view.get_result(msg_ids)
95 result = self.active_view.get_result(msg_ids)
96
96
97 result.get()
97 result.get()
98 result.display_outputs()
98 result.display_outputs()
99
99
100 @skip_doctest
100 @skip_doctest
101 @line_cell_magic
101 @line_magic
102 def px(self, line='', cell=None):
102 def px(self, parameter_s=''):
103 """Executes the given python command in parallel.
104
105 To use this a :class:`DirectView` instance must be created
106 and then activated by calling its :meth:`activate` method.
107
108 Then you can do the following::
109
110 In [24]: %px a = os.getpid()
111 Parallel execution on engine(s): all
112
113 In [25]: %px print a
114 [stdout:0] 1234
115 [stdout:1] 1235
116 [stdout:2] 1236
117 [stdout:3] 1237
118 """
119 return self.parallel_execute(parameter_s)
120
121 def parallel_execute(self, cell, block=None, groupby='type'):
122 """implementation used by %px and %%parallel"""
123
124 if self.active_view is None:
125 raise UsageError(NO_ACTIVE_VIEW)
126
127 # defaults:
128 block = self.active_view.block if block is None else block
129
130 base = "Parallel" if block else "Async parallel"
131 print base + " execution on engine(s): %s" % self.active_view.targets
132
133 result = self.active_view.execute(cell, silent=False, block=False)
134 if block:
135 result.get()
136 result.display_outputs(groupby)
137
138 @skip_doctest
139 @cell_magic
140 def parallel(self, line='', cell=None):
103 """Executes the given python command in parallel.
141 """Executes the given python command in parallel.
104
142
105 Cell magic usage:
143 Cell magic usage:
106
144
107 %%px [-o] [-e] [--group-options=type|engine|order] [--[no]block]
145 %%px [-o] [-e] [--group-options=type|engine|order] [--[no]block]
108
146
109 Options (%%px cell magic only):
147 Options (%%px cell magic only):
110
148
111 -o: collate outputs in oder (same as group-outputs=order)
149 -o: collate outputs in oder (same as group-outputs=order)
112
150
113 -e: group outputs by engine (same as group-outputs=engine)
151 -e: group outputs by engine (same as group-outputs=engine)
114
152
115 --group-outputs=type [default behavior]:
153 --group-outputs=type [default behavior]:
116 each output type (stdout, stderr, displaypub) for all engines
154 each output type (stdout, stderr, displaypub) for all engines
117 displayed together.
155 displayed together.
118
156
119 --group-outputs=order:
157 --group-outputs=order:
120 The same as 'type', but individual displaypub outputs (e.g. plots)
158 The same as 'type', but individual displaypub outputs (e.g. plots)
121 will be interleaved, so it will display all of the first plots,
159 will be interleaved, so it will display all of the first plots,
122 then all of the second plots, etc.
160 then all of the second plots, etc.
123
161
124 --group-outputs=engine:
162 --group-outputs=engine:
125 All of an engine's output is displayed before moving on to the next.
163 All of an engine's output is displayed before moving on to the next.
126
164
127 --[no]block:
165 --[no]block:
128 Whether or not to block for the execution to complete
166 Whether or not to block for the execution to complete
129 (and display the results). If unspecified, the active view's
167 (and display the results). If unspecified, the active view's
130
168
131
169
132 To use this a :class:`DirectView` instance must be created
170 To use this a :class:`DirectView` instance must be created
133 and then activated by calling its :meth:`activate` method.
171 and then activated by calling its :meth:`activate` method.
134
172
135 Then you can do the following::
173 Then you can do the following::
136
174
137 In [24]: %px a = os.getpid()
175 In [24]: %%parallel --noblock a = os.getpid()
138 Parallel execution on engine(s): all
176 Async parallel execution on engine(s): all
139
177
140 In [25]: %px print a
178 In [25]: %px print a
141 [stdout:0] 1234
179 [stdout:0] 1234
142 [stdout:1] 1235
180 [stdout:1] 1235
143 [stdout:2] 1236
181 [stdout:2] 1236
144 [stdout:3] 1237
182 [stdout:3] 1237
145 """
183 """
146
147 if self.active_view is None:
148 raise UsageError(NO_ACTIVE_VIEW)
149
184
150 # defaults:
185 block = None
151 block = self.active_view.block
152 groupby = 'type'
186 groupby = 'type'
153
187 # as a cell magic, we accept args
154 if cell is None:
188 opts, _ = self.parse_options(line, 'oe', 'group-outputs=', 'block', 'noblock')
155 # line magic
189
156 cell = line
190 if 'group-outputs' in opts:
157 else:
191 groupby = opts['group-outputs']
158 # as a cell magic, we accept args
192 elif 'o' in opts:
159 opts, _ = self.parse_options(line, 'oe', 'group-outputs=', 'block', 'noblock')
193 groupby = 'order'
160
194 elif 'e' in opts:
161 if 'group-outputs' in opts:
195 groupby = 'engine'
162 groupby = opts['group-outputs']
163 elif 'o' in opts:
164 groupby = 'order'
165 elif 'e' in opts:
166 groupby = 'engine'
167
196
168 if 'block' in opts:
197 if 'block' in opts:
169 block = True
198 block = True
170 elif 'noblock' in opts:
199 elif 'noblock' in opts:
171 block = False
200 block = False
172
201
173 base = "Parallel" if self.active_view.block else "Async parallel"
202 return self.parallel_execute(cell, block=block, groupby=groupby)
174 print base + " execution on engine(s): %s" % self.active_view.targets
175
176 result = self.active_view.execute(cell, silent=False, block=False)
177 if block:
178 result.get()
179 result.display_outputs(groupby)
180
203
181 @skip_doctest
204 @skip_doctest
182 @line_magic
205 @line_magic
183 def autopx(self, parameter_s=''):
206 def autopx(self, parameter_s=''):
184 """Toggles auto parallel mode.
207 """Toggles auto parallel mode.
185
208
186 To use this a :class:`DirectView` instance must be created
209 To use this a :class:`DirectView` instance must be created
187 and then activated by calling its :meth:`activate` method. Once this
210 and then activated by calling its :meth:`activate` method. Once this
188 is called, all commands typed at the command line are send to
211 is called, all commands typed at the command line are send to
189 the engines to be executed in parallel. To control which engine
212 the engines to be executed in parallel. To control which engine
190 are used, set the ``targets`` attributed of the multiengine client
213 are used, set the ``targets`` attributed of the multiengine client
191 before entering ``%autopx`` mode.
214 before entering ``%autopx`` mode.
192
215
193 Then you can do the following::
216 Then you can do the following::
194
217
195 In [25]: %autopx
218 In [25]: %autopx
196 %autopx to enabled
219 %autopx to enabled
197
220
198 In [26]: a = 10
221 In [26]: a = 10
199 Parallel execution on engine(s): [0,1,2,3]
222 Parallel execution on engine(s): [0,1,2,3]
200 In [27]: print a
223 In [27]: print a
201 Parallel execution on engine(s): [0,1,2,3]
224 Parallel execution on engine(s): [0,1,2,3]
202 [stdout:0] 10
225 [stdout:0] 10
203 [stdout:1] 10
226 [stdout:1] 10
204 [stdout:2] 10
227 [stdout:2] 10
205 [stdout:3] 10
228 [stdout:3] 10
206
229
207
230
208 In [27]: %autopx
231 In [27]: %autopx
209 %autopx disabled
232 %autopx disabled
210 """
233 """
211 if self._autopx:
234 if self._autopx:
212 self._disable_autopx()
235 self._disable_autopx()
213 else:
236 else:
214 self._enable_autopx()
237 self._enable_autopx()
215
238
216 def _enable_autopx(self):
239 def _enable_autopx(self):
217 """Enable %autopx mode by saving the original run_cell and installing
240 """Enable %autopx mode by saving the original run_cell and installing
218 pxrun_cell.
241 pxrun_cell.
219 """
242 """
220 if self.active_view is None:
243 if self.active_view is None:
221 raise UsageError(NO_ACTIVE_VIEW)
244 raise UsageError(NO_ACTIVE_VIEW)
222
245
223 # override run_cell
246 # override run_cell
224 self._original_run_cell = self.shell.run_cell
247 self._original_run_cell = self.shell.run_cell
225 self.shell.run_cell = self.pxrun_cell
248 self.shell.run_cell = self.pxrun_cell
226
249
227 self._autopx = True
250 self._autopx = True
228 print "%autopx enabled"
251 print "%autopx enabled"
229
252
230 def _disable_autopx(self):
253 def _disable_autopx(self):
231 """Disable %autopx by restoring the original InteractiveShell.run_cell.
254 """Disable %autopx by restoring the original InteractiveShell.run_cell.
232 """
255 """
233 if self._autopx:
256 if self._autopx:
234 self.shell.run_cell = self._original_run_cell
257 self.shell.run_cell = self._original_run_cell
235 self._autopx = False
258 self._autopx = False
236 print "%autopx disabled"
259 print "%autopx disabled"
237
260
238 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
261 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
239 """drop-in replacement for InteractiveShell.run_cell.
262 """drop-in replacement for InteractiveShell.run_cell.
240
263
241 This executes code remotely, instead of in the local namespace.
264 This executes code remotely, instead of in the local namespace.
242
265
243 See InteractiveShell.run_cell for details.
266 See InteractiveShell.run_cell for details.
244 """
267 """
245
268
246 if (not raw_cell) or raw_cell.isspace():
269 if (not raw_cell) or raw_cell.isspace():
247 return
270 return
248
271
249 ipself = self.shell
272 ipself = self.shell
250
273
251 with ipself.builtin_trap:
274 with ipself.builtin_trap:
252 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
275 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
253
276
254 # Store raw and processed history
277 # Store raw and processed history
255 if store_history:
278 if store_history:
256 ipself.history_manager.store_inputs(ipself.execution_count,
279 ipself.history_manager.store_inputs(ipself.execution_count,
257 cell, raw_cell)
280 cell, raw_cell)
258
281
259 # ipself.logger.log(cell, raw_cell)
282 # ipself.logger.log(cell, raw_cell)
260
283
261 cell_name = ipself.compile.cache(cell, ipself.execution_count)
284 cell_name = ipself.compile.cache(cell, ipself.execution_count)
262
285
263 try:
286 try:
264 ast.parse(cell, filename=cell_name)
287 ast.parse(cell, filename=cell_name)
265 except (OverflowError, SyntaxError, ValueError, TypeError,
288 except (OverflowError, SyntaxError, ValueError, TypeError,
266 MemoryError):
289 MemoryError):
267 # Case 1
290 # Case 1
268 ipself.showsyntaxerror()
291 ipself.showsyntaxerror()
269 ipself.execution_count += 1
292 ipself.execution_count += 1
270 return None
293 return None
271 except NameError:
294 except NameError:
272 # ignore name errors, because we don't know the remote keys
295 # ignore name errors, because we don't know the remote keys
273 pass
296 pass
274
297
275 if store_history:
298 if store_history:
276 # Write output to the database. Does nothing unless
299 # Write output to the database. Does nothing unless
277 # history output logging is enabled.
300 # history output logging is enabled.
278 ipself.history_manager.store_output(ipself.execution_count)
301 ipself.history_manager.store_output(ipself.execution_count)
279 # Each cell is a *single* input, regardless of how many lines it has
302 # Each cell is a *single* input, regardless of how many lines it has
280 ipself.execution_count += 1
303 ipself.execution_count += 1
281 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
304 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
282 self._disable_autopx()
305 self._disable_autopx()
283 return False
306 return False
284 else:
307 else:
285 try:
308 try:
286 result = self.active_view.execute(cell, silent=False, block=False)
309 result = self.active_view.execute(cell, silent=False, block=False)
287 except:
310 except:
288 ipself.showtraceback()
311 ipself.showtraceback()
289 return True
312 return True
290 else:
313 else:
291 if self.active_view.block:
314 if self.active_view.block:
292 try:
315 try:
293 result.get()
316 result.get()
294 except:
317 except:
295 self.shell.showtraceback()
318 self.shell.showtraceback()
296 return True
319 return True
297 else:
320 else:
298 result.display_outputs()
321 result.display_outputs()
299 return False
322 return False
300
323
301
324
302 __doc__ = __doc__.format(
325 __doc__ = __doc__.format(
303 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
326 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
304 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
327 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
305 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__
328 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__
306 )
329 )
307
330
308 _loaded = False
331 _loaded = False
309
332
310
333
311 def load_ipython_extension(ip):
334 def load_ipython_extension(ip):
312 """Load the extension in IPython."""
335 """Load the extension in IPython."""
313 global _loaded
336 global _loaded
314 if not _loaded:
337 if not _loaded:
315 ip.register_magics(ParallelMagics)
338 ip.register_magics(ParallelMagics)
316 _loaded = True
339 _loaded = True
General Comments 0
You need to be logged in to leave comments. Login now