##// END OF EJS Templates
add `%%px --local` for local execution...
MinRK -
Show More
@@ -1,424 +1,430
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 ``%pxresult``
20 ``%pxresult``
21
21
22 {RESULT_DOC}
22 {RESULT_DOC}
23
23
24 ``%pxconfig``
24 ``%pxconfig``
25
25
26 {CONFIG_DOC}
26 {CONFIG_DOC}
27
27
28 """
28 """
29
29
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31 # Copyright (C) 2008 The IPython Development Team
31 # Copyright (C) 2008 The IPython Development Team
32 #
32 #
33 # Distributed under the terms of the BSD License. The full license is in
33 # Distributed under the terms of the BSD License. The full license is in
34 # the file COPYING, distributed as part of this software.
34 # the file COPYING, distributed as part of this software.
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38 # Imports
38 # Imports
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40
40
41 import ast
41 import ast
42 import re
42 import re
43
43
44 from IPython.core.error import UsageError
44 from IPython.core.error import UsageError
45 from IPython.core.magic import Magics
45 from IPython.core.magic import Magics
46 from IPython.core import magic_arguments
46 from IPython.core import magic_arguments
47 from IPython.testing.skipdoctest import skip_doctest
47 from IPython.testing.skipdoctest import skip_doctest
48
48
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50 # Definitions of magic functions for use with IPython
50 # Definitions of magic functions for use with IPython
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52
52
53
53
54 NO_LAST_RESULT = "%pxresult recalls last %px result, which has not yet been used."
54 NO_LAST_RESULT = "%pxresult recalls last %px result, which has not yet been used."
55
55
56 def exec_args(f):
56 def exec_args(f):
57 """decorator for adding block/targets args for execution
57 """decorator for adding block/targets args for execution
58
58
59 applied to %pxconfig and %%px
59 applied to %pxconfig and %%px
60 """
60 """
61 args = [
61 args = [
62 magic_arguments.argument('-b', '--block', action="store_const",
62 magic_arguments.argument('-b', '--block', action="store_const",
63 const=True, dest='block',
63 const=True, dest='block',
64 help="use blocking (sync) execution",
64 help="use blocking (sync) execution",
65 ),
65 ),
66 magic_arguments.argument('-a', '--noblock', action="store_const",
66 magic_arguments.argument('-a', '--noblock', action="store_const",
67 const=False, dest='block',
67 const=False, dest='block',
68 help="use non-blocking (async) execution",
68 help="use non-blocking (async) execution",
69 ),
69 ),
70 magic_arguments.argument('-t', '--targets', type=str,
70 magic_arguments.argument('-t', '--targets', type=str,
71 help="specify the targets on which to execute",
71 help="specify the targets on which to execute",
72 ),
72 ),
73 magic_arguments.argument('--local', action="store_const",
74 const=True, dest="local",
75 help="also execute the cell in the local namespace",
76 ),
73 magic_arguments.argument('--verbose', action="store_const",
77 magic_arguments.argument('--verbose', action="store_const",
74 const=True, dest="set_verbose",
78 const=True, dest="set_verbose",
75 help="print a message at each execution",
79 help="print a message at each execution",
76 ),
80 ),
77 magic_arguments.argument('--no-verbose', action="store_const",
81 magic_arguments.argument('--no-verbose', action="store_const",
78 const=False, dest="set_verbose",
82 const=False, dest="set_verbose",
79 help="don't print any messages",
83 help="don't print any messages",
80 ),
84 ),
81 ]
85 ]
82 for a in args:
86 for a in args:
83 f = a(f)
87 f = a(f)
84 return f
88 return f
85
89
86 def output_args(f):
90 def output_args(f):
87 """decorator for output-formatting args
91 """decorator for output-formatting args
88
92
89 applied to %pxresult and %%px
93 applied to %pxresult and %%px
90 """
94 """
91 args = [
95 args = [
92 magic_arguments.argument('-r', action="store_const", dest='groupby',
96 magic_arguments.argument('-r', action="store_const", dest='groupby',
93 const='order',
97 const='order',
94 help="collate outputs in order (same as group-outputs=order)"
98 help="collate outputs in order (same as group-outputs=order)"
95 ),
99 ),
96 magic_arguments.argument('-e', action="store_const", dest='groupby',
100 magic_arguments.argument('-e', action="store_const", dest='groupby',
97 const='engine',
101 const='engine',
98 help="group outputs by engine (same as group-outputs=engine)"
102 help="group outputs by engine (same as group-outputs=engine)"
99 ),
103 ),
100 magic_arguments.argument('--group-outputs', dest='groupby', type=str,
104 magic_arguments.argument('--group-outputs', dest='groupby', type=str,
101 choices=['engine', 'order', 'type'], default='type',
105 choices=['engine', 'order', 'type'], default='type',
102 help="""Group the outputs in a particular way.
106 help="""Group the outputs in a particular way.
103
107
104 Choices are:
108 Choices are:
105
109
106 type: group outputs of all engines by type (stdout, stderr, displaypub, etc.).
110 type: group outputs of all engines by type (stdout, stderr, displaypub, etc.).
107
111
108 engine: display all output for each engine together.
112 engine: display all output for each engine together.
109
113
110 order: like type, but individual displaypub output from each engine is collated.
114 order: like type, but individual displaypub output from each engine is collated.
111 For example, if multiple plots are generated by each engine, the first
115 For example, if multiple plots are generated by each engine, the first
112 figure of each engine will be displayed, then the second of each, etc.
116 figure of each engine will be displayed, then the second of each, etc.
113 """
117 """
114 ),
118 ),
115 magic_arguments.argument('-o', '--out', dest='save_name', type=str,
119 magic_arguments.argument('-o', '--out', dest='save_name', type=str,
116 help="""store the AsyncResult object for this computation
120 help="""store the AsyncResult object for this computation
117 in the global namespace under this name.
121 in the global namespace under this name.
118 """
122 """
119 ),
123 ),
120 ]
124 ]
121 for a in args:
125 for a in args:
122 f = a(f)
126 f = a(f)
123 return f
127 return f
124
128
125 class ParallelMagics(Magics):
129 class ParallelMagics(Magics):
126 """A set of magics useful when controlling a parallel IPython cluster.
130 """A set of magics useful when controlling a parallel IPython cluster.
127 """
131 """
128
132
129 # magic-related
133 # magic-related
130 magics = None
134 magics = None
131 registered = True
135 registered = True
132
136
133 # suffix for magics
137 # suffix for magics
134 suffix = ''
138 suffix = ''
135 # A flag showing if autopx is activated or not
139 # A flag showing if autopx is activated or not
136 _autopx = False
140 _autopx = False
137 # the current view used by the magics:
141 # the current view used by the magics:
138 view = None
142 view = None
139 # last result cache for %pxresult
143 # last result cache for %pxresult
140 last_result = None
144 last_result = None
141 # verbose flag
145 # verbose flag
142 verbose = False
146 verbose = False
143
147
144 def __init__(self, shell, view, suffix=''):
148 def __init__(self, shell, view, suffix=''):
145 self.view = view
149 self.view = view
146 self.suffix = suffix
150 self.suffix = suffix
147
151
148 # register magics
152 # register magics
149 self.magics = dict(cell={},line={})
153 self.magics = dict(cell={},line={})
150 line_magics = self.magics['line']
154 line_magics = self.magics['line']
151
155
152 px = 'px' + suffix
156 px = 'px' + suffix
153 if not suffix:
157 if not suffix:
154 # keep %result for legacy compatibility
158 # keep %result for legacy compatibility
155 line_magics['result'] = self.result
159 line_magics['result'] = self.result
156
160
157 line_magics['pxresult' + suffix] = self.result
161 line_magics['pxresult' + suffix] = self.result
158 line_magics[px] = self.px
162 line_magics[px] = self.px
159 line_magics['pxconfig' + suffix] = self.pxconfig
163 line_magics['pxconfig' + suffix] = self.pxconfig
160 line_magics['auto' + px] = self.autopx
164 line_magics['auto' + px] = self.autopx
161
165
162 self.magics['cell'][px] = self.cell_px
166 self.magics['cell'][px] = self.cell_px
163
167
164 super(ParallelMagics, self).__init__(shell=shell)
168 super(ParallelMagics, self).__init__(shell=shell)
165
169
166 def _eval_target_str(self, ts):
170 def _eval_target_str(self, ts):
167 if ':' in ts:
171 if ':' in ts:
168 targets = eval("self.view.client.ids[%s]" % ts)
172 targets = eval("self.view.client.ids[%s]" % ts)
169 elif 'all' in ts:
173 elif 'all' in ts:
170 targets = 'all'
174 targets = 'all'
171 else:
175 else:
172 targets = eval(ts)
176 targets = eval(ts)
173 return targets
177 return targets
174
178
175 @magic_arguments.magic_arguments()
179 @magic_arguments.magic_arguments()
176 @exec_args
180 @exec_args
177 def pxconfig(self, line):
181 def pxconfig(self, line):
178 """configure default targets/blocking for %px magics"""
182 """configure default targets/blocking for %px magics"""
179 args = magic_arguments.parse_argstring(self.pxconfig, line)
183 args = magic_arguments.parse_argstring(self.pxconfig, line)
180 if args.targets:
184 if args.targets:
181 self.view.targets = self._eval_target_str(args.targets)
185 self.view.targets = self._eval_target_str(args.targets)
182 if args.block is not None:
186 if args.block is not None:
183 self.view.block = args.block
187 self.view.block = args.block
184 if args.set_verbose is not None:
188 if args.set_verbose is not None:
185 self.verbose = args.set_verbose
189 self.verbose = args.set_verbose
186
190
187 @magic_arguments.magic_arguments()
191 @magic_arguments.magic_arguments()
188 @output_args
192 @output_args
189 @skip_doctest
193 @skip_doctest
190 def result(self, line=''):
194 def result(self, line=''):
191 """Print the result of the last asynchronous %px command.
195 """Print the result of the last asynchronous %px command.
192
196
193 This lets you recall the results of %px computations after
197 This lets you recall the results of %px computations after
194 asynchronous submission (block=False).
198 asynchronous submission (block=False).
195
199
196 Examples
200 Examples
197 --------
201 --------
198 ::
202 ::
199
203
200 In [23]: %px os.getpid()
204 In [23]: %px os.getpid()
201 Async parallel execution on engine(s): all
205 Async parallel execution on engine(s): all
202
206
203 In [24]: %pxresult
207 In [24]: %pxresult
204 Out[8:10]: 60920
208 Out[8:10]: 60920
205 Out[9:10]: 60921
209 Out[9:10]: 60921
206 Out[10:10]: 60922
210 Out[10:10]: 60922
207 Out[11:10]: 60923
211 Out[11:10]: 60923
208 """
212 """
209 args = magic_arguments.parse_argstring(self.result, line)
213 args = magic_arguments.parse_argstring(self.result, line)
210
214
211 if self.last_result is None:
215 if self.last_result is None:
212 raise UsageError(NO_LAST_RESULT)
216 raise UsageError(NO_LAST_RESULT)
213
217
214 self.last_result.get()
218 self.last_result.get()
215 self.last_result.display_outputs(groupby=args.groupby)
219 self.last_result.display_outputs(groupby=args.groupby)
216
220
217 @skip_doctest
221 @skip_doctest
218 def px(self, line=''):
222 def px(self, line=''):
219 """Executes the given python command in parallel.
223 """Executes the given python command in parallel.
220
224
221 Examples
225 Examples
222 --------
226 --------
223 ::
227 ::
224
228
225 In [24]: %px a = os.getpid()
229 In [24]: %px a = os.getpid()
226 Parallel execution on engine(s): all
230 Parallel execution on engine(s): all
227
231
228 In [25]: %px print a
232 In [25]: %px print a
229 [stdout:0] 1234
233 [stdout:0] 1234
230 [stdout:1] 1235
234 [stdout:1] 1235
231 [stdout:2] 1236
235 [stdout:2] 1236
232 [stdout:3] 1237
236 [stdout:3] 1237
233 """
237 """
234 return self.parallel_execute(line)
238 return self.parallel_execute(line)
235
239
236 def parallel_execute(self, cell, block=None, groupby='type', save_name=None):
240 def parallel_execute(self, cell, block=None, groupby='type', save_name=None):
237 """implementation used by %px and %%parallel"""
241 """implementation used by %px and %%parallel"""
238
242
239 # defaults:
243 # defaults:
240 block = self.view.block if block is None else block
244 block = self.view.block if block is None else block
241
245
242 base = "Parallel" if block else "Async parallel"
246 base = "Parallel" if block else "Async parallel"
243
247
244 targets = self.view.targets
248 targets = self.view.targets
245 if isinstance(targets, list) and len(targets) > 10:
249 if isinstance(targets, list) and len(targets) > 10:
246 str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:]
250 str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:]
247 else:
251 else:
248 str_targets = str(targets)
252 str_targets = str(targets)
249 if self.verbose:
253 if self.verbose:
250 print base + " execution on engine(s): %s" % str_targets
254 print base + " execution on engine(s): %s" % str_targets
251
255
252 result = self.view.execute(cell, silent=False, block=False)
256 result = self.view.execute(cell, silent=False, block=False)
253 self.last_result = result
257 self.last_result = result
254
258
255 if save_name:
259 if save_name:
256 self.shell.user_ns[save_name] = result
260 self.shell.user_ns[save_name] = result
257
261
258 if block:
262 if block:
259 result.get()
263 result.get()
260 result.display_outputs(groupby)
264 result.display_outputs(groupby)
261 else:
265 else:
262 # return AsyncResult only on non-blocking submission
266 # return AsyncResult only on non-blocking submission
263 return result
267 return result
264
268
265 @magic_arguments.magic_arguments()
269 @magic_arguments.magic_arguments()
266 @exec_args
270 @exec_args
267 @output_args
271 @output_args
268 @skip_doctest
272 @skip_doctest
269 def cell_px(self, line='', cell=None):
273 def cell_px(self, line='', cell=None):
270 """Executes the cell in parallel.
274 """Executes the cell in parallel.
271
275
272 Examples
276 Examples
273 --------
277 --------
274 ::
278 ::
275
279
276 In [24]: %%px --noblock
280 In [24]: %%px --noblock
277 ....: a = os.getpid()
281 ....: a = os.getpid()
278 Async parallel execution on engine(s): all
282 Async parallel execution on engine(s): all
279
283
280 In [25]: %%px
284 In [25]: %%px
281 ....: print a
285 ....: print a
282 [stdout:0] 1234
286 [stdout:0] 1234
283 [stdout:1] 1235
287 [stdout:1] 1235
284 [stdout:2] 1236
288 [stdout:2] 1236
285 [stdout:3] 1237
289 [stdout:3] 1237
286 """
290 """
287
291
288 args = magic_arguments.parse_argstring(self.cell_px, line)
292 args = magic_arguments.parse_argstring(self.cell_px, line)
289
293
290 if args.targets:
294 if args.targets:
291 save_targets = self.view.targets
295 save_targets = self.view.targets
292 self.view.targets = self._eval_target_str(args.targets)
296 self.view.targets = self._eval_target_str(args.targets)
297 if args.local:
298 self.shell.run_cell(cell)
293 try:
299 try:
294 return self.parallel_execute(cell, block=args.block,
300 return self.parallel_execute(cell, block=args.block,
295 groupby=args.groupby,
301 groupby=args.groupby,
296 save_name=args.save_name,
302 save_name=args.save_name,
297 )
303 )
298 finally:
304 finally:
299 if args.targets:
305 if args.targets:
300 self.view.targets = save_targets
306 self.view.targets = save_targets
301
307
302 @skip_doctest
308 @skip_doctest
303 def autopx(self, line=''):
309 def autopx(self, line=''):
304 """Toggles auto parallel mode.
310 """Toggles auto parallel mode.
305
311
306 Once this is called, all commands typed at the command line are send to
312 Once this is called, all commands typed at the command line are send to
307 the engines to be executed in parallel. To control which engine are
313 the engines to be executed in parallel. To control which engine are
308 used, the ``targets`` attribute of the view before
314 used, the ``targets`` attribute of the view before
309 entering ``%autopx`` mode.
315 entering ``%autopx`` mode.
310
316
311
317
312 Then you can do the following::
318 Then you can do the following::
313
319
314 In [25]: %autopx
320 In [25]: %autopx
315 %autopx to enabled
321 %autopx to enabled
316
322
317 In [26]: a = 10
323 In [26]: a = 10
318 Parallel execution on engine(s): [0,1,2,3]
324 Parallel execution on engine(s): [0,1,2,3]
319 In [27]: print a
325 In [27]: print a
320 Parallel execution on engine(s): [0,1,2,3]
326 Parallel execution on engine(s): [0,1,2,3]
321 [stdout:0] 10
327 [stdout:0] 10
322 [stdout:1] 10
328 [stdout:1] 10
323 [stdout:2] 10
329 [stdout:2] 10
324 [stdout:3] 10
330 [stdout:3] 10
325
331
326
332
327 In [27]: %autopx
333 In [27]: %autopx
328 %autopx disabled
334 %autopx disabled
329 """
335 """
330 if self._autopx:
336 if self._autopx:
331 self._disable_autopx()
337 self._disable_autopx()
332 else:
338 else:
333 self._enable_autopx()
339 self._enable_autopx()
334
340
335 def _enable_autopx(self):
341 def _enable_autopx(self):
336 """Enable %autopx mode by saving the original run_cell and installing
342 """Enable %autopx mode by saving the original run_cell and installing
337 pxrun_cell.
343 pxrun_cell.
338 """
344 """
339 # override run_cell
345 # override run_cell
340 self._original_run_cell = self.shell.run_cell
346 self._original_run_cell = self.shell.run_cell
341 self.shell.run_cell = self.pxrun_cell
347 self.shell.run_cell = self.pxrun_cell
342
348
343 self._autopx = True
349 self._autopx = True
344 print "%autopx enabled"
350 print "%autopx enabled"
345
351
346 def _disable_autopx(self):
352 def _disable_autopx(self):
347 """Disable %autopx by restoring the original InteractiveShell.run_cell.
353 """Disable %autopx by restoring the original InteractiveShell.run_cell.
348 """
354 """
349 if self._autopx:
355 if self._autopx:
350 self.shell.run_cell = self._original_run_cell
356 self.shell.run_cell = self._original_run_cell
351 self._autopx = False
357 self._autopx = False
352 print "%autopx disabled"
358 print "%autopx disabled"
353
359
354 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
360 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
355 """drop-in replacement for InteractiveShell.run_cell.
361 """drop-in replacement for InteractiveShell.run_cell.
356
362
357 This executes code remotely, instead of in the local namespace.
363 This executes code remotely, instead of in the local namespace.
358
364
359 See InteractiveShell.run_cell for details.
365 See InteractiveShell.run_cell for details.
360 """
366 """
361
367
362 if (not raw_cell) or raw_cell.isspace():
368 if (not raw_cell) or raw_cell.isspace():
363 return
369 return
364
370
365 ipself = self.shell
371 ipself = self.shell
366
372
367 with ipself.builtin_trap:
373 with ipself.builtin_trap:
368 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
374 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
369
375
370 # Store raw and processed history
376 # Store raw and processed history
371 if store_history:
377 if store_history:
372 ipself.history_manager.store_inputs(ipself.execution_count,
378 ipself.history_manager.store_inputs(ipself.execution_count,
373 cell, raw_cell)
379 cell, raw_cell)
374
380
375 # ipself.logger.log(cell, raw_cell)
381 # ipself.logger.log(cell, raw_cell)
376
382
377 cell_name = ipself.compile.cache(cell, ipself.execution_count)
383 cell_name = ipself.compile.cache(cell, ipself.execution_count)
378
384
379 try:
385 try:
380 ast.parse(cell, filename=cell_name)
386 ast.parse(cell, filename=cell_name)
381 except (OverflowError, SyntaxError, ValueError, TypeError,
387 except (OverflowError, SyntaxError, ValueError, TypeError,
382 MemoryError):
388 MemoryError):
383 # Case 1
389 # Case 1
384 ipself.showsyntaxerror()
390 ipself.showsyntaxerror()
385 ipself.execution_count += 1
391 ipself.execution_count += 1
386 return None
392 return None
387 except NameError:
393 except NameError:
388 # ignore name errors, because we don't know the remote keys
394 # ignore name errors, because we don't know the remote keys
389 pass
395 pass
390
396
391 if store_history:
397 if store_history:
392 # Write output to the database. Does nothing unless
398 # Write output to the database. Does nothing unless
393 # history output logging is enabled.
399 # history output logging is enabled.
394 ipself.history_manager.store_output(ipself.execution_count)
400 ipself.history_manager.store_output(ipself.execution_count)
395 # Each cell is a *single* input, regardless of how many lines it has
401 # Each cell is a *single* input, regardless of how many lines it has
396 ipself.execution_count += 1
402 ipself.execution_count += 1
397 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
403 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
398 self._disable_autopx()
404 self._disable_autopx()
399 return False
405 return False
400 else:
406 else:
401 try:
407 try:
402 result = self.view.execute(cell, silent=False, block=False)
408 result = self.view.execute(cell, silent=False, block=False)
403 except:
409 except:
404 ipself.showtraceback()
410 ipself.showtraceback()
405 return True
411 return True
406 else:
412 else:
407 if self.view.block:
413 if self.view.block:
408 try:
414 try:
409 result.get()
415 result.get()
410 except:
416 except:
411 self.shell.showtraceback()
417 self.shell.showtraceback()
412 return True
418 return True
413 else:
419 else:
414 with ipself.builtin_trap:
420 with ipself.builtin_trap:
415 result.display_outputs()
421 result.display_outputs()
416 return False
422 return False
417
423
418
424
419 __doc__ = __doc__.format(
425 __doc__ = __doc__.format(
420 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
426 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
421 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
427 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
422 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__,
428 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__,
423 CONFIG_DOC = ' '*8 + ParallelMagics.pxconfig.__doc__,
429 CONFIG_DOC = ' '*8 + ParallelMagics.pxconfig.__doc__,
424 )
430 )
General Comments 0
You need to be logged in to leave comments. Login now