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= |
|
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 ( |
|
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 |
|
|
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 |
|
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 |
|
|
258 | lines.append(self._get_engine_str(ei)) | |
251 |
|
|
259 | lines.extend((etb or 'No traceback available').splitlines()) | |
252 |
|
|
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 |
|
|
267 | lines.append(self._get_engine_str(ei)) | |
260 |
|
|
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 |
|
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() |
|
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() |
|
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() |
|
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(' |
|
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 |
|
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 |
|
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 |
|
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 |
|
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': |
|
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 |
|
|
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 = |
|
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 |
|
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 |
|
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\" % |
|
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 |
"% |
|
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 |
|
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 |
R |
|
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 |
|
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 [7 |
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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