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