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