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