##// END OF EJS Templates
Merge pull request #1893 from minrk/compositeerr...
Fernando Perez -
r7503:60e66298 merge
parent child Browse files
Show More
@@ -0,0 +1,424 b''
1 # encoding: utf-8
2 """
3 =============
4 parallelmagic
5 =============
6
7 Magic command interface for interactive parallel work.
8
9 Usage
10 =====
11
12 ``%autopx``
13
14 {AUTOPX_DOC}
15
16 ``%px``
17
18 {PX_DOC}
19
20 ``%pxresult``
21
22 {RESULT_DOC}
23
24 ``%pxconfig``
25
26 {CONFIG_DOC}
27
28 """
29
30 #-----------------------------------------------------------------------------
31 # Copyright (C) 2008 The IPython Development Team
32 #
33 # Distributed under the terms of the BSD License. The full license is in
34 # the file COPYING, distributed as part of this software.
35 #-----------------------------------------------------------------------------
36
37 #-----------------------------------------------------------------------------
38 # Imports
39 #-----------------------------------------------------------------------------
40
41 import ast
42 import re
43
44 from IPython.core.error import UsageError
45 from IPython.core.magic import Magics
46 from IPython.core import magic_arguments
47 from IPython.testing.skipdoctest import skip_doctest
48
49 #-----------------------------------------------------------------------------
50 # Definitions of magic functions for use with IPython
51 #-----------------------------------------------------------------------------
52
53
54 NO_LAST_RESULT = "%pxresult recalls last %px result, which has not yet been used."
55
56 def exec_args(f):
57 """decorator for adding block/targets args for execution
58
59 applied to %pxconfig and %%px
60 """
61 args = [
62 magic_arguments.argument('-b', '--block', action="store_const",
63 const=True, dest='block',
64 help="use blocking (sync) execution",
65 ),
66 magic_arguments.argument('-a', '--noblock', action="store_const",
67 const=False, dest='block',
68 help="use non-blocking (async) execution",
69 ),
70 magic_arguments.argument('-t', '--targets', type=str,
71 help="specify the targets on which to execute",
72 ),
73 magic_arguments.argument('--verbose', action="store_const",
74 const=True, dest="set_verbose",
75 help="print a message at each execution",
76 ),
77 magic_arguments.argument('--no-verbose', action="store_const",
78 const=False, dest="set_verbose",
79 help="don't print any messages",
80 ),
81 ]
82 for a in args:
83 f = a(f)
84 return f
85
86 def output_args(f):
87 """decorator for output-formatting args
88
89 applied to %pxresult and %%px
90 """
91 args = [
92 magic_arguments.argument('-r', action="store_const", dest='groupby',
93 const='order',
94 help="collate outputs in order (same as group-outputs=order)"
95 ),
96 magic_arguments.argument('-e', action="store_const", dest='groupby',
97 const='engine',
98 help="group outputs by engine (same as group-outputs=engine)"
99 ),
100 magic_arguments.argument('--group-outputs', dest='groupby', type=str,
101 choices=['engine', 'order', 'type'], default='type',
102 help="""Group the outputs in a particular way.
103
104 Choices are:
105
106 type: group outputs of all engines by type (stdout, stderr, displaypub, etc.).
107
108 engine: display all output for each engine together.
109
110 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
112 figure of each engine will be displayed, then the second of each, etc.
113 """
114 ),
115 magic_arguments.argument('-o', '--out', dest='save_name', type=str,
116 help="""store the AsyncResult object for this computation
117 in the global namespace under this name.
118 """
119 ),
120 ]
121 for a in args:
122 f = a(f)
123 return f
124
125 class ParallelMagics(Magics):
126 """A set of magics useful when controlling a parallel IPython cluster.
127 """
128
129 # magic-related
130 magics = None
131 registered = True
132
133 # suffix for magics
134 suffix = ''
135 # A flag showing if autopx is activated or not
136 _autopx = False
137 # the current view used by the magics:
138 view = None
139 # last result cache for %pxresult
140 last_result = None
141 # verbose flag
142 verbose = False
143
144 def __init__(self, shell, view, suffix=''):
145 self.view = view
146 self.suffix = suffix
147
148 # register magics
149 self.magics = dict(cell={},line={})
150 line_magics = self.magics['line']
151
152 px = 'px' + suffix
153 if not suffix:
154 # keep %result for legacy compatibility
155 line_magics['result'] = self.result
156
157 line_magics['pxresult' + suffix] = self.result
158 line_magics[px] = self.px
159 line_magics['pxconfig' + suffix] = self.pxconfig
160 line_magics['auto' + px] = self.autopx
161
162 self.magics['cell'][px] = self.cell_px
163
164 super(ParallelMagics, self).__init__(shell=shell)
165
166 def _eval_target_str(self, ts):
167 if ':' in ts:
168 targets = eval("self.view.client.ids[%s]" % ts)
169 elif 'all' in ts:
170 targets = 'all'
171 else:
172 targets = eval(ts)
173 return targets
174
175 @magic_arguments.magic_arguments()
176 @exec_args
177 def pxconfig(self, line):
178 """configure default targets/blocking for %px magics"""
179 args = magic_arguments.parse_argstring(self.pxconfig, line)
180 if args.targets:
181 self.view.targets = self._eval_target_str(args.targets)
182 if args.block is not None:
183 self.view.block = args.block
184 if args.set_verbose is not None:
185 self.verbose = args.set_verbose
186
187 @magic_arguments.magic_arguments()
188 @output_args
189 @skip_doctest
190 def result(self, line=''):
191 """Print the result of the last asynchronous %px command.
192
193 This lets you recall the results of %px computations after
194 asynchronous submission (block=False).
195
196 Examples
197 --------
198 ::
199
200 In [23]: %px os.getpid()
201 Async parallel execution on engine(s): all
202
203 In [24]: %pxresult
204 Out[8:10]: 60920
205 Out[9:10]: 60921
206 Out[10:10]: 60922
207 Out[11:10]: 60923
208 """
209 args = magic_arguments.parse_argstring(self.result, line)
210
211 if self.last_result is None:
212 raise UsageError(NO_LAST_RESULT)
213
214 self.last_result.get()
215 self.last_result.display_outputs(groupby=args.groupby)
216
217 @skip_doctest
218 def px(self, line=''):
219 """Executes the given python command in parallel.
220
221 Examples
222 --------
223 ::
224
225 In [24]: %px a = os.getpid()
226 Parallel execution on engine(s): all
227
228 In [25]: %px print a
229 [stdout:0] 1234
230 [stdout:1] 1235
231 [stdout:2] 1236
232 [stdout:3] 1237
233 """
234 return self.parallel_execute(line)
235
236 def parallel_execute(self, cell, block=None, groupby='type', save_name=None):
237 """implementation used by %px and %%parallel"""
238
239 # defaults:
240 block = self.view.block if block is None else block
241
242 base = "Parallel" if block else "Async parallel"
243
244 targets = self.view.targets
245 if isinstance(targets, list) and len(targets) > 10:
246 str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:]
247 else:
248 str_targets = str(targets)
249 if self.verbose:
250 print base + " execution on engine(s): %s" % str_targets
251
252 result = self.view.execute(cell, silent=False, block=False)
253 self.last_result = result
254
255 if save_name:
256 self.shell.user_ns[save_name] = result
257
258 if block:
259 result.get()
260 result.display_outputs(groupby)
261 else:
262 # return AsyncResult only on non-blocking submission
263 return result
264
265 @magic_arguments.magic_arguments()
266 @exec_args
267 @output_args
268 @skip_doctest
269 def cell_px(self, line='', cell=None):
270 """Executes the cell in parallel.
271
272 Examples
273 --------
274 ::
275
276 In [24]: %%px --noblock
277 ....: a = os.getpid()
278 Async parallel execution on engine(s): all
279
280 In [25]: %%px
281 ....: print a
282 [stdout:0] 1234
283 [stdout:1] 1235
284 [stdout:2] 1236
285 [stdout:3] 1237
286 """
287
288 args = magic_arguments.parse_argstring(self.cell_px, line)
289
290 if args.targets:
291 save_targets = self.view.targets
292 self.view.targets = self._eval_target_str(args.targets)
293 try:
294 return self.parallel_execute(cell, block=args.block,
295 groupby=args.groupby,
296 save_name=args.save_name,
297 )
298 finally:
299 if args.targets:
300 self.view.targets = save_targets
301
302 @skip_doctest
303 def autopx(self, line=''):
304 """Toggles auto parallel mode.
305
306 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
308 used, the ``targets`` attribute of the view before
309 entering ``%autopx`` mode.
310
311
312 Then you can do the following::
313
314 In [25]: %autopx
315 %autopx to enabled
316
317 In [26]: a = 10
318 Parallel execution on engine(s): [0,1,2,3]
319 In [27]: print a
320 Parallel execution on engine(s): [0,1,2,3]
321 [stdout:0] 10
322 [stdout:1] 10
323 [stdout:2] 10
324 [stdout:3] 10
325
326
327 In [27]: %autopx
328 %autopx disabled
329 """
330 if self._autopx:
331 self._disable_autopx()
332 else:
333 self._enable_autopx()
334
335 def _enable_autopx(self):
336 """Enable %autopx mode by saving the original run_cell and installing
337 pxrun_cell.
338 """
339 # override run_cell
340 self._original_run_cell = self.shell.run_cell
341 self.shell.run_cell = self.pxrun_cell
342
343 self._autopx = True
344 print "%autopx enabled"
345
346 def _disable_autopx(self):
347 """Disable %autopx by restoring the original InteractiveShell.run_cell.
348 """
349 if self._autopx:
350 self.shell.run_cell = self._original_run_cell
351 self._autopx = False
352 print "%autopx disabled"
353
354 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
355 """drop-in replacement for InteractiveShell.run_cell.
356
357 This executes code remotely, instead of in the local namespace.
358
359 See InteractiveShell.run_cell for details.
360 """
361
362 if (not raw_cell) or raw_cell.isspace():
363 return
364
365 ipself = self.shell
366
367 with ipself.builtin_trap:
368 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
369
370 # Store raw and processed history
371 if store_history:
372 ipself.history_manager.store_inputs(ipself.execution_count,
373 cell, raw_cell)
374
375 # ipself.logger.log(cell, raw_cell)
376
377 cell_name = ipself.compile.cache(cell, ipself.execution_count)
378
379 try:
380 ast.parse(cell, filename=cell_name)
381 except (OverflowError, SyntaxError, ValueError, TypeError,
382 MemoryError):
383 # Case 1
384 ipself.showsyntaxerror()
385 ipself.execution_count += 1
386 return None
387 except NameError:
388 # ignore name errors, because we don't know the remote keys
389 pass
390
391 if store_history:
392 # Write output to the database. Does nothing unless
393 # history output logging is enabled.
394 ipself.history_manager.store_output(ipself.execution_count)
395 # Each cell is a *single* input, regardless of how many lines it has
396 ipself.execution_count += 1
397 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
398 self._disable_autopx()
399 return False
400 else:
401 try:
402 result = self.view.execute(cell, silent=False, block=False)
403 except:
404 ipself.showtraceback()
405 return True
406 else:
407 if self.view.block:
408 try:
409 result.get()
410 except:
411 self.shell.showtraceback()
412 return True
413 else:
414 with ipself.builtin_trap:
415 result.display_outputs()
416 return False
417
418
419 __doc__ = __doc__.format(
420 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
421 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
422 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__,
423 CONFIG_DOC = ' '*8 + ParallelMagics.pxconfig.__doc__,
424 )
@@ -0,0 +1,389 b''
1 .. _parallel_magics:
2
3 =======================
4 Parallel Magic Commands
5 =======================
6
7 We provide a few IPython magic commands
8 that make it a bit more pleasant to execute Python commands on the engines interactively.
9 These are mainly shortcuts to :meth:`.DirectView.execute`
10 and :meth:`.AsyncResult.display_outputs` methods repsectively.
11
12 These magics will automatically become available when you create a Client:
13
14 .. sourcecode:: ipython
15
16 In [2]: rc = parallel.Client()
17
18 The initially active View will have attributes ``targets='all', block=True``,
19 which is a blocking view of all engines, evaluated at request time
20 (adding/removing engines will change where this view's tasks will run).
21
22 The Magics
23 ==========
24
25 %px
26 ---
27
28 The %px magic executes a single Python command on the engines
29 specified by the :attr:`targets` attribute of the :class:`DirectView` instance:
30
31 .. sourcecode:: ipython
32
33 # import numpy here and everywhere
34 In [25]: with rc[:].sync_imports():
35 ....: import numpy
36 importing numpy on engine(s)
37
38 In [27]: %px a = numpy.random.rand(2,2)
39 Parallel execution on engines: [0, 1, 2, 3]
40
41 In [28]: %px numpy.linalg.eigvals(a)
42 Parallel execution on engines: [0, 1, 2, 3]
43 Out [0:68]: array([ 0.77120707, -0.19448286])
44 Out [1:68]: array([ 1.10815921, 0.05110369])
45 Out [2:68]: array([ 0.74625527, -0.37475081])
46 Out [3:68]: array([ 0.72931905, 0.07159743])
47
48 In [29]: %px print 'hi'
49 Parallel execution on engine(s): all
50 [stdout:0] hi
51 [stdout:1] hi
52 [stdout:2] hi
53 [stdout:3] hi
54
55
56 Since engines are IPython as well, you can even run magics remotely:
57
58 .. sourcecode:: ipython
59
60 In [28]: %px %pylab inline
61 Parallel execution on engine(s): all
62 [stdout:0]
63 Welcome to pylab, a matplotlib-based Python environment...
64 For more information, type 'help(pylab)'.
65 [stdout:1]
66 Welcome to pylab, a matplotlib-based Python environment...
67 For more information, type 'help(pylab)'.
68 [stdout:2]
69 Welcome to pylab, a matplotlib-based Python environment...
70 For more information, type 'help(pylab)'.
71 [stdout:3]
72 Welcome to pylab, a matplotlib-based Python environment...
73 For more information, type 'help(pylab)'.
74
75 And once in pylab mode with the inline backend,
76 you can make plots and they will be displayed in your frontend
77 if it suports the inline figures (e.g. notebook or qtconsole):
78
79 .. sourcecode:: ipython
80
81 In [40]: %px plot(rand(100))
82 Parallel execution on engine(s): all
83 <plot0>
84 <plot1>
85 <plot2>
86 <plot3>
87 Out[0:79]: [<matplotlib.lines.Line2D at 0x10a6286d0>]
88 Out[1:79]: [<matplotlib.lines.Line2D at 0x10b9476d0>]
89 Out[2:79]: [<matplotlib.lines.Line2D at 0x110652750>]
90 Out[3:79]: [<matplotlib.lines.Line2D at 0x10c6566d0>]
91
92
93 %%px Cell Magic
94 ---------------
95
96 %%px can be used as a Cell Magic, which accepts some arguments for controlling
97 the execution.
98
99
100 Targets and Blocking
101 ********************
102
103 %%px accepts ``--targets`` for controlling which engines on which to run,
104 and ``--[no]block`` for specifying the blocking behavior of this cell,
105 independent of the defaults for the View.
106
107 .. sourcecode:: ipython
108
109 In [6]: %%px --targets ::2
110 ...: print "I am even"
111 ...:
112 Parallel execution on engine(s): [0, 2]
113 [stdout:0] I am even
114 [stdout:2] I am even
115
116 In [7]: %%px --targets 1
117 ...: print "I am number 1"
118 ...:
119 Parallel execution on engine(s): 1
120 I am number 1
121
122 In [8]: %%px
123 ...: print "still 'all' by default"
124 ...:
125 Parallel execution on engine(s): all
126 [stdout:0] still 'all' by default
127 [stdout:1] still 'all' by default
128 [stdout:2] still 'all' by default
129 [stdout:3] still 'all' by default
130
131 In [9]: %%px --noblock
132 ...: import time
133 ...: time.sleep(1)
134 ...: time.time()
135 ...:
136 Async parallel execution on engine(s): all
137 Out[9]: <AsyncResult: execute>
138
139 In [10]: %pxresult
140 Out[0:12]: 1339454561.069116
141 Out[1:10]: 1339454561.076752
142 Out[2:12]: 1339454561.072837
143 Out[3:10]: 1339454561.066665
144
145
146 .. seealso::
147
148 :ref:`%pxconfig` accepts these same arguments for changing the *default*
149 values of targets/blocking for the active View.
150
151
152 Output Display
153 **************
154
155
156 %%px also accepts a ``--group-outputs`` argument,
157 which adjusts how the outputs of multiple engines are presented.
158
159 .. seealso::
160
161 :meth:`.AsyncResult.display_outputs` for the grouping options.
162
163 .. sourcecode:: ipython
164
165 In [50]: %%px --block --group-outputs=engine
166 ....: import numpy as np
167 ....: A = np.random.random((2,2))
168 ....: ev = numpy.linalg.eigvals(A)
169 ....: print ev
170 ....: ev.max()
171 ....:
172 Parallel execution on engine(s): all
173 [stdout:0] [ 0.60640442 0.95919621]
174 Out [0:73]: 0.9591962130899806
175 [stdout:1] [ 0.38501813 1.29430871]
176 Out [1:73]: 1.2943087091452372
177 [stdout:2] [-0.85925141 0.9387692 ]
178 Out [2:73]: 0.93876920456230284
179 [stdout:3] [ 0.37998269 1.24218246]
180 Out [3:73]: 1.2421824618493817
181
182
183 %pxresult
184 ---------
185
186 If you are using %px in non-blocking mode, you won't get output.
187 You can use %pxresult to display the outputs of the latest command,
188 just as is done when %px is blocking:
189
190 .. sourcecode:: ipython
191
192 In [39]: dv.block = False
193
194 In [40]: %px print 'hi'
195 Async parallel execution on engine(s): all
196
197 In [41]: %pxresult
198 [stdout:0] hi
199 [stdout:1] hi
200 [stdout:2] hi
201 [stdout:3] hi
202
203 %pxresult simply calls :meth:`.AsyncResult.display_outputs` on the most recent request.
204 It accepts the same output-grouping arguments as %%px, so you can use it to view
205 a result in different ways.
206
207
208 %autopx
209 -------
210
211 The %autopx magic switches to a mode where everything you type is executed
212 on the engines until you do %autopx again.
213
214 .. sourcecode:: ipython
215
216 In [30]: dv.block=True
217
218 In [31]: %autopx
219 %autopx enabled
220
221 In [32]: max_evals = []
222
223 In [33]: for i in range(100):
224 ....: a = numpy.random.rand(10,10)
225 ....: a = a+a.transpose()
226 ....: evals = numpy.linalg.eigvals(a)
227 ....: max_evals.append(evals[0].real)
228 ....:
229
230 In [34]: print "Average max eigenvalue is: %f" % (sum(max_evals)/len(max_evals))
231 [stdout:0] Average max eigenvalue is: 10.193101
232 [stdout:1] Average max eigenvalue is: 10.064508
233 [stdout:2] Average max eigenvalue is: 10.055724
234 [stdout:3] Average max eigenvalue is: 10.086876
235
236 In [35]: %autopx
237 Auto Parallel Disabled
238
239 %pxconfig
240 ---------
241
242 The default targets and blocking behavior for the magics are governed by the :attr:`block`
243 and :attr:`targets` attribute of the active View. If you have a handle for the view,
244 you can set these attributes directly, but if you don't, you can change them with
245 the %pxconfig magic:
246
247 .. sourcecode:: ipython
248
249 In [3]: %pxconfig --block
250
251 In [5]: %px print 'hi'
252 Parallel execution on engine(s): all
253 [stdout:0] hi
254 [stdout:1] hi
255 [stdout:2] hi
256 [stdout:3] hi
257
258 In [6]: %pxconfig --targets ::2
259
260 In [7]: %px print 'hi'
261 Parallel execution on engine(s): [0, 2]
262 [stdout:0] hi
263 [stdout:2] hi
264
265 In [8]: %pxconfig --noblock
266
267 In [9]: %px print 'are you there?'
268 Async parallel execution on engine(s): [0, 2]
269 Out[9]: <AsyncResult: execute>
270
271 In [10]: %pxresult
272 [stdout:0] are you there?
273 [stdout:2] are you there?
274
275
276 Multiple Active Views
277 =====================
278
279 The parallel magics are associated with a particular :class:`~.DirectView` object.
280 You can change the active view by calling the :meth:`~.DirectView.activate` method
281 on any view.
282
283 .. sourcecode:: ipython
284
285 In [11]: even = rc[::2]
286
287 In [12]: even.activate()
288
289 In [13]: %px print 'hi'
290 Async parallel execution on engine(s): [0, 2]
291 Out[13]: <AsyncResult: execute>
292
293 In [14]: even.block = True
294
295 In [15]: %px print 'hi'
296 Parallel execution on engine(s): [0, 2]
297 [stdout:0] hi
298 [stdout:2] hi
299
300 When activating a View, you can also specify a *suffix*, so that a whole different
301 set of magics are associated with that view, without replacing the existing ones.
302
303 .. sourcecode:: ipython
304
305 # restore the original DirecView to the base %px magics
306 In [16]: rc.activate()
307 Out[16]: <DirectView all>
308
309 In [17]: even.activate('_even')
310
311 In [18]: %px print 'hi all'
312 Parallel execution on engine(s): all
313 [stdout:0] hi all
314 [stdout:1] hi all
315 [stdout:2] hi all
316 [stdout:3] hi all
317
318 In [19]: %px_even print "We aren't odd!"
319 Parallel execution on engine(s): [0, 2]
320 [stdout:0] We aren't odd!
321 [stdout:2] We aren't odd!
322
323 This suffix is applied to the end of all magics, e.g. %autopx_even, %pxresult_even, etc.
324
325 For convenience, the :class:`~.Client` has a :meth:`~.Client.activate` method as well,
326 which creates a DirectView with block=True, activates it, and returns the new View.
327
328 The initial magics registered when you create a client are the result of a call to
329 :meth:`rc.activate` with default args.
330
331
332 Engines as Kernels
333 ==================
334
335 Engines are really the same object as the Kernels used elsewhere in IPython,
336 with the minor exception that engines connect to a controller, while regular kernels
337 bind their sockets, listening for connections from a QtConsole or other frontends.
338
339 Sometimes for debugging or inspection purposes, you would like a QtConsole connected
340 to an engine for more direct interaction. You can do this by first instructing
341 the Engine to *also* bind its kernel, to listen for connections:
342
343 .. sourcecode:: ipython
344
345 In [50]: %px from IPython.parallel import bind_kernel; bind_kernel()
346
347 Then, if your engines are local, you can start a qtconsole right on the engine(s):
348
349 .. sourcecode:: ipython
350
351 In [51]: %px %qtconsole
352
353 Careful with this one, because if your view is of 16 engines it will start 16 QtConsoles!
354
355 Or you can view just the connection info, and work out the right way to connect to the engines,
356 depending on where they live and where you are:
357
358 .. sourcecode:: ipython
359
360 In [51]: %px %connect_info
361 Parallel execution on engine(s): all
362 [stdout:0]
363 {
364 "stdin_port": 60387,
365 "ip": "127.0.0.1",
366 "hb_port": 50835,
367 "key": "eee2dd69-7dd3-4340-bf3e-7e2e22a62542",
368 "shell_port": 55328,
369 "iopub_port": 58264
370 }
371
372 Paste the above JSON into a file, and connect with:
373 $> ipython <app> --existing <file>
374 or, if you are local, you can connect with just:
375 $> ipython <app> --existing kernel-60125.json
376 or even just:
377 $> ipython <app> --existing
378 if this is the most recent IPython session you have started.
379 [stdout:1]
380 {
381 "stdin_port": 61869,
382 ...
383
384 .. note::
385
386 ``%qtconsole`` will call :func:`bind_kernel` on an engine if it hasn't been done already,
387 so you can often skip that first step.
388
389
@@ -17,9 +17,3 b" if hasattr(app, 'exec_lines'):"
17 else:
17 else:
18 app.exec_lines = [lines]
18 app.exec_lines = [lines]
19
19
20 # Load the parallelmagic extension to enable %result, %px, %autopx magics.
21 if hasattr(app, 'extensions'):
22 app.extensions.append('parallelmagic')
23 else:
24 app.extensions = ['parallelmagic']
25
@@ -1697,6 +1697,11 b' class InteractiveShell(SingletonConfigurable):'
1697 care of calling it if needed, so unless you are explicitly catching a
1697 care of calling it if needed, so unless you are explicitly catching a
1698 SyntaxError exception, don't try to analyze the stack manually and
1698 SyntaxError exception, don't try to analyze the stack manually and
1699 simply call this method."""
1699 simply call this method."""
1700 try:
1701 from IPython.parallel.error import RemoteError
1702 except ImportError:
1703 class RemoteError(Exception): pass
1704
1700
1705
1701 try:
1706 try:
1702 try:
1707 try:
@@ -1711,6 +1716,10 b' class InteractiveShell(SingletonConfigurable):'
1711 self.showsyntaxerror(filename)
1716 self.showsyntaxerror(filename)
1712 elif etype is UsageError:
1717 elif etype is UsageError:
1713 self.write_err("UsageError: %s" % value)
1718 self.write_err("UsageError: %s" % value)
1719 elif issubclass(etype, RemoteError):
1720 # IPython.parallel remote exceptions.
1721 # Draw the remote traceback, not the local one.
1722 self._showtraceback(etype, value, value.render_traceback())
1714 else:
1723 else:
1715 if exception_only:
1724 if exception_only:
1716 stb = ['An exception has occurred, use %tb to see '
1725 stb = ['An exception has occurred, use %tb to see '
@@ -4,23 +4,7 b''
4 parallelmagic
4 parallelmagic
5 =============
5 =============
6
6
7 Magic command interface for interactive parallel work.
7 Deprecated, parallel magics are no longer an extension.
8
9 Usage
10 =====
11
12 ``%autopx``
13
14 {AUTOPX_DOC}
15
16 ``%px``
17
18 {PX_DOC}
19
20 ``%result``
21
22 {RESULT_DOC}
23
24 """
8 """
25
9
26 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
@@ -30,314 +14,7 b' Usage'
30 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
31 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
32
16
33 #-----------------------------------------------------------------------------
17 from warnings import warn
34 # Imports
35 #-----------------------------------------------------------------------------
36
37 import ast
38 import re
39
40 from IPython.core.error import UsageError
41 from IPython.core.magic import Magics, magics_class, line_magic, cell_magic
42 from IPython.testing.skipdoctest import skip_doctest
43
44 #-----------------------------------------------------------------------------
45 # Definitions of magic functions for use with IPython
46 #-----------------------------------------------------------------------------
47
48
49 NO_ACTIVE_VIEW = "Use activate() on a DirectView object to use it with magics."
50
51
52 @magics_class
53 class ParallelMagics(Magics):
54 """A set of magics useful when controlling a parallel IPython cluster.
55 """
56
57 # A flag showing if autopx is activated or not
58 _autopx = False
59 # the current view used by the magics:
60 active_view = None
61
62 @skip_doctest
63 @line_magic
64 def result(self, parameter_s=''):
65 """Print the result of command i on all engines.
66
67 To use this a :class:`DirectView` instance must be created
68 and then activated by calling its :meth:`activate` method.
69
70 This lets you recall the results of %px computations after
71 asynchronous submission (view.block=False).
72
73 Then you can do the following::
74
75 In [23]: %px os.getpid()
76 Async parallel execution on engine(s): all
77
78 In [24]: %result
79 [ 8] Out[10]: 60920
80 [ 9] Out[10]: 60921
81 [10] Out[10]: 60922
82 [11] Out[10]: 60923
83 """
84
85 if self.active_view is None:
86 raise UsageError(NO_ACTIVE_VIEW)
87
88 stride = len(self.active_view)
89 try:
90 index = int(parameter_s)
91 except:
92 index = -1
93 msg_ids = self.active_view.history[stride * index:(stride * (index + 1)) or None]
94
95 result = self.active_view.get_result(msg_ids)
96
97 result.get()
98 result.display_outputs()
99
100 @skip_doctest
101 @line_magic
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 else:
138 # return AsyncResult only on non-blocking submission
139 return result
140
141 @skip_doctest
142 @cell_magic('px')
143 def cell_px(self, line='', cell=None):
144 """Executes the given python command in parallel.
145
146 Cell magic usage:
147
148 %%px [-o] [-e] [--group-options=type|engine|order] [--[no]block]
149
150 Options (%%px cell magic only):
151
152 -o: collate outputs in oder (same as group-outputs=order)
153
154 -e: group outputs by engine (same as group-outputs=engine)
155
156 --group-outputs=type [default behavior]:
157 each output type (stdout, stderr, displaypub) for all engines
158 displayed together.
159
160 --group-outputs=order:
161 The same as 'type', but individual displaypub outputs (e.g. plots)
162 will be interleaved, so it will display all of the first plots,
163 then all of the second plots, etc.
164
165 --group-outputs=engine:
166 All of an engine's output is displayed before moving on to the next.
167
168 --[no]block:
169 Whether or not to block for the execution to complete
170 (and display the results). If unspecified, the active view's
171
172
173 To use this a :class:`DirectView` instance must be created
174 and then activated by calling its :meth:`activate` method.
175
176 Then you can do the following::
177
178 In [24]: %%parallel --noblock a = os.getpid()
179 Async parallel execution on engine(s): all
180
181 In [25]: %px print a
182 [stdout:0] 1234
183 [stdout:1] 1235
184 [stdout:2] 1236
185 [stdout:3] 1237
186 """
187
188 block = None
189 groupby = 'type'
190 # as a cell magic, we accept args
191 opts, _ = self.parse_options(line, 'oe', 'group-outputs=', 'block', 'noblock')
192
193 if 'group-outputs' in opts:
194 groupby = opts['group-outputs']
195 elif 'o' in opts:
196 groupby = 'order'
197 elif 'e' in opts:
198 groupby = 'engine'
199
200 if 'block' in opts:
201 block = True
202 elif 'noblock' in opts:
203 block = False
204
205 return self.parallel_execute(cell, block=block, groupby=groupby)
206
207 @skip_doctest
208 @line_magic
209 def autopx(self, parameter_s=''):
210 """Toggles auto parallel mode.
211
212 To use this a :class:`DirectView` instance must be created
213 and then activated by calling its :meth:`activate` method. Once this
214 is called, all commands typed at the command line are send to
215 the engines to be executed in parallel. To control which engine
216 are used, set the ``targets`` attributed of the multiengine client
217 before entering ``%autopx`` mode.
218
219 Then you can do the following::
220
221 In [25]: %autopx
222 %autopx to enabled
223
224 In [26]: a = 10
225 Parallel execution on engine(s): [0,1,2,3]
226 In [27]: print a
227 Parallel execution on engine(s): [0,1,2,3]
228 [stdout:0] 10
229 [stdout:1] 10
230 [stdout:2] 10
231 [stdout:3] 10
232
233
234 In [27]: %autopx
235 %autopx disabled
236 """
237 if self._autopx:
238 self._disable_autopx()
239 else:
240 self._enable_autopx()
241
242 def _enable_autopx(self):
243 """Enable %autopx mode by saving the original run_cell and installing
244 pxrun_cell.
245 """
246 if self.active_view is None:
247 raise UsageError(NO_ACTIVE_VIEW)
248
249 # override run_cell
250 self._original_run_cell = self.shell.run_cell
251 self.shell.run_cell = self.pxrun_cell
252
253 self._autopx = True
254 print "%autopx enabled"
255
256 def _disable_autopx(self):
257 """Disable %autopx by restoring the original InteractiveShell.run_cell.
258 """
259 if self._autopx:
260 self.shell.run_cell = self._original_run_cell
261 self._autopx = False
262 print "%autopx disabled"
263
264 def pxrun_cell(self, raw_cell, store_history=False, silent=False):
265 """drop-in replacement for InteractiveShell.run_cell.
266
267 This executes code remotely, instead of in the local namespace.
268
269 See InteractiveShell.run_cell for details.
270 """
271
272 if (not raw_cell) or raw_cell.isspace():
273 return
274
275 ipself = self.shell
276
277 with ipself.builtin_trap:
278 cell = ipself.prefilter_manager.prefilter_lines(raw_cell)
279
280 # Store raw and processed history
281 if store_history:
282 ipself.history_manager.store_inputs(ipself.execution_count,
283 cell, raw_cell)
284
285 # ipself.logger.log(cell, raw_cell)
286
287 cell_name = ipself.compile.cache(cell, ipself.execution_count)
288
289 try:
290 ast.parse(cell, filename=cell_name)
291 except (OverflowError, SyntaxError, ValueError, TypeError,
292 MemoryError):
293 # Case 1
294 ipself.showsyntaxerror()
295 ipself.execution_count += 1
296 return None
297 except NameError:
298 # ignore name errors, because we don't know the remote keys
299 pass
300
301 if store_history:
302 # Write output to the database. Does nothing unless
303 # history output logging is enabled.
304 ipself.history_manager.store_output(ipself.execution_count)
305 # Each cell is a *single* input, regardless of how many lines it has
306 ipself.execution_count += 1
307 if re.search(r'get_ipython\(\)\.magic\(u?["\']%?autopx', cell):
308 self._disable_autopx()
309 return False
310 else:
311 try:
312 result = self.active_view.execute(cell, silent=False, block=False)
313 except:
314 ipself.showtraceback()
315 return True
316 else:
317 if self.active_view.block:
318 try:
319 result.get()
320 except:
321 self.shell.showtraceback()
322 return True
323 else:
324 with ipself.builtin_trap:
325 result.display_outputs()
326 return False
327
328
329 __doc__ = __doc__.format(
330 AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__,
331 PX_DOC = ' '*8 + ParallelMagics.px.__doc__,
332 RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__
333 )
334
335 _loaded = False
336
337
18
338 def load_ipython_extension(ip):
19 def load_ipython_extension(ip):
339 """Load the extension in IPython."""
20 warn("Parallel Magics are no longer defined in an extension", DeprecationWarning)
340 global _loaded
341 if not _loaded:
342 ip.register_magics(ParallelMagics)
343 _loaded = True
@@ -36,8 +36,9 b' from .client.asyncresult import *'
36 from .client.client import Client
36 from .client.client import Client
37 from .client.remotefunction import *
37 from .client.remotefunction import *
38 from .client.view import *
38 from .client.view import *
39 from .util import interactive
40 from .controller.dependency import *
39 from .controller.dependency import *
40 from .error import *
41 from .util import interactive
41
42
42 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
43 # Functions
44 # Functions
@@ -158,6 +158,8 b' class AsyncResult(object):'
158 self._success = True
158 self._success = True
159 finally:
159 finally:
160 self._metadata = map(self._client.metadata.get, self.msg_ids)
160 self._metadata = map(self._client.metadata.get, self.msg_ids)
161 self._wait_for_outputs(10)
162
161
163
162
164
163 def successful(self):
165 def successful(self):
@@ -424,6 +426,19 b' class AsyncResult(object):'
424 if self.pyout is not None:
426 if self.pyout is not None:
425 display(self.get())
427 display(self.get())
426
428
429 def _wait_for_outputs(self, timeout=-1):
430 """wait for the 'status=idle' message that indicates we have all outputs
431 """
432 if not self._success:
433 # don't wait on errors
434 return
435 tic = time.time()
436 while not all(md['outputs_ready'] for md in self._metadata):
437 time.sleep(0.01)
438 self._client._flush_iopub(self._client._iopub_socket)
439 if timeout >= 0 and time.time() > tic + timeout:
440 break
441
427 @check_ready
442 @check_ready
428 def display_outputs(self, groupby="type"):
443 def display_outputs(self, groupby="type"):
429 """republish the outputs of the computation
444 """republish the outputs of the computation
@@ -453,8 +468,6 b' class AsyncResult(object):'
453 several plots, and you would like to see all of the first
468 several plots, and you would like to see all of the first
454 plots together, then all of the second plots, and so on.
469 plots together, then all of the second plots, and so on.
455 """
470 """
456 # flush iopub, just in case
457 self._client._flush_iopub(self._client._iopub_socket)
458 if self._single_result:
471 if self._single_result:
459 self._display_single_result()
472 self._display_single_result()
460 return
473 return
@@ -619,7 +632,6 b' class AsyncMapResult(AsyncResult):'
619 yield r
632 yield r
620
633
621
634
622
623 class AsyncHubResult(AsyncResult):
635 class AsyncHubResult(AsyncResult):
624 """Class to wrap pending results that must be requested from the Hub.
636 """Class to wrap pending results that must be requested from the Hub.
625
637
@@ -627,6 +639,10 b' class AsyncHubResult(AsyncResult):'
627 so use `AsyncHubResult.wait()` sparingly.
639 so use `AsyncHubResult.wait()` sparingly.
628 """
640 """
629
641
642 def _wait_for_outputs(self, timeout=None):
643 """no-op, because HubResults are never incomplete"""
644 return
645
630 def wait(self, timeout=-1):
646 def wait(self, timeout=-1):
631 """wait for result to complete."""
647 """wait for result to complete."""
632 start = time.time()
648 start = time.time()
@@ -32,6 +32,7 b' import zmq'
32
32
33 from IPython.config.configurable import MultipleInstanceError
33 from IPython.config.configurable import MultipleInstanceError
34 from IPython.core.application import BaseIPythonApplication
34 from IPython.core.application import BaseIPythonApplication
35 from IPython.core.profiledir import ProfileDir, ProfileDirError
35
36
36 from IPython.utils.coloransi import TermColors
37 from IPython.utils.coloransi import TermColors
37 from IPython.utils.jsonutil import rekey
38 from IPython.utils.jsonutil import rekey
@@ -50,7 +51,6 b' from IPython.parallel import util'
50 from IPython.zmq.session import Session, Message
51 from IPython.zmq.session import Session, Message
51
52
52 from .asyncresult import AsyncResult, AsyncHubResult
53 from .asyncresult import AsyncResult, AsyncHubResult
53 from IPython.core.profiledir import ProfileDir, ProfileDirError
54 from .view import DirectView, LoadBalancedView
54 from .view import DirectView, LoadBalancedView
55
55
56 if sys.version_info[0] >= 3:
56 if sys.version_info[0] >= 3:
@@ -184,6 +184,7 b' class Metadata(dict):'
184 'stdout' : '',
184 'stdout' : '',
185 'stderr' : '',
185 'stderr' : '',
186 'outputs' : [],
186 'outputs' : [],
187 'outputs_ready' : False,
187 }
188 }
188 self.update(md)
189 self.update(md)
189 self.update(dict(*args, **kwargs))
190 self.update(dict(*args, **kwargs))
@@ -480,6 +481,18 b' class Client(HasTraits):'
480 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
481 self._queue_handlers = {'execute_reply' : self._handle_execute_reply,
481 'apply_reply' : self._handle_apply_reply}
482 'apply_reply' : self._handle_apply_reply}
482 self._connect(sshserver, ssh_kwargs, timeout)
483 self._connect(sshserver, ssh_kwargs, timeout)
484
485 # last step: setup magics, if we are in IPython:
486
487 try:
488 ip = get_ipython()
489 except NameError:
490 return
491 else:
492 if 'px' not in ip.magics_manager.magics:
493 # in IPython but we are the first Client.
494 # activate a default view for parallel magics.
495 self.activate()
483
496
484 def __del__(self):
497 def __del__(self):
485 """cleanup sockets, but _not_ context."""
498 """cleanup sockets, but _not_ context."""
@@ -868,6 +881,10 b' class Client(HasTraits):'
868 md['outputs'].append(content)
881 md['outputs'].append(content)
869 elif msg_type == 'pyout':
882 elif msg_type == 'pyout':
870 md['pyout'] = content
883 md['pyout'] = content
884 elif msg_type == 'status':
885 # idle message comes after all outputs
886 if content['execution_state'] == 'idle':
887 md['outputs_ready'] = True
871 else:
888 else:
872 # unhandled msg_type (status, etc.)
889 # unhandled msg_type (status, etc.)
873 pass
890 pass
@@ -905,6 +922,29 b' class Client(HasTraits):'
905 # always copy:
922 # always copy:
906 return list(self._ids)
923 return list(self._ids)
907
924
925 def activate(self, targets='all', suffix=''):
926 """Create a DirectView and register it with IPython magics
927
928 Defines the magics `%px, %autopx, %pxresult, %%px`
929
930 Parameters
931 ----------
932
933 targets: int, list of ints, or 'all'
934 The engines on which the view's magics will run
935 suffix: str [default: '']
936 The suffix, if any, for the magics. This allows you to have
937 multiple views associated with parallel magics at the same time.
938
939 e.g. ``rc.activate(targets=0, suffix='0')`` will give you
940 the magics ``%px0``, ``%pxresult0``, etc. for running magics just
941 on engine 0.
942 """
943 view = self.direct_view(targets)
944 view.block = True
945 view.activate(suffix)
946 return view
947
908 def close(self):
948 def close(self):
909 if self._closed:
949 if self._closed:
910 return
950 return
@@ -1099,8 +1139,26 b' class Client(HasTraits):'
1099 raise error
1139 raise error
1100
1140
1101 @spin_first
1141 @spin_first
1102 def shutdown(self, targets=None, restart=False, hub=False, block=None):
1142 def shutdown(self, targets='all', restart=False, hub=False, block=None):
1103 """Terminates one or more engine processes, optionally including the hub."""
1143 """Terminates one or more engine processes, optionally including the hub.
1144
1145 Parameters
1146 ----------
1147
1148 targets: list of ints or 'all' [default: all]
1149 Which engines to shutdown.
1150 hub: bool [default: False]
1151 Whether to include the Hub. hub=True implies targets='all'.
1152 block: bool [default: self.block]
1153 Whether to wait for clean shutdown replies or not.
1154 restart: bool [default: False]
1155 NOT IMPLEMENTED
1156 whether to restart engines after shutting them down.
1157 """
1158
1159 if restart:
1160 raise NotImplementedError("Engine restart is not yet implemented")
1161
1104 block = self.block if block is None else block
1162 block = self.block if block is None else block
1105 if hub:
1163 if hub:
1106 targets = 'all'
1164 targets = 'all'
@@ -60,8 +60,6 b' def sync_results(f, self, *args, **kwargs):'
60 delta = self.outstanding.difference(self.client.outstanding)
60 delta = self.outstanding.difference(self.client.outstanding)
61 completed = self.outstanding.intersection(delta)
61 completed = self.outstanding.intersection(delta)
62 self.outstanding = self.outstanding.difference(completed)
62 self.outstanding = self.outstanding.difference(completed)
63 for msg_id in completed:
64 self.results[msg_id] = self.client.results[msg_id]
65 return ret
63 return ret
66
64
67 @decorator
65 @decorator
@@ -122,6 +120,7 b' class View(HasTraits):'
122
120
123 def __init__(self, client=None, socket=None, **flags):
121 def __init__(self, client=None, socket=None, **flags):
124 super(View, self).__init__(client=client, _socket=socket)
122 super(View, self).__init__(client=client, _socket=socket)
123 self.results = client.results
125 self.block = client.block
124 self.block = client.block
126
125
127 self.set_flags(**flags)
126 self.set_flags(**flags)
@@ -792,33 +791,37 b' class DirectView(View):'
792 return self.client.kill(targets=targets, block=block)
791 return self.client.kill(targets=targets, block=block)
793
792
794 #----------------------------------------
793 #----------------------------------------
795 # activate for %px,%autopx magics
794 # activate for %px, %autopx, etc. magics
796 #----------------------------------------
795 #----------------------------------------
797 def activate(self):
798 """Make this `View` active for parallel magic commands.
799
796
800 IPython has a magic command syntax to work with `MultiEngineClient` objects.
797 def activate(self, suffix=''):
801 In a given IPython session there is a single active one. While
798 """Activate IPython magics associated with this View
802 there can be many `Views` created and used by the user,
799
803 there is only one active one. The active `View` is used whenever
800 Defines the magics `%px, %autopx, %pxresult, %%px, %pxconfig`
804 the magic commands %px and %autopx are used.
801
805
802 Parameters
806 The activate() method is called on a given `View` to make it
803 ----------
807 active. Once this has been done, the magic commands can be used.
804
805 suffix: str [default: '']
806 The suffix, if any, for the magics. This allows you to have
807 multiple views associated with parallel magics at the same time.
808
809 e.g. ``rc[::2].activate(suffix='_even')`` will give you
810 the magics ``%px_even``, ``%pxresult_even``, etc. for running magics
811 on the even engines.
808 """
812 """
809
813
814 from IPython.parallel.client.magics import ParallelMagics
815
810 try:
816 try:
811 # This is injected into __builtins__.
817 # This is injected into __builtins__.
812 ip = get_ipython()
818 ip = get_ipython()
813 except NameError:
819 except NameError:
814 print "The IPython parallel magics (%result, %px, %autopx) only work within IPython."
820 print "The IPython parallel magics (%px, etc.) only work within IPython."
815 else:
821 return
816 pmagic = ip.magics_manager.registry.get('ParallelMagics')
822
817 if pmagic is None:
823 M = ParallelMagics(ip, self, suffix)
818 ip.magic('load_ext parallelmagic')
824 ip.magics_manager.register(M)
819 pmagic = ip.magics_manager.registry.get('ParallelMagics')
820
821 pmagic.active_view = self
822
825
823
826
824 @skip_doctest
827 @skip_doctest
@@ -188,11 +188,17 b' class RemoteError(KernelError):'
188 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
188 return "<Remote[%s]:%s(%s)>"%(engineid, self.ename, self.evalue)
189
189
190 def __str__(self):
190 def __str__(self):
191 sig = "%s(%s)"%(self.ename, self.evalue)
191 return "%s(%s)" % (self.ename, self.evalue)
192 if self.traceback:
192
193 return sig + '\n' + self.traceback
193 def render_traceback(self):
194 else:
194 """render traceback to a list of lines"""
195 return sig
195 return (self.traceback or "No traceback available").splitlines()
196
197 def print_traceback(self, excid=None):
198 """print my traceback"""
199 print('\n'.join(self.render_traceback()))
200
201
196
202
197
203
198 class TaskRejectError(KernelError):
204 class TaskRejectError(KernelError):
@@ -243,21 +249,28 b' class CompositeError(RemoteError):'
243
249
244 def __repr__(self):
250 def __repr__(self):
245 return "CompositeError(%i)"%len(self.elist)
251 return "CompositeError(%i)"%len(self.elist)
246
252
247 def print_tracebacks(self, excid=None):
253 def render_traceback(self, excid=None):
254 """render one or all of my tracebacks to a list of lines"""
255 lines = []
248 if excid is None:
256 if excid is None:
249 for (en,ev,etb,ei) in self.elist:
257 for (en,ev,etb,ei) in self.elist:
250 print (self._get_engine_str(ei))
258 lines.append(self._get_engine_str(ei))
251 print (etb or 'No traceback available')
259 lines.extend((etb or 'No traceback available').splitlines())
252 print ()
260 lines.append('')
253 else:
261 else:
254 try:
262 try:
255 en,ev,etb,ei = self.elist[excid]
263 en,ev,etb,ei = self.elist[excid]
256 except:
264 except:
257 raise IndexError("an exception with index %i does not exist"%excid)
265 raise IndexError("an exception with index %i does not exist"%excid)
258 else:
266 else:
259 print (self._get_engine_str(ei))
267 lines.append(self._get_engine_str(ei))
260 print (etb or 'No traceback available')
268 lines.extend((etb or 'No traceback available').splitlines())
269
270 return lines
271
272 def print_traceback(self, excid=None):
273 print('\n'.join(self.render_traceback(excid)))
261
274
262 def raise_exception(self, excid=0):
275 def raise_exception(self, excid=0):
263 try:
276 try:
@@ -24,6 +24,7 b' from tempfile import mktemp'
24
24
25 import zmq
25 import zmq
26
26
27 from IPython import parallel
27 from IPython.parallel.client import client as clientmod
28 from IPython.parallel.client import client as clientmod
28 from IPython.parallel import error
29 from IPython.parallel import error
29 from IPython.parallel import AsyncResult, AsyncHubResult
30 from IPython.parallel import AsyncResult, AsyncHubResult
@@ -284,16 +285,16 b' class TestClient(ClusterTestCase):'
284 """wait for an engine to become idle, according to the Hub"""
285 """wait for an engine to become idle, according to the Hub"""
285 rc = self.client
286 rc = self.client
286
287
287 # timeout 2s, polling every 100ms
288 # timeout 5s, polling every 100ms
288 for i in range(20):
289 qs = rc.queue_status()
289 qs = rc.queue_status()
290 for i in range(50):
290 if qs['unassigned'] or any(qs[eid]['tasks'] for eid in rc.ids):
291 if qs['unassigned'] or any(qs[eid]['tasks'] for eid in rc.ids):
291 time.sleep(0.1)
292 time.sleep(0.1)
293 qs = rc.queue_status()
292 else:
294 else:
293 break
295 break
294
296
295 # ensure Hub up to date:
297 # ensure Hub up to date:
296 qs = rc.queue_status()
297 self.assertEquals(qs['unassigned'], 0)
298 self.assertEquals(qs['unassigned'], 0)
298 for eid in rc.ids:
299 for eid in rc.ids:
299 self.assertEquals(qs[eid]['tasks'], 0)
300 self.assertEquals(qs[eid]['tasks'], 0)
@@ -420,3 +421,16 b' class TestClient(ClusterTestCase):'
420 "Shouldn't be spinning, but got wall_time=%f" % ar.wall_time
421 "Shouldn't be spinning, but got wall_time=%f" % ar.wall_time
421 )
422 )
422
423
424 def test_activate(self):
425 ip = get_ipython()
426 magics = ip.magics_manager.magics
427 self.assertTrue('px' in magics['line'])
428 self.assertTrue('px' in magics['cell'])
429 v0 = self.client.activate(-1, '0')
430 self.assertTrue('px0' in magics['line'])
431 self.assertTrue('px0' in magics['cell'])
432 self.assertEquals(v0.targets, self.client.ids[-1])
433 v0 = self.client.activate('all', 'all')
434 self.assertTrue('pxall' in magics['line'])
435 self.assertTrue('pxall' in magics['cell'])
436 self.assertEquals(v0.targets, 'all')
@@ -88,7 +88,7 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
88
88
89 for block in (True, False):
89 for block in (True, False):
90 v.block = block
90 v.block = block
91
91 ip.magic("pxconfig --verbose")
92 with capture_output() as io:
92 with capture_output() as io:
93 ip.run_cell_magic("px", "", "1")
93 ip.run_cell_magic("px", "", "1")
94 if block:
94 if block:
@@ -117,7 +117,7 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
117 ip.run_cell_magic('px', '--group-outputs=engine', 'generate_output()')
117 ip.run_cell_magic('px', '--group-outputs=engine', 'generate_output()')
118
118
119 self.assertFalse('\n\n' in io.stdout)
119 self.assertFalse('\n\n' in io.stdout)
120 lines = io.stdout.splitlines()[1:]
120 lines = io.stdout.splitlines()
121 expected = [
121 expected = [
122 r'\[stdout:\d+\]',
122 r'\[stdout:\d+\]',
123 'stdout',
123 'stdout',
@@ -151,7 +151,7 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
151 ip.run_cell_magic('px', '--group-outputs=order', 'generate_output()')
151 ip.run_cell_magic('px', '--group-outputs=order', 'generate_output()')
152
152
153 self.assertFalse('\n\n' in io.stdout)
153 self.assertFalse('\n\n' in io.stdout)
154 lines = io.stdout.splitlines()[1:]
154 lines = io.stdout.splitlines()
155 expected = []
155 expected = []
156 expected.extend([
156 expected.extend([
157 r'\[stdout:\d+\]',
157 r'\[stdout:\d+\]',
@@ -192,7 +192,7 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
192 ip.run_cell_magic('px', '--group-outputs=type', 'generate_output()')
192 ip.run_cell_magic('px', '--group-outputs=type', 'generate_output()')
193
193
194 self.assertFalse('\n\n' in io.stdout)
194 self.assertFalse('\n\n' in io.stdout)
195 lines = io.stdout.splitlines()[1:]
195 lines = io.stdout.splitlines()
196
196
197 expected = []
197 expected = []
198 expected.extend([
198 expected.extend([
@@ -229,6 +229,7 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
229 self.assertEquals(v['a'], [5])
229 self.assertEquals(v['a'], [5])
230 ip.magic('px a=10')
230 ip.magic('px a=10')
231 self.assertEquals(v['a'], [10])
231 self.assertEquals(v['a'], [10])
232 ip.magic('pxconfig --verbose')
232 with capture_output() as io:
233 with capture_output() as io:
233 ar = ip.magic('px print (a)')
234 ar = ip.magic('px print (a)')
234 self.assertTrue(isinstance(ar, AsyncResult))
235 self.assertTrue(isinstance(ar, AsyncResult))
@@ -258,7 +259,7 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
258
259
259 self.assertTrue(output.startswith('%autopx enabled'), output)
260 self.assertTrue(output.startswith('%autopx enabled'), output)
260 self.assertTrue(output.rstrip().endswith('%autopx disabled'), output)
261 self.assertTrue(output.rstrip().endswith('%autopx disabled'), output)
261 self.assertTrue('RemoteError: ZeroDivisionError' in output, output)
262 self.assertTrue('ZeroDivisionError' in output, output)
262 self.assertTrue('\nOut[' in output, output)
263 self.assertTrue('\nOut[' in output, output)
263 self.assertTrue(': 24690' in output, output)
264 self.assertTrue(': 24690' in output, output)
264 ar = v.get_result(-1)
265 ar = v.get_result(-1)
@@ -301,20 +302,13 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
301 data = dict(a=111,b=222)
302 data = dict(a=111,b=222)
302 v.push(data, block=True)
303 v.push(data, block=True)
303
304
304 ip.magic('px a')
305 for name in ('a', 'b'):
305 ip.magic('px b')
306 ip.magic('px ' + name)
306 for idx, name in [
307 ('', 'b'),
308 ('-1', 'b'),
309 ('2', 'b'),
310 ('1', 'a'),
311 ('-2', 'a'),
312 ]:
313 with capture_output() as io:
307 with capture_output() as io:
314 ip.magic('result ' + idx)
308 ip.magic('pxresult')
315 output = io.stdout
309 output = io.stdout
316 msg = "expected %s output to include %s, but got: %s" % \
310 msg = "expected %s output to include %s, but got: %s" % \
317 ('%result '+idx, str(data[name]), output)
311 ('%pxresult', str(data[name]), output)
318 self.assertTrue(str(data[name]) in output, msg)
312 self.assertTrue(str(data[name]) in output, msg)
319
313
320 @dec.skipif_not_matplotlib
314 @dec.skipif_not_matplotlib
@@ -336,5 +330,57 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):'
336
330
337 self.assertTrue('Out[' in io.stdout, io.stdout)
331 self.assertTrue('Out[' in io.stdout, io.stdout)
338 self.assertTrue('matplotlib.lines' in io.stdout, io.stdout)
332 self.assertTrue('matplotlib.lines' in io.stdout, io.stdout)
339
333
334 def test_pxconfig(self):
335 ip = get_ipython()
336 rc = self.client
337 v = rc.activate(-1, '_tst')
338 self.assertEquals(v.targets, rc.ids[-1])
339 ip.magic("%pxconfig_tst -t :")
340 self.assertEquals(v.targets, rc.ids)
341 ip.magic("%pxconfig_tst -t ::2")
342 self.assertEquals(v.targets, rc.ids[::2])
343 ip.magic("%pxconfig_tst -t 1::2")
344 self.assertEquals(v.targets, rc.ids[1::2])
345 ip.magic("%pxconfig_tst -t 1")
346 self.assertEquals(v.targets, 1)
347 ip.magic("%pxconfig_tst --block")
348 self.assertEquals(v.block, True)
349 ip.magic("%pxconfig_tst --noblock")
350 self.assertEquals(v.block, False)
351
352 def test_cellpx_targets(self):
353 """%%px --targets doesn't change defaults"""
354 ip = get_ipython()
355 rc = self.client
356 view = rc.activate(rc.ids)
357 self.assertEquals(view.targets, rc.ids)
358 ip.magic('pxconfig --verbose')
359 for cell in ("pass", "1/0"):
360 with capture_output() as io:
361 try:
362 ip.run_cell_magic("px", "--targets all", cell)
363 except pmod.RemoteError:
364 pass
365 self.assertTrue('engine(s): all' in io.stdout)
366 self.assertEquals(view.targets, rc.ids)
367
368
369 def test_cellpx_block(self):
370 """%%px --block doesn't change default"""
371 ip = get_ipython()
372 rc = self.client
373 view = rc.activate(rc.ids)
374 view.block = False
375 self.assertEquals(view.targets, rc.ids)
376 ip.magic('pxconfig --verbose')
377 for cell in ("pass", "1/0"):
378 with capture_output() as io:
379 try:
380 ip.run_cell_magic("px", "--block", cell)
381 except pmod.RemoteError:
382 pass
383 self.assertFalse('Async' in io.stdout)
384 self.assertFalse(view.block)
385
340
386
@@ -469,7 +469,6 b' class TestView(ClusterTestCase, ParametricTestCase):'
469 e0.block = True
469 e0.block = True
470 ar = e0.execute("5", silent=False)
470 ar = e0.execute("5", silent=False)
471 er = ar.get()
471 er = ar.get()
472 self._wait_for(lambda : bool(er.pyout))
473 self.assertEquals(str(er), "<ExecuteReply[%i]: 5>" % er.execution_count)
472 self.assertEquals(str(er), "<ExecuteReply[%i]: 5>" % er.execution_count)
474 self.assertEquals(er.pyout['data']['text/plain'], '5')
473 self.assertEquals(er.pyout['data']['text/plain'], '5')
475
474
@@ -478,14 +477,12 b' class TestView(ClusterTestCase, ParametricTestCase):'
478 e0.block = True
477 e0.block = True
479 ar = e0.execute("print (5)", silent=False)
478 ar = e0.execute("print (5)", silent=False)
480 er = ar.get()
479 er = ar.get()
481 self._wait_for(lambda : bool(er.stdout))
482 self.assertEquals(er.stdout.strip(), '5')
480 self.assertEquals(er.stdout.strip(), '5')
483
481
484 def test_execute_pyout(self):
482 def test_execute_pyout(self):
485 """execute triggers pyout with silent=False"""
483 """execute triggers pyout with silent=False"""
486 view = self.client[:]
484 view = self.client[:]
487 ar = view.execute("5", silent=False, block=True)
485 ar = view.execute("5", silent=False, block=True)
488 self._wait_for(lambda : all(ar.pyout))
489
486
490 expected = [{'text/plain' : '5'}] * len(view)
487 expected = [{'text/plain' : '5'}] * len(view)
491 mimes = [ out['data'] for out in ar.pyout ]
488 mimes = [ out['data'] for out in ar.pyout ]
@@ -505,7 +502,6 b' class TestView(ClusterTestCase, ParametricTestCase):'
505 ar = view.execute("%whos", block=True)
502 ar = view.execute("%whos", block=True)
506 # this will raise, if that failed
503 # this will raise, if that failed
507 ar.get(5)
504 ar.get(5)
508 self._wait_for(lambda : all(ar.stdout))
509 for stdout in ar.stdout:
505 for stdout in ar.stdout:
510 lines = stdout.splitlines()
506 lines = stdout.splitlines()
511 self.assertEquals(lines[0].split(), ['Variable', 'Type', 'Data/Info'])
507 self.assertEquals(lines[0].split(), ['Variable', 'Type', 'Data/Info'])
@@ -523,7 +519,6 b' class TestView(ClusterTestCase, ParametricTestCase):'
523 view.execute("from IPython.core.display import *")
519 view.execute("from IPython.core.display import *")
524 ar = view.execute("[ display(i) for i in range(5) ]", block=True)
520 ar = view.execute("[ display(i) for i in range(5) ]", block=True)
525
521
526 self._wait_for(lambda : all(len(er.outputs) >= 5 for er in ar))
527 expected = [ {u'text/plain' : unicode(j)} for j in range(5) ]
522 expected = [ {u'text/plain' : unicode(j)} for j in range(5) ]
528 for outputs in ar.outputs:
523 for outputs in ar.outputs:
529 mimes = [ out['data'] for out in outputs ]
524 mimes = [ out['data'] for out in outputs ]
@@ -540,7 +535,6 b' class TestView(ClusterTestCase, ParametricTestCase):'
540
535
541 ar = view.apply_async(publish)
536 ar = view.apply_async(publish)
542 ar.get(5)
537 ar.get(5)
543 self._wait_for(lambda : all(len(out) >= 5 for out in ar.outputs))
544 expected = [ {u'text/plain' : unicode(j)} for j in range(5) ]
538 expected = [ {u'text/plain' : unicode(j)} for j in range(5) ]
545 for outputs in ar.outputs:
539 for outputs in ar.outputs:
546 mimes = [ out['data'] for out in outputs ]
540 mimes = [ out['data'] for out in outputs ]
@@ -562,7 +556,6 b' class TestView(ClusterTestCase, ParametricTestCase):'
562 # include imports, in case user config
556 # include imports, in case user config
563 ar = view.execute("plot(rand(100))", silent=False)
557 ar = view.execute("plot(rand(100))", silent=False)
564 reply = ar.get(5)
558 reply = ar.get(5)
565 self._wait_for(lambda : all(ar.outputs))
566 self.assertEquals(len(reply.outputs), 1)
559 self.assertEquals(len(reply.outputs), 1)
567 output = reply.outputs[0]
560 output = reply.outputs[0]
568 self.assertTrue("data" in output)
561 self.assertTrue("data" in output)
@@ -308,16 +308,22 b' class Kernel(Configurable):'
308 {u'code':code, u'execution_count': execution_count},
308 {u'code':code, u'execution_count': execution_count},
309 parent=parent, ident=self._topic('pyin')
309 parent=parent, ident=self._topic('pyin')
310 )
310 )
311
311
312 def execute_request(self, stream, ident, parent):
312 def _publish_status(self, status, parent=None):
313
313 """send status (busy/idle) on IOPub"""
314 self.session.send(self.iopub_socket,
314 self.session.send(self.iopub_socket,
315 u'status',
315 u'status',
316 {u'execution_state':u'busy'},
316 {u'execution_state': status},
317 parent=parent,
317 parent=parent,
318 ident=self._topic('status'),
318 ident=self._topic('status'),
319 )
319 )
320
320
321
322 def execute_request(self, stream, ident, parent):
323 """handle an execute_request"""
324
325 self._publish_status(u'busy', parent)
326
321 try:
327 try:
322 content = parent[u'content']
328 content = parent[u'content']
323 code = content[u'code']
329 code = content[u'code']
@@ -433,11 +439,7 b' class Kernel(Configurable):'
433 if not silent and reply_msg['content']['status'] == u'error':
439 if not silent and reply_msg['content']['status'] == u'error':
434 self._abort_queues()
440 self._abort_queues()
435
441
436 self.session.send(self.iopub_socket,
442 self._publish_status(u'idle', parent)
437 u'status',
438 {u'execution_state':u'idle'},
439 parent=parent,
440 ident=self._topic('status'))
441
443
442 def complete_request(self, stream, ident, parent):
444 def complete_request(self, stream, ident, parent):
443 txt, matches = self._complete(parent)
445 txt, matches = self._complete(parent)
@@ -529,9 +531,12 b' class Kernel(Configurable):'
529 self.log.error("Got bad msg: %s", parent, exc_info=True)
531 self.log.error("Got bad msg: %s", parent, exc_info=True)
530 return
532 return
531
533
534 self._publish_status(u'busy', parent)
535
532 # Set the parent message of the display hook and out streams.
536 # Set the parent message of the display hook and out streams.
533 self.shell.displayhook.set_parent(parent)
537 shell = self.shell
534 self.shell.display_pub.set_parent(parent)
538 shell.displayhook.set_parent(parent)
539 shell.display_pub.set_parent(parent)
535 sys.stdout.set_parent(parent)
540 sys.stdout.set_parent(parent)
536 sys.stderr.set_parent(parent)
541 sys.stderr.set_parent(parent)
537
542
@@ -540,7 +545,7 b' class Kernel(Configurable):'
540 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
545 # self.session.send(self.iopub_socket, u'pyin', {u'code':code},parent=parent)
541 sub = self._make_subheader()
546 sub = self._make_subheader()
542 try:
547 try:
543 working = self.shell.user_ns
548 working = shell.user_ns
544
549
545 prefix = "_"+str(msg_id).replace("-","")+"_"
550 prefix = "_"+str(msg_id).replace("-","")+"_"
546
551
@@ -558,7 +563,7 b' class Kernel(Configurable):'
558 working.update(ns)
563 working.update(ns)
559 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
564 code = "%s = %s(*%s,**%s)" % (resultname, fname, argname, kwargname)
560 try:
565 try:
561 exec code in self.shell.user_global_ns, self.shell.user_ns
566 exec code in shell.user_global_ns, shell.user_ns
562 result = working.get(resultname)
567 result = working.get(resultname)
563 finally:
568 finally:
564 for key in ns.iterkeys():
569 for key in ns.iterkeys():
@@ -567,14 +572,23 b' class Kernel(Configurable):'
567 packed_result,buf = serialize_object(result)
572 packed_result,buf = serialize_object(result)
568 result_buf = [packed_result]+buf
573 result_buf = [packed_result]+buf
569 except:
574 except:
570 exc_content = self._wrap_exception('apply')
575 # invoke IPython traceback formatting
571 # exc_msg = self.session.msg(u'pyerr', exc_content, parent)
576 shell.showtraceback()
572 self.session.send(self.iopub_socket, u'pyerr', exc_content, parent=parent,
577 # FIXME - fish exception info out of shell, possibly left there by
578 # run_code. We'll need to clean up this logic later.
579 reply_content = {}
580 if shell._reply_content is not None:
581 reply_content.update(shell._reply_content)
582 e_info = dict(engine_uuid=self.ident, engine_id=self.int_id, method='apply')
583 reply_content['engine_info'] = e_info
584 # reset after use
585 shell._reply_content = None
586
587 self.session.send(self.iopub_socket, u'pyerr', reply_content, parent=parent,
573 ident=self._topic('pyerr'))
588 ident=self._topic('pyerr'))
574 reply_content = exc_content
575 result_buf = []
589 result_buf = []
576
590
577 if exc_content['ename'] == 'UnmetDependency':
591 if reply_content['ename'] == 'UnmetDependency':
578 sub['dependencies_met'] = False
592 sub['dependencies_met'] = False
579 else:
593 else:
580 reply_content = {'status' : 'ok'}
594 reply_content = {'status' : 'ok'}
@@ -589,6 +603,8 b' class Kernel(Configurable):'
589 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
603 reply_msg = self.session.send(stream, u'apply_reply', reply_content,
590 parent=parent, ident=ident,buffers=result_buf, subheader=sub)
604 parent=parent, ident=ident,buffers=result_buf, subheader=sub)
591
605
606 self._publish_status(u'idle', parent)
607
592 #---------------------------------------------------------------------------
608 #---------------------------------------------------------------------------
593 # Control messages
609 # Control messages
594 #---------------------------------------------------------------------------
610 #---------------------------------------------------------------------------
@@ -16,21 +16,19 b''
16 {
16 {
17 "cell_type": "markdown",
17 "cell_type": "markdown",
18 "source": [
18 "source": [
19 "IPython has a few magics for working with your engines.",
19 "IPython has a few magics for working with your engines.\n",
20 "",
20 "\n",
21 "This assumes you have started an IPython cluster, either with the notebook interface,",
21 "This assumes you have started an IPython cluster, either with the notebook interface,\n",
22 "or the `ipcluster/controller/engine` commands."
22 "or the `ipcluster/controller/engine` commands."
23 ]
23 ]
24 },
24 },
25 {
25 {
26 "cell_type": "code",
26 "cell_type": "code",
27 "collapsed": false,
28 "input": [
27 "input": [
29 "from IPython import parallel",
28 "from IPython import parallel\n",
30 "rc = parallel.Client()",
29 "rc = parallel.Client()\n",
31 "dv = rc[:]",
30 "dv = rc[:]\n",
32 "dv.block = True",
31 "rc.ids"
33 "dv"
34 ],
32 ],
35 "language": "python",
33 "language": "python",
36 "outputs": []
34 "outputs": []
@@ -38,21 +36,11 b''
38 {
36 {
39 "cell_type": "markdown",
37 "cell_type": "markdown",
40 "source": [
38 "source": [
41 "The parallel magics come from the `parallelmagics` IPython extension.",
39 "Creating a Client registers the parallel magics `%px`, `%%px`, `%pxresult`, `pxconfig`, and `%autopx`. \n",
42 "The magics are set to work with a particular View object,",
40 "These magics are initially associated with a DirectView always associated with all currently registered engines."
43 "so to activate them, you call the `activate()` method on a particular view:"
44 ]
41 ]
45 },
42 },
46 {
43 {
47 "cell_type": "code",
48 "collapsed": true,
49 "input": [
50 "dv.activate()"
51 ],
52 "language": "python",
53 "outputs": []
54 },
55 {
56 "cell_type": "markdown",
44 "cell_type": "markdown",
57 "source": [
45 "source": [
58 "Now we can execute code remotely with `%px`:"
46 "Now we can execute code remotely with `%px`:"
@@ -60,7 +48,6 b''
60 },
48 },
61 {
49 {
62 "cell_type": "code",
50 "cell_type": "code",
63 "collapsed": false,
64 "input": [
51 "input": [
65 "%px a=5"
52 "%px a=5"
66 ],
53 ],
@@ -69,7 +56,6 b''
69 },
56 },
70 {
57 {
71 "cell_type": "code",
58 "cell_type": "code",
72 "collapsed": false,
73 "input": [
59 "input": [
74 "%px print a"
60 "%px print a"
75 ],
61 ],
@@ -78,7 +64,6 b''
78 },
64 },
79 {
65 {
80 "cell_type": "code",
66 "cell_type": "code",
81 "collapsed": false,
82 "input": [
67 "input": [
83 "%px a"
68 "%px a"
84 ],
69 ],
@@ -87,9 +72,8 b''
87 },
72 },
88 {
73 {
89 "cell_type": "code",
74 "cell_type": "code",
90 "collapsed": false,
91 "input": [
75 "input": [
92 "with dv.sync_imports():",
76 "with dv.sync_imports():\n",
93 " import sys"
77 " import sys"
94 ],
78 ],
95 "language": "python",
79 "language": "python",
@@ -97,7 +81,6 b''
97 },
81 },
98 {
82 {
99 "cell_type": "code",
83 "cell_type": "code",
100 "collapsed": false,
101 "input": [
84 "input": [
102 "%px print >> sys.stderr, \"ERROR\""
85 "%px print >> sys.stderr, \"ERROR\""
103 ],
86 ],
@@ -107,24 +90,22 b''
107 {
90 {
108 "cell_type": "markdown",
91 "cell_type": "markdown",
109 "source": [
92 "source": [
110 "You don't have to wait for results:"
93 "You don't have to wait for results. The `%pxconfig` magic lets you change the default blocking/targets for the `%px` magics:"
111 ]
94 ]
112 },
95 },
113 {
96 {
114 "cell_type": "code",
97 "cell_type": "code",
115 "collapsed": true,
116 "input": [
98 "input": [
117 "dv.block = False"
99 "%pxconfig --noblock"
118 ],
100 ],
119 "language": "python",
101 "language": "python",
120 "outputs": []
102 "outputs": []
121 },
103 },
122 {
104 {
123 "cell_type": "code",
105 "cell_type": "code",
124 "collapsed": false,
125 "input": [
106 "input": [
126 "%px import time",
107 "%px import time\n",
127 "%px time.sleep(5)",
108 "%px time.sleep(5)\n",
128 "%px time.time()"
109 "%px time.time()"
129 ],
110 ],
130 "language": "python",
111 "language": "python",
@@ -133,15 +114,14 b''
133 {
114 {
134 "cell_type": "markdown",
115 "cell_type": "markdown",
135 "source": [
116 "source": [
136 "But you will notice that this didn't output the result of the last command.",
117 "But you will notice that this didn't output the result of the last command.\n",
137 "For this, we have `%result`, which displays the output of the latest request:"
118 "For this, we have `%pxresult`, which displays the output of the latest request:"
138 ]
119 ]
139 },
120 },
140 {
121 {
141 "cell_type": "code",
122 "cell_type": "code",
142 "collapsed": false,
143 "input": [
123 "input": [
144 "%result"
124 "%pxresult"
145 ],
125 ],
146 "language": "python",
126 "language": "python",
147 "outputs": []
127 "outputs": []
@@ -154,9 +134,8 b''
154 },
134 },
155 {
135 {
156 "cell_type": "code",
136 "cell_type": "code",
157 "collapsed": false,
158 "input": [
137 "input": [
159 "dv.block = True",
138 "%pxconfig --block\n",
160 "%px %pylab inline"
139 "%px %pylab inline"
161 ],
140 ],
162 "language": "python",
141 "language": "python",
@@ -165,17 +144,15 b''
165 {
144 {
166 "cell_type": "markdown",
145 "cell_type": "markdown",
167 "source": [
146 "source": [
168 "`%%px` can also be used as a cell magic, for submitting whole blocks.",
147 "`%%px` can also be used as a cell magic, for submitting whole blocks.\n",
169 "This one acceps `--block` and `--noblock` flags to specify",
148 "This one acceps `--block` and `--noblock` flags to specify\n",
170 "the blocking behavior, though the default is unchanged.",
149 "the blocking behavior, though the default is unchanged.\n"
171 ""
172 ]
150 ]
173 },
151 },
174 {
152 {
175 "cell_type": "code",
153 "cell_type": "code",
176 "collapsed": true,
177 "input": [
154 "input": [
178 "dv.scatter('id', dv.targets, flatten=True)",
155 "dv.scatter('id', dv.targets, flatten=True)\n",
179 "dv['stride'] = len(dv)"
156 "dv['stride'] = len(dv)"
180 ],
157 ],
181 "language": "python",
158 "language": "python",
@@ -183,13 +160,12 b''
183 },
160 },
184 {
161 {
185 "cell_type": "code",
162 "cell_type": "code",
186 "collapsed": false,
187 "input": [
163 "input": [
188 "%%px --noblock",
164 "%%px --noblock\n",
189 "x = linspace(0,pi,1000)",
165 "x = linspace(0,pi,1000)\n",
190 "for n in range(id,12, stride):",
166 "for n in range(id,12, stride):\n",
191 " print n",
167 " print n\n",
192 " plt.plot(x,sin(n*x))",
168 " plt.plot(x,sin(n*x))\n",
193 "plt.title(\"Plot %i\" % id)"
169 "plt.title(\"Plot %i\" % id)"
194 ],
170 ],
195 "language": "python",
171 "language": "python",
@@ -197,9 +173,8 b''
197 },
173 },
198 {
174 {
199 "cell_type": "code",
175 "cell_type": "code",
200 "collapsed": false,
201 "input": [
176 "input": [
202 "%result"
177 "%pxresult"
203 ],
178 ],
204 "language": "python",
179 "language": "python",
205 "outputs": []
180 "outputs": []
@@ -207,28 +182,27 b''
207 {
182 {
208 "cell_type": "markdown",
183 "cell_type": "markdown",
209 "source": [
184 "source": [
210 "It also lets you choose some amount of the grouping of the outputs with `--group-outputs`:",
185 "It also lets you choose some amount of the grouping of the outputs with `--group-outputs`:\n",
211 "",
186 "\n",
212 "The choices are:",
187 "The choices are:\n",
213 "",
188 "\n",
214 "* `engine` - all of an engine's output is collected together",
189 "* `engine` - all of an engine's output is collected together\n",
215 "* `type` - where stdout of each engine is grouped, etc. (the default)",
190 "* `type` - where stdout of each engine is grouped, etc. (the default)\n",
216 "* `order` - same as `type`, but individual displaypub outputs are interleaved.",
191 "* `order` - same as `type`, but individual displaypub outputs are interleaved.\n",
217 " That is, it will output the first plot from each engine, then the second from each,",
192 " That is, it will output the first plot from each engine, then the second from each,\n",
218 " etc."
193 " etc."
219 ]
194 ]
220 },
195 },
221 {
196 {
222 "cell_type": "code",
197 "cell_type": "code",
223 "collapsed": false,
224 "input": [
198 "input": [
225 "%%px --group-outputs=engine",
199 "%%px --group-outputs=engine\n",
226 "x = linspace(0,pi,1000)",
200 "x = linspace(0,pi,1000)\n",
227 "for n in range(id,12, stride):",
201 "for n in range(id+1,12, stride):\n",
228 " print n",
202 " print n\n",
229 " plt.figure()",
203 " plt.figure()\n",
230 " plt.plot(x,sin(n*x))",
204 " plt.plot(x,sin(n*x))\n",
231 "plt.title(\"Plot %i\" % id)"
205 " plt.title(\"Plot %i\" % n)"
232 ],
206 ],
233 "language": "python",
207 "language": "python",
234 "outputs": []
208 "outputs": []
@@ -236,20 +210,16 b''
236 {
210 {
237 "cell_type": "markdown",
211 "cell_type": "markdown",
238 "source": [
212 "source": [
239 "When you specify 'order', then individual display outputs (e.g. plots) will be interleaved:"
213 "When you specify 'order', then individual display outputs (e.g. plots) will be interleaved.\n",
214 "\n",
215 "`%pxresult` takes the same output-ordering arguments as `%%px`, \n",
216 "so you can view the previous result in a variety of different ways with a few sequential calls to `%pxresult`:"
240 ]
217 ]
241 },
218 },
242 {
219 {
243 "cell_type": "code",
220 "cell_type": "code",
244 "collapsed": false,
245 "input": [
221 "input": [
246 "%%px --group-outputs=order",
222 "%pxresult --group-outputs=order"
247 "x = linspace(0,pi,1000)",
248 "for n in range(id,12, stride):",
249 " print n",
250 " plt.figure()",
251 " plt.plot(x,sin(n*x))",
252 "plt.title(\"Plot %i\" % id)"
253 ],
223 ],
254 "language": "python",
224 "language": "python",
255 "outputs": []
225 "outputs": []
@@ -269,53 +239,144 b''
269 },
239 },
270 {
240 {
271 "cell_type": "code",
241 "cell_type": "code",
272 "collapsed": true,
273 "input": [
242 "input": [
274 "def generate_output():",
243 "def generate_output():\n",
275 " \"\"\"function for testing output",
244 " \"\"\"function for testing output\n",
276 " ",
245 " \n",
277 " publishes two outputs of each type, and returns something",
246 " publishes two outputs of each type, and returns something\n",
278 " \"\"\"",
247 " \"\"\"\n",
279 " ",
248 " \n",
280 " import sys,os",
249 " import sys,os\n",
281 " from IPython.core.display import display, HTML, Math",
250 " from IPython.core.display import display, HTML, Math\n",
282 " ",
251 " \n",
283 " print \"stdout\"",
252 " print \"stdout\"\n",
284 " print >> sys.stderr, \"stderr\"",
253 " print >> sys.stderr, \"stderr\"\n",
285 " ",
254 " \n",
286 " display(HTML(\"<b>HTML</b>\"))",
255 " display(HTML(\"<b>HTML</b>\"))\n",
287 " ",
256 " \n",
288 " print \"stdout2\"",
257 " print \"stdout2\"\n",
289 " print >> sys.stderr, \"stderr2\"",
258 " print >> sys.stderr, \"stderr2\"\n",
290 " ",
259 " \n",
291 " display(Math(r\"\\alpha=\\beta\"))",
260 " display(Math(r\"\\alpha=\\beta\"))\n",
292 " ",
261 " \n",
293 " return os.getpid()",
262 " return os.getpid()\n",
294 "",
263 "\n",
295 "dv['generate_output'] = generate_output"
264 "dv['generate_output'] = generate_output"
296 ],
265 ],
297 "language": "python",
266 "language": "python",
298 "outputs": []
267 "outputs": []
299 },
268 },
300 {
269 {
270 "cell_type": "markdown",
271 "source": [
272 "You can also have more than one set of parallel magics registered at a time.\n",
273 "\n",
274 "The `View.activate()` method takes a suffix argument, which is added to `'px'`."
275 ]
276 },
277 {
278 "cell_type": "code",
279 "input": [
280 "e0 = rc[-1]\n",
281 "e0.block = True\n",
282 "e0.activate('0')"
283 ],
284 "language": "python",
285 "outputs": []
286 },
287 {
301 "cell_type": "code",
288 "cell_type": "code",
302 "collapsed": true,
303 "input": [
289 "input": [
304 "e0 = rc[-1]",
290 "%px0 generate_output()"
305 "e0.block = True",
306 "e0.activate()"
307 ],
291 ],
308 "language": "python",
292 "language": "python",
309 "outputs": []
293 "outputs": []
310 },
294 },
311 {
295 {
312 "cell_type": "code",
296 "cell_type": "code",
313 "collapsed": false,
314 "input": [
297 "input": [
315 "%px generate_output()"
298 "%px generate_output()"
316 ],
299 ],
317 "language": "python",
300 "language": "python",
318 "outputs": []
301 "outputs": []
302 },
303 {
304 "cell_type": "markdown",
305 "source": [
306 "As mentioned above, we can redisplay those same results with various grouping:"
307 ]
308 },
309 {
310 "cell_type": "code",
311 "input": [
312 "%pxresult --group-outputs order"
313 ],
314 "language": "python",
315 "outputs": []
316 },
317 {
318 "cell_type": "code",
319 "input": [
320 "%pxresult --group-outputs engine"
321 ],
322 "language": "python",
323 "outputs": []
324 },
325 {
326 "cell_type": "heading",
327 "level": 2,
328 "source": [
329 "Parallel Exceptions"
330 ]
331 },
332 {
333 "cell_type": "markdown",
334 "source": [
335 "When you raise exceptions with the parallel exception,\n",
336 "the CompositeError raised locally will display your remote traceback."
337 ]
338 },
339 {
340 "cell_type": "code",
341 "input": [
342 "%%px\n",
343 "from numpy.random import random\n",
344 "A = random((100,100,'invalid shape'))"
345 ],
346 "language": "python",
347 "outputs": []
348 },
349 {
350 "cell_type": "heading",
351 "level": 2,
352 "source": [
353 "Remote Cell Magics"
354 ]
355 },
356 {
357 "cell_type": "markdown",
358 "source": [
359 "Remember, Engines are IPython too, so the cell that is run remotely by %%px can in turn use a cell magic."
360 ]
361 },
362 {
363 "cell_type": "code",
364 "input": [
365 "%%px\n",
366 "%%timeit\n",
367 "from numpy.random import random\n",
368 "from numpy.linalg import norm\n",
369 "A = random((100,100))\n",
370 "norm(A, 2) "
371 ],
372 "language": "python",
373 "outputs": []
374 },
375 {
376 "cell_type": "code",
377 "input": [],
378 "language": "python",
379 "outputs": []
319 }
380 }
320 ]
381 ]
321 }
382 }
@@ -72,7 +72,6 b' Extensions bundled with IPython'
72
72
73 autoreload
73 autoreload
74 cythonmagic
74 cythonmagic
75 parallelmagic
76 rmagic
75 rmagic
77 storemagic
76 storemagic
78 sympyprinting
77 sympyprinting
@@ -101,11 +101,45 b' Map results are iterable!'
101 =========================
101 =========================
102
102
103 When an AsyncResult object has multiple results (e.g. the :class:`~AsyncMapResult`
103 When an AsyncResult object has multiple results (e.g. the :class:`~AsyncMapResult`
104 object), you can actually iterate through them, and act on the results as they arrive:
104 object), you can actually iterate through results themselves, and act on them as they arrive:
105
105
106 .. literalinclude:: ../../examples/parallel/itermapresult.py
106 .. literalinclude:: ../../examples/parallel/itermapresult.py
107 :language: python
107 :language: python
108 :lines: 20-67
108 :lines: 20-67
109
110 That is to say, if you treat an AsyncMapResult as if it were a list of your actual
111 results, it should behave as you would expect, with the only difference being
112 that you can start iterating through the results before they have even been computed.
113
114 This lets you do a dumb version of map/reduce with the builtin Python functions,
115 and the only difference between doing this locally and doing it remotely in parallel
116 is using the asynchronous view.map instead of the builtin map.
117
118
119 Here is a simple one-line RMS (root-mean-square) implemented with Python's builtin map/reduce.
120
121 .. sourcecode:: ipython
122
123 In [38]: X = np.linspace(0,100)
124
125 In [39]: from math import sqrt
126
127 In [40]: add = lambda a,b: a+b
128
129 In [41]: sq = lambda x: x*x
130
131 In [42]: sqrt(reduce(add, map(sq, X)) / len(X))
132 Out[42]: 58.028845747399714
133
134 In [43]: sqrt(reduce(add, view.map(sq, X)) / len(X))
135 Out[43]: 58.028845747399714
136
137 To break that down:
138
139 1. ``map(sq, X)`` Compute the square of each element in the list (locally, or in parallel)
140 2. ``reduce(add, sqX) / len(X)`` compute the mean by summing over the list (or AsyncMapResult)
141 and dividing by the size
142 3. take the square root of the resulting number
109
143
110 .. seealso::
144 .. seealso::
111
145
@@ -10,6 +10,7 b' Using IPython for parallel computing'
10 parallel_intro.txt
10 parallel_intro.txt
11 parallel_process.txt
11 parallel_process.txt
12 parallel_multiengine.txt
12 parallel_multiengine.txt
13 magics.txt
13 parallel_task.txt
14 parallel_task.txt
14 asyncresult.txt
15 asyncresult.txt
15 parallel_mpi.txt
16 parallel_mpi.txt
@@ -34,19 +34,13 b' The following will fail:'
34 In [3]: A = numpy.zeros(2)
34 In [3]: A = numpy.zeros(2)
35
35
36 In [4]: def setter(a):
36 In [4]: def setter(a):
37 ...: a[0]=1
37 ...: a[0]=1
38 ...: return a
38 ...: return a
39
39
40 In [5]: rc[0].apply_sync(setter, A)
40 In [5]: rc[0].apply_sync(setter, A)
41 ---------------------------------------------------------------------------
41 ---------------------------------------------------------------------------
42 RemoteError Traceback (most recent call last)
42 RuntimeError Traceback (most recent call last)<string> in <module>()
43 ...
43 <ipython-input-12-c3e7afeb3075> in setter(a)
44 RemoteError: RuntimeError(array is not writeable)
45 Traceback (most recent call last):
46 File "/path/to/site-packages/IPython/parallel/streamkernel.py", line 329, in apply_request
47 exec code in working, working
48 File "<string>", line 1, in <module>
49 File "<ipython-input-14-736187483856>", line 2, in setter
50 RuntimeError: array is not writeable
44 RuntimeError: array is not writeable
51
45
52 If you do need to edit the array in-place, just remember to copy the array if it's read-only.
46 If you do need to edit the array in-place, just remember to copy the array if it's read-only.
This diff has been collapsed as it changes many lines, (514 lines changed) Show them Hide them
@@ -387,229 +387,9 b' The following examples demonstrate how to use the instance attributes:'
387 The :attr:`block` and :attr:`targets` instance attributes of the
387 The :attr:`block` and :attr:`targets` instance attributes of the
388 :class:`.DirectView` also determine the behavior of the parallel magic commands.
388 :class:`.DirectView` also determine the behavior of the parallel magic commands.
389
389
390 Parallel magic commands
391 -----------------------
392
393 We provide a few IPython magic commands (``%px``, ``%autopx`` and ``%result``)
394 that make it a bit more pleasant to execute Python commands on the engines interactively.
395 These are simply shortcuts to :meth:`.DirectView.execute`
396 and :meth:`.AsyncResult.display_outputs` methods repsectively.
397 The ``%px`` magic executes a single Python command on the engines
398 specified by the :attr:`targets` attribute of the :class:`DirectView` instance:
399
400 .. sourcecode:: ipython
401
402 # Create a DirectView for all targets
403 In [22]: dv = rc[:]
404
405 # Make this DirectView active for parallel magic commands
406 In [23]: dv.activate()
407
408 In [24]: dv.block=True
409
410 # import numpy here and everywhere
411 In [25]: with dv.sync_imports():
412 ....: import numpy
413 importing numpy on engine(s)
414
415 In [27]: %px a = numpy.random.rand(2,2)
416 Parallel execution on engines: [0, 1, 2, 3]
417
418 In [28]: %px numpy.linalg.eigvals(a)
419 Parallel execution on engines: [0, 1, 2, 3]
420 [0] Out[68]: array([ 0.77120707, -0.19448286])
421 [1] Out[68]: array([ 1.10815921, 0.05110369])
422 [2] Out[68]: array([ 0.74625527, -0.37475081])
423 [3] Out[68]: array([ 0.72931905, 0.07159743])
424
425 In [29]: %px print 'hi'
426 Parallel execution on engine(s): [0, 1, 2, 3]
427 [stdout:0] hi
428 [stdout:1] hi
429 [stdout:2] hi
430 [stdout:3] hi
431
432
433 Since engines are IPython as well, you can even run magics remotely:
434
435 .. sourcecode:: ipython
436
437 In [28]: %px %pylab inline
438 Parallel execution on engine(s): [0, 1, 2, 3]
439 [stdout:0]
440 Welcome to pylab, a matplotlib-based Python environment...
441 For more information, type 'help(pylab)'.
442 [stdout:1]
443 Welcome to pylab, a matplotlib-based Python environment...
444 For more information, type 'help(pylab)'.
445 [stdout:2]
446 Welcome to pylab, a matplotlib-based Python environment...
447 For more information, type 'help(pylab)'.
448 [stdout:3]
449 Welcome to pylab, a matplotlib-based Python environment...
450 For more information, type 'help(pylab)'.
451
452 And once in pylab mode with the inline backend,
453 you can make plots and they will be displayed in your frontend
454 if it suports the inline figures (e.g. notebook or qtconsole):
455
456 .. sourcecode:: ipython
457
458 In [40]: %px plot(rand(100))
459 Parallel execution on engine(s): [0, 1, 2, 3]
460 <plot0>
461 <plot1>
462 <plot2>
463 <plot3>
464 [0] Out[79]: [<matplotlib.lines.Line2D at 0x10a6286d0>]
465 [1] Out[79]: [<matplotlib.lines.Line2D at 0x10b9476d0>]
466 [2] Out[79]: [<matplotlib.lines.Line2D at 0x110652750>]
467 [3] Out[79]: [<matplotlib.lines.Line2D at 0x10c6566d0>]
468
469
470 ``%%px`` Cell Magic
471 *******************
472
473 `%%px` can also be used as a Cell Magic, which accepts ``--[no]block`` flags,
474 and a ``--group-outputs`` argument, which adjust how the outputs of multiple
475 engines are presented.
476
477 .. seealso::
390 .. seealso::
478
391
479 :meth:`.AsyncResult.display_outputs` for the grouping options.
392 See the documentation of the :ref:`Parallel Magics <parallel_magics>`.
480
481 .. sourcecode:: ipython
482
483 In [50]: %%px --block --group-outputs=engine
484 ....: import numpy as np
485 ....: A = np.random.random((2,2))
486 ....: ev = numpy.linalg.eigvals(A)
487 ....: print ev
488 ....: ev.max()
489 ....:
490 Parallel execution on engine(s): [0, 1, 2, 3]
491 [stdout:0] [ 0.60640442 0.95919621]
492 [0] Out[73]: 0.9591962130899806
493 [stdout:1] [ 0.38501813 1.29430871]
494 [1] Out[73]: 1.2943087091452372
495 [stdout:2] [-0.85925141 0.9387692 ]
496 [2] Out[73]: 0.93876920456230284
497 [stdout:3] [ 0.37998269 1.24218246]
498 [3] Out[73]: 1.2421824618493817
499
500 ``%result`` Magic
501 *****************
502
503 If you are using ``%px`` in non-blocking mode, you won't get output.
504 You can use ``%result`` to display the outputs of the latest command,
505 just as is done when ``%px`` is blocking:
506
507 .. sourcecode:: ipython
508
509 In [39]: dv.block = False
510
511 In [40]: %px print 'hi'
512 Async parallel execution on engine(s): [0, 1, 2, 3]
513
514 In [41]: %result
515 [stdout:0] hi
516 [stdout:1] hi
517 [stdout:2] hi
518 [stdout:3] hi
519
520 ``%result`` simply calls :meth:`.AsyncResult.display_outputs` on the most recent request.
521 You can pass integers as indices if you want a result other than the latest,
522 e.g. ``%result -2``, or ``%result 0`` for the first.
523
524
525 ``%autopx``
526 ***********
527
528 The ``%autopx`` magic switches to a mode where everything you type is executed
529 on the engines until you do ``%autopx`` again.
530
531 .. sourcecode:: ipython
532
533 In [30]: dv.block=True
534
535 In [31]: %autopx
536 %autopx enabled
537
538 In [32]: max_evals = []
539
540 In [33]: for i in range(100):
541 ....: a = numpy.random.rand(10,10)
542 ....: a = a+a.transpose()
543 ....: evals = numpy.linalg.eigvals(a)
544 ....: max_evals.append(evals[0].real)
545 ....:
546
547 In [34]: print "Average max eigenvalue is: %f" % (sum(max_evals)/len(max_evals))
548 [stdout:0] Average max eigenvalue is: 10.193101
549 [stdout:1] Average max eigenvalue is: 10.064508
550 [stdout:2] Average max eigenvalue is: 10.055724
551 [stdout:3] Average max eigenvalue is: 10.086876
552
553 In [35]: %autopx
554 Auto Parallel Disabled
555
556
557 Engines as Kernels
558 ******************
559
560 Engines are really the same object as the Kernels used elsewhere in IPython,
561 with the minor exception that engines connect to a controller, while regular kernels
562 bind their sockets, listening for connections from a QtConsole or other frontends.
563
564 Sometimes for debugging or inspection purposes, you would like a QtConsole connected
565 to an engine for more direct interaction. You can do this by first instructing
566 the Engine to *also* bind its kernel, to listen for connections:
567
568 .. sourcecode:: ipython
569
570 In [50]: %px from IPython.parallel import bind_kernel; bind_kernel()
571
572 Then, if your engines are local, you can start a qtconsole right on the engine(s):
573
574 .. sourcecode:: ipython
575
576 In [51]: %px %qtconsole
577
578 Careful with this one, because if your view is of 16 engines it will start 16 QtConsoles!
579
580 Or you can view just the connection info, and work out the right way to connect to the engines,
581 depending on where they live and where you are:
582
583 .. sourcecode:: ipython
584
585 In [51]: %px %connect_info
586 Parallel execution on engine(s): [0, 1, 2, 3]
587 [stdout:0]
588 {
589 "stdin_port": 60387,
590 "ip": "127.0.0.1",
591 "hb_port": 50835,
592 "key": "eee2dd69-7dd3-4340-bf3e-7e2e22a62542",
593 "shell_port": 55328,
594 "iopub_port": 58264
595 }
596
597 Paste the above JSON into a file, and connect with:
598 $> ipython <app> --existing <file>
599 or, if you are local, you can connect with just:
600 $> ipython <app> --existing kernel-60125.json
601 or even just:
602 $> ipython <app> --existing
603 if this is the most recent IPython session you have started.
604 [stdout:1]
605 {
606 "stdin_port": 61869,
607 ...
608
609 .. note::
610
611 ``%qtconsole`` will call :func:`bind_kernel` on an engine if it hasn't been done already,
612 so you can often skip that first step.
613
393
614
394
615 Moving Python objects around
395 Moving Python objects around
@@ -753,13 +533,20 b' be collected and raise a CompositeError, as demonstrated in the next section.'
753 ....: time.sleep(t)
533 ....: time.sleep(t)
754 ....: return t
534 ....: return t
755
535
536 .. note::
537
538 :func:`sync_imports` does not allow ``import foo as bar`` syntax,
539 because the assignment represented by the ``as bar`` part is not
540 available to the import hook.
541
542
756 .. _parallel_exceptions:
543 .. _parallel_exceptions:
757
544
758 Parallel exceptions
545 Parallel exceptions
759 -------------------
546 -------------------
760
547
761 In the multiengine interface, parallel commands can raise Python exceptions,
548 In the multiengine interface, parallel commands can raise Python exceptions,
762 just like serial commands. But, it is a little subtle, because a single
549 just like serial commands. But it is a little subtle, because a single
763 parallel command can actually raise multiple exceptions (one for each engine
550 parallel command can actually raise multiple exceptions (one for each engine
764 the command was run on). To express this idea, we have a
551 the command was run on). To express this idea, we have a
765 :exc:`CompositeError` exception class that will be raised in most cases. The
552 :exc:`CompositeError` exception class that will be raised in most cases. The
@@ -768,58 +555,32 b' more other types of exceptions. Here is how it works:'
768
555
769 .. sourcecode:: ipython
556 .. sourcecode:: ipython
770
557
771 In [76]: dview.block=True
558 In [78]: dview.block = True
559
560 In [79]: dview.execute("1/0")
561 [0:execute]:
562 ---------------------------------------------------------------------------
563 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
564 ----> 1 1/0
565 ZeroDivisionError: integer division or modulo by zero
772
566
773 In [77]: dview.execute('1/0')
567 [1:execute]:
774 ---------------------------------------------------------------------------
568 ---------------------------------------------------------------------------
775 CompositeError Traceback (most recent call last)
569 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
776 /home/user/<ipython-input-10-5d56b303a66c> in <module>()
570 ----> 1 1/0
777 ----> 1 dview.execute('1/0')
571 ZeroDivisionError: integer division or modulo by zero
778
572
779 /path/to/site-packages/IPython/parallel/client/view.pyc in execute(self, code, targets, block)
573 [2:execute]:
780 591 default: self.block
574 ---------------------------------------------------------------------------
781 592 """
575 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
782 --> 593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
576 ----> 1 1/0
783 594
577 ZeroDivisionError: integer division or modulo by zero
784 595 def run(self, filename, targets=None, block=None):
578
785
579 [3:execute]:
786 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
580 ---------------------------------------------------------------------------
787
581 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
788 /path/to/site-packages/IPython/parallel/client/view.pyc in sync_results(f, self, *args, **kwargs)
582 ----> 1 1/0
789 55 def sync_results(f, self, *args, **kwargs):
583 ZeroDivisionError: integer division or modulo by zero
790 56 """sync relevant results from self.client to our results attribute."""
791 ---> 57 ret = f(self, *args, **kwargs)
792 58 delta = self.outstanding.difference(self.client.outstanding)
793 59 completed = self.outstanding.intersection(delta)
794
795 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
796
797 /path/to/site-packages/IPython/parallel/client/view.pyc in save_ids(f, self, *args, **kwargs)
798 44 n_previous = len(self.client.history)
799 45 try:
800 ---> 46 ret = f(self, *args, **kwargs)
801 47 finally:
802 48 nmsgs = len(self.client.history) - n_previous
803
804 /path/to/site-packages/IPython/parallel/client/view.pyc in _really_apply(self, f, args, kwargs, targets, block, track)
805 529 if block:
806 530 try:
807 --> 531 return ar.get()
808 532 except KeyboardInterrupt:
809 533 pass
810
811 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
812 101 return self._result
813 102 else:
814 --> 103 raise self._exception
815 104 else:
816 105 raise error.TimeoutError("Result not ready.")
817
818 CompositeError: one or more exceptions from call to method: _execute
819 [0:apply]: ZeroDivisionError: integer division or modulo by zero
820 [1:apply]: ZeroDivisionError: integer division or modulo by zero
821 [2:apply]: ZeroDivisionError: integer division or modulo by zero
822 [3:apply]: ZeroDivisionError: integer division or modulo by zero
823
584
824 Notice how the error message printed when :exc:`CompositeError` is raised has
585 Notice how the error message printed when :exc:`CompositeError` is raised has
825 information about the individual exceptions that were raised on each engine.
586 information about the individual exceptions that were raised on each engine.
@@ -828,33 +589,14 b' If you want, you can even raise one of these original exceptions:'
828 .. sourcecode:: ipython
589 .. sourcecode:: ipython
829
590
830 In [80]: try:
591 In [80]: try:
831 ....: dview.execute('1/0')
592 ....: dview.execute('1/0', block=True)
832 ....: except parallel.error.CompositeError, e:
593 ....: except parallel.error.CompositeError, e:
833 ....: e.raise_exception()
594 ....: e.raise_exception()
834 ....:
595 ....:
835 ....:
596 ....:
836 ---------------------------------------------------------------------------
597 ---------------------------------------------------------------------------
837 RemoteError Traceback (most recent call last)
598 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
838 /home/user/<ipython-input-17-8597e7e39858> in <module>()
599 ----> 1 1/0
839 2 dview.execute('1/0')
840 3 except CompositeError as e:
841 ----> 4 e.raise_exception()
842
843 /path/to/site-packages/IPython/parallel/error.pyc in raise_exception(self, excid)
844 266 raise IndexError("an exception with index %i does not exist"%excid)
845 267 else:
846 --> 268 raise RemoteError(en, ev, etb, ei)
847 269
848 270
849
850 RemoteError: ZeroDivisionError(integer division or modulo by zero)
851 Traceback (most recent call last):
852 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
853 exec code in working,working
854 File "<string>", line 1, in <module>
855 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
856 exec code in globals()
857 File "<string>", line 1, in <module>
858 ZeroDivisionError: integer division or modulo by zero
600 ZeroDivisionError: integer division or modulo by zero
859
601
860 If you are working in IPython, you can simple type ``%debug`` after one of
602 If you are working in IPython, you can simple type ``%debug`` after one of
@@ -864,112 +606,56 b' instance:'
864 .. sourcecode:: ipython
606 .. sourcecode:: ipython
865
607
866 In [81]: dview.execute('1/0')
608 In [81]: dview.execute('1/0')
609 [0:execute]:
867 ---------------------------------------------------------------------------
610 ---------------------------------------------------------------------------
868 CompositeError Traceback (most recent call last)
611 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
869 /home/user/<ipython-input-10-5d56b303a66c> in <module>()
612 ----> 1 1/0
870 ----> 1 dview.execute('1/0')
871
872 /path/to/site-packages/IPython/parallel/client/view.pyc in execute(self, code, targets, block)
873 591 default: self.block
874 592 """
875 --> 593 return self._really_apply(util._execute, args=(code,), block=block, targets=targets)
876 594
877 595 def run(self, filename, targets=None, block=None):
878
879 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
880
881 /path/to/site-packages/IPython/parallel/client/view.pyc in sync_results(f, self, *args, **kwargs)
882 55 def sync_results(f, self, *args, **kwargs):
883 56 """sync relevant results from self.client to our results attribute."""
884 ---> 57 ret = f(self, *args, **kwargs)
885 58 delta = self.outstanding.difference(self.client.outstanding)
886 59 completed = self.outstanding.intersection(delta)
887
888 /home/user/<string> in _really_apply(self, f, args, kwargs, targets, block, track)
889
890 /path/to/site-packages/IPython/parallel/client/view.pyc in save_ids(f, self, *args, **kwargs)
891 44 n_previous = len(self.client.history)
892 45 try:
893 ---> 46 ret = f(self, *args, **kwargs)
894 47 finally:
895 48 nmsgs = len(self.client.history) - n_previous
896
897 /path/to/site-packages/IPython/parallel/client/view.pyc in _really_apply(self, f, args, kwargs, targets, block, track)
898 529 if block:
899 530 try:
900 --> 531 return ar.get()
901 532 except KeyboardInterrupt:
902 533 pass
903
904 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
905 101 return self._result
906 102 else:
907 --> 103 raise self._exception
908 104 else:
909 105 raise error.TimeoutError("Result not ready.")
910
911 CompositeError: one or more exceptions from call to method: _execute
912 [0:apply]: ZeroDivisionError: integer division or modulo by zero
913 [1:apply]: ZeroDivisionError: integer division or modulo by zero
914 [2:apply]: ZeroDivisionError: integer division or modulo by zero
915 [3:apply]: ZeroDivisionError: integer division or modulo by zero
916
917 In [82]: %debug
918 > /path/to/site-packages/IPython/parallel/client/asyncresult.py(103)get()
919 102 else:
920 --> 103 raise self._exception
921 104 else:
922
923 # With the debugger running, self._exception is the exceptions instance. We can tab complete
924 # on it and see the extra methods that are available.
925 ipdb> self._exception.<tab>
926 e.__class__ e.__getitem__ e.__new__ e.__setstate__ e.args
927 e.__delattr__ e.__getslice__ e.__reduce__ e.__str__ e.elist
928 e.__dict__ e.__hash__ e.__reduce_ex__ e.__weakref__ e.message
929 e.__doc__ e.__init__ e.__repr__ e._get_engine_str e.print_tracebacks
930 e.__getattribute__ e.__module__ e.__setattr__ e._get_traceback e.raise_exception
931 ipdb> self._exception.print_tracebacks()
932 [0:apply]:
933 Traceback (most recent call last):
934 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
935 exec code in working,working
936 File "<string>", line 1, in <module>
937 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
938 exec code in globals()
939 File "<string>", line 1, in <module>
940 ZeroDivisionError: integer division or modulo by zero
613 ZeroDivisionError: integer division or modulo by zero
941
614
942
615 [1:execute]:
943 [1:apply]:
616 ---------------------------------------------------------------------------
944 Traceback (most recent call last):
617 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
945 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
618 ----> 1 1/0
946 exec code in working,working
947 File "<string>", line 1, in <module>
948 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
949 exec code in globals()
950 File "<string>", line 1, in <module>
951 ZeroDivisionError: integer division or modulo by zero
619 ZeroDivisionError: integer division or modulo by zero
952
620
953
621 [2:execute]:
954 [2:apply]:
622 ---------------------------------------------------------------------------
955 Traceback (most recent call last):
623 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
956 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
624 ----> 1 1/0
957 exec code in working,working
958 File "<string>", line 1, in <module>
959 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
960 exec code in globals()
961 File "<string>", line 1, in <module>
962 ZeroDivisionError: integer division or modulo by zero
625 ZeroDivisionError: integer division or modulo by zero
963
626
964
627 [3:execute]:
965 [3:apply]:
628 ---------------------------------------------------------------------------
966 Traceback (most recent call last):
629 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
967 File "/path/to/site-packages/IPython/parallel/engine/streamkernel.py", line 330, in apply_request
630 ----> 1 1/0
968 exec code in working,working
631 ZeroDivisionError: integer division or modulo by zero
969 File "<string>", line 1, in <module>
632
970 File "/path/to/site-packages/IPython/parallel/util.py", line 354, in _execute
633 In [82]: %debug
971 exec code in globals()
634 > /.../site-packages/IPython/parallel/client/asyncresult.py(125)get()
972 File "<string>", line 1, in <module>
635 124 else:
636 --> 125 raise self._exception
637 126 else:
638
639 # Here, self._exception is the CompositeError instance:
640
641 ipdb> e = self._exception
642 ipdb> e
643 CompositeError(4)
644
645 # we can tab-complete on e to see available methods:
646 ipdb> e.<TAB>
647 e.args e.message e.traceback
648 e.elist e.msg
649 e.ename e.print_traceback
650 e.engine_info e.raise_exception
651 e.evalue e.render_traceback
652
653 # We can then display the individual tracebacks, if we want:
654 ipdb> e.print_traceback(1)
655 [1:execute]:
656 ---------------------------------------------------------------------------
657 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
658 ----> 1 1/0
973 ZeroDivisionError: integer division or modulo by zero
659 ZeroDivisionError: integer division or modulo by zero
974
660
975
661
@@ -982,21 +668,27 b' All of this same error handling magic even works in non-blocking mode:'
982 In [84]: ar = dview.execute('1/0')
668 In [84]: ar = dview.execute('1/0')
983
669
984 In [85]: ar.get()
670 In [85]: ar.get()
671 [0:execute]:
985 ---------------------------------------------------------------------------
672 ---------------------------------------------------------------------------
986 CompositeError Traceback (most recent call last)
673 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
987 /home/user/<ipython-input-21-8531eb3d26fb> in <module>()
674 ----> 1 1/0
988 ----> 1 ar.get()
675 ZeroDivisionError: integer division or modulo by zero
989
676
990 /path/to/site-packages/IPython/parallel/client/asyncresult.pyc in get(self, timeout)
677 [1:execute]:
991 101 return self._result
678 ---------------------------------------------------------------------------
992 102 else:
679 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
993 --> 103 raise self._exception
680 ----> 1 1/0
994 104 else:
681 ZeroDivisionError: integer division or modulo by zero
995 105 raise error.TimeoutError("Result not ready.")
682
996
683 [2:execute]:
997 CompositeError: one or more exceptions from call to method: _execute
684 ---------------------------------------------------------------------------
998 [0:apply]: ZeroDivisionError: integer division or modulo by zero
685 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
999 [1:apply]: ZeroDivisionError: integer division or modulo by zero
686 ----> 1 1/0
1000 [2:apply]: ZeroDivisionError: integer division or modulo by zero
687 ZeroDivisionError: integer division or modulo by zero
1001 [3:apply]: ZeroDivisionError: integer division or modulo by zero
688
689 [3:execute]:
690 ---------------------------------------------------------------------------
691 ZeroDivisionError Traceback (most recent call last)<ipython-input-1-05c9758a9c21> in <module>()
692 ----> 1 1/0
693 ZeroDivisionError: integer division or modulo by zero
1002
694
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now