Show More
@@ -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: | |
@@ -480,6 +480,18 b' class Client(HasTraits):' | |||||
480 | self._queue_handlers = {'execute_reply' : self._handle_execute_reply, |
|
480 | self._queue_handlers = {'execute_reply' : self._handle_execute_reply, | |
481 | 'apply_reply' : self._handle_apply_reply} |
|
481 | 'apply_reply' : self._handle_apply_reply} | |
482 | self._connect(sshserver, ssh_kwargs, timeout) |
|
482 | self._connect(sshserver, ssh_kwargs, timeout) | |
|
483 | ||||
|
484 | # last step: setup magics, if we are in IPython: | |||
|
485 | ||||
|
486 | try: | |||
|
487 | ip = get_ipython() | |||
|
488 | except NameError: | |||
|
489 | return | |||
|
490 | else: | |||
|
491 | if 'px' not in ip.magics_manager.magics: | |||
|
492 | # in IPython but we are the first Client. | |||
|
493 | # activate a default view for parallel magics. | |||
|
494 | self.activate() | |||
483 |
|
495 | |||
484 | def __del__(self): |
|
496 | def __del__(self): | |
485 | """cleanup sockets, but _not_ context.""" |
|
497 | """cleanup sockets, but _not_ context.""" | |
@@ -905,6 +917,29 b' class Client(HasTraits):' | |||||
905 | # always copy: |
|
917 | # always copy: | |
906 | return list(self._ids) |
|
918 | return list(self._ids) | |
907 |
|
919 | |||
|
920 | def activate(self, targets='all', suffix=''): | |||
|
921 | """Create a DirectView and register it with IPython magics | |||
|
922 | ||||
|
923 | Defines the magics `%px, %autopx, %pxresult, %%px` | |||
|
924 | ||||
|
925 | Parameters | |||
|
926 | ---------- | |||
|
927 | ||||
|
928 | targets: int, list of ints, or 'all' | |||
|
929 | The engines on which the view's magics will run | |||
|
930 | suffix: str [default: ''] | |||
|
931 | The suffix, if any, for the magics. This allows you to have | |||
|
932 | multiple views associated with parallel magics at the same time. | |||
|
933 | ||||
|
934 | e.g. ``rc.activate(targets=0, suffix='0')`` will give you | |||
|
935 | the magics ``%px0``, ``%pxresult0``, etc. for running magics just | |||
|
936 | on engine 0. | |||
|
937 | """ | |||
|
938 | view = self.direct_view(targets) | |||
|
939 | view.block = True | |||
|
940 | view.activate(suffix) | |||
|
941 | return view | |||
|
942 | ||||
908 | def close(self): |
|
943 | def close(self): | |
909 | if self._closed: |
|
944 | if self._closed: | |
910 | return |
|
945 | return |
@@ -17,10 +17,14 b' Usage' | |||||
17 |
|
17 | |||
18 | {PX_DOC} |
|
18 | {PX_DOC} | |
19 |
|
19 | |||
20 | ``%result`` |
|
20 | ``%pxresult`` | |
21 |
|
21 | |||
22 | {RESULT_DOC} |
|
22 | {RESULT_DOC} | |
23 |
|
23 | |||
|
24 | ``%pxconfig`` | |||
|
25 | ||||
|
26 | {CONFIG_DOC} | |||
|
27 | ||||
24 | """ |
|
28 | """ | |
25 |
|
29 | |||
26 | #----------------------------------------------------------------------------- |
|
30 | #----------------------------------------------------------------------------- | |
@@ -38,7 +42,8 b' import ast' | |||||
38 | import re |
|
42 | import re | |
39 |
|
43 | |||
40 | from IPython.core.error import UsageError |
|
44 | from IPython.core.error import UsageError | |
41 |
from IPython.core.magic import Magics |
|
45 | from IPython.core.magic import Magics | |
|
46 | from IPython.core import magic_arguments | |||
42 | from IPython.testing.skipdoctest import skip_doctest |
|
47 | from IPython.testing.skipdoctest import skip_doctest | |
43 |
|
48 | |||
44 | #----------------------------------------------------------------------------- |
|
49 | #----------------------------------------------------------------------------- | |
@@ -46,95 +51,164 b' from IPython.testing.skipdoctest import skip_doctest' | |||||
46 | #----------------------------------------------------------------------------- |
|
51 | #----------------------------------------------------------------------------- | |
47 |
|
52 | |||
48 |
|
53 | |||
49 | NO_ACTIVE_VIEW = "Use activate() on a DirectView object to use it with magics." |
|
54 | NO_LAST_RESULT = "%pxresult recalls last %px result, which has not yet been used." | |
50 | NO_LAST_RESULT = "%result recalls last %px result, which has not yet been used." |
|
|||
51 |
|
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 | ] | |||
|
74 | for a in args: | |||
|
75 | f = a(f) | |||
|
76 | return f | |||
|
77 | ||||
|
78 | def output_args(f): | |||
|
79 | """decorator for output-formatting args | |||
|
80 | ||||
|
81 | applied to %pxresult and %%px | |||
|
82 | """ | |||
|
83 | args = [ | |||
|
84 | magic_arguments.argument('-r', action="store_const", dest='groupby', | |||
|
85 | const='order', | |||
|
86 | help="collate outputs in order (same as group-outputs=order)" | |||
|
87 | ), | |||
|
88 | magic_arguments.argument('-e', action="store_const", dest='groupby', | |||
|
89 | const='engine', | |||
|
90 | help="group outputs by engine (same as group-outputs=engine)" | |||
|
91 | ), | |||
|
92 | magic_arguments.argument('--group-outputs', dest='groupby', type=str, | |||
|
93 | choices=['engine', 'order', 'type'], default='type', | |||
|
94 | help="""Group the outputs in a particular way. | |||
|
95 | ||||
|
96 | Choices are: | |||
|
97 | ||||
|
98 | type: group outputs of all engines by type (stdout, stderr, displaypub, etc.). | |||
|
99 | ||||
|
100 | engine: display all output for each engine together. | |||
|
101 | ||||
|
102 | order: like type, but individual displaypub output from each engine is collated. | |||
|
103 | For example, if multiple plots are generated by each engine, the first | |||
|
104 | figure of each engine will be displayed, then the second of each, etc. | |||
|
105 | """ | |||
|
106 | ), | |||
|
107 | magic_arguments.argument('-o', '--out', dest='save_name', type=str, | |||
|
108 | help="""store the AsyncResult object for this computation | |||
|
109 | in the global namespace under this name. | |||
|
110 | """ | |||
|
111 | ), | |||
|
112 | ] | |||
|
113 | for a in args: | |||
|
114 | f = a(f) | |||
|
115 | return f | |||
52 |
|
116 | |||
53 | @magics_class |
|
|||
54 | class ParallelMagics(Magics): |
|
117 | class ParallelMagics(Magics): | |
55 | """A set of magics useful when controlling a parallel IPython cluster. |
|
118 | """A set of magics useful when controlling a parallel IPython cluster. | |
56 | """ |
|
119 | """ | |
57 |
|
120 | |||
|
121 | # magic-related | |||
|
122 | magics = None | |||
|
123 | registered = True | |||
|
124 | ||||
|
125 | # suffix for magics | |||
|
126 | suffix = '' | |||
58 | # A flag showing if autopx is activated or not |
|
127 | # A flag showing if autopx is activated or not | |
59 | _autopx = False |
|
128 | _autopx = False | |
60 | # the current view used by the magics: |
|
129 | # the current view used by the magics: | |
61 |
|
|
130 | view = None | |
62 | # last result cache for %result |
|
131 | # last result cache for %pxresult | |
63 | last_result = None |
|
132 | last_result = None | |
64 |
|
133 | |||
65 | @skip_doctest |
|
134 | def __init__(self, shell, view, suffix=''): | |
66 | @line_magic |
|
135 | self.view = view | |
67 | def result(self, line=''): |
|
136 | self.suffix = suffix | |
68 | """Print the result of the last asynchronous %px command. |
|
|||
69 |
|
||||
70 | Usage: |
|
|||
71 |
|
137 | |||
72 | %result [-o] [-e] [--group-options=type|engine|order] |
|
138 | # register magics | |
|
139 | self.magics = dict(cell={},line={}) | |||
|
140 | line_magics = self.magics['line'] | |||
73 |
|
141 | |||
74 | Options: |
|
142 | px = 'px' + suffix | |
|
143 | if not suffix: | |||
|
144 | # keep %result for legacy compatibility | |||
|
145 | line_magics['result'] = self.result | |||
75 |
|
146 | |||
76 | -o: collate outputs in order (same as group-outputs=order) |
|
147 | line_magics['pxresult' + suffix] = self.result | |
|
148 | line_magics[px] = self.px | |||
|
149 | line_magics['pxconfig' + suffix] = self.pxconfig | |||
|
150 | line_magics['auto' + px] = self.autopx | |||
77 |
|
151 | |||
78 | -e: group outputs by engine (same as group-outputs=engine) |
|
152 | self.magics['cell'][px] = self.cell_px | |
79 |
|
153 | |||
80 | --group-outputs=type [default behavior]: |
|
154 | super(ParallelMagics, self).__init__(shell=shell) | |
81 | each output type (stdout, stderr, displaypub) for all engines |
|
155 | ||
82 | displayed together. |
|
156 | def _eval_target_str(self, ts): | |
83 |
|
157 | if ':' in ts: | ||
84 | --group-outputs=order: |
|
158 | targets = eval("self.view.client.ids[%s]" % ts) | |
85 | The same as 'type', but individual displaypub outputs (e.g. plots) |
|
159 | elif 'all' in ts: | |
86 | will be interleaved, so it will display all of the first plots, |
|
160 | targets = 'all' | |
87 | then all of the second plots, etc. |
|
161 | else: | |
88 |
|
162 | targets = eval(ts) | ||
89 | --group-outputs=engine: |
|
163 | return targets | |
90 | All of an engine's output is displayed before moving on to the next. |
|
164 | ||
91 |
|
165 | @magic_arguments.magic_arguments() | ||
92 | To use this a :class:`DirectView` instance must be created |
|
166 | @exec_args | |
93 | and then activated by calling its :meth:`activate` method. |
|
167 | def pxconfig(self, line): | |
|
168 | """configure default targets/blocking for %px magics""" | |||
|
169 | args = magic_arguments.parse_argstring(self.pxconfig, line) | |||
|
170 | if args.targets: | |||
|
171 | self.view.targets = self._eval_target_str(args.targets) | |||
|
172 | if args.block is not None: | |||
|
173 | self.view.block = args.block | |||
|
174 | ||||
|
175 | @magic_arguments.magic_arguments() | |||
|
176 | @output_args | |||
|
177 | @skip_doctest | |||
|
178 | def result(self, line=''): | |||
|
179 | """Print the result of the last asynchronous %px command. | |||
94 |
|
180 | |||
95 | This lets you recall the results of %px computations after |
|
181 | This lets you recall the results of %px computations after | |
96 |
asynchronous submission ( |
|
182 | asynchronous submission (block=False). | |
97 |
|
183 | |||
98 | Then you can do the following:: |
|
184 | Examples | |
|
185 | -------- | |||
|
186 | :: | |||
99 |
|
187 | |||
100 | In [23]: %px os.getpid() |
|
188 | In [23]: %px os.getpid() | |
101 | Async parallel execution on engine(s): all |
|
189 | Async parallel execution on engine(s): all | |
102 |
|
190 | |||
103 | In [24]: %result |
|
191 | In [24]: %pxresult | |
104 | [ 8] Out[10]: 60920 |
|
192 | [ 8] Out[10]: 60920 | |
105 | [ 9] Out[10]: 60921 |
|
193 | [ 9] Out[10]: 60921 | |
106 | [10] Out[10]: 60922 |
|
194 | [10] Out[10]: 60922 | |
107 | [11] Out[10]: 60923 |
|
195 | [11] Out[10]: 60923 | |
108 | """ |
|
196 | """ | |
109 | opts, _ = self.parse_options(line, 'oe', 'group-outputs=') |
|
197 | args = magic_arguments.parse_argstring(self.result, line) | |
110 |
|
||||
111 | if 'group-outputs' in opts: |
|
|||
112 | groupby = opts['group-outputs'] |
|
|||
113 | elif 'o' in opts: |
|
|||
114 | groupby = 'order' |
|
|||
115 | elif 'e' in opts: |
|
|||
116 | groupby = 'engine' |
|
|||
117 | else: |
|
|||
118 | groupby = 'type' |
|
|||
119 |
|
||||
120 | if self.active_view is None: |
|
|||
121 | raise UsageError(NO_ACTIVE_VIEW) |
|
|||
122 |
|
198 | |||
123 | if self.last_result is None: |
|
199 | if self.last_result is None: | |
124 | raise UsageError(NO_LAST_RESULT) |
|
200 | raise UsageError(NO_LAST_RESULT) | |
125 |
|
201 | |||
126 | self.last_result.get() |
|
202 | self.last_result.get() | |
127 | self.last_result.display_outputs(groupby=groupby) |
|
203 | self.last_result.display_outputs(groupby=args.groupby) | |
128 |
|
204 | |||
129 | @skip_doctest |
|
205 | @skip_doctest | |
130 | @line_magic |
|
206 | def px(self, line=''): | |
131 | def px(self, parameter_s=''): |
|
|||
132 | """Executes the given python command in parallel. |
|
207 | """Executes the given python command in parallel. | |
133 |
|
208 | |||
134 | To use this a :class:`DirectView` instance must be created |
|
209 | Examples | |
135 | and then activated by calling its :meth:`activate` method. |
|
210 | -------- | |
136 |
|
211 | :: | ||
137 | Then you can do the following:: |
|
|||
138 |
|
212 | |||
139 | In [24]: %px a = os.getpid() |
|
213 | In [24]: %px a = os.getpid() | |
140 | Parallel execution on engine(s): all |
|
214 | Parallel execution on engine(s): all | |
@@ -145,27 +219,24 b' class ParallelMagics(Magics):' | |||||
145 | [stdout:2] 1236 |
|
219 | [stdout:2] 1236 | |
146 | [stdout:3] 1237 |
|
220 | [stdout:3] 1237 | |
147 | """ |
|
221 | """ | |
148 |
return self.parallel_execute( |
|
222 | return self.parallel_execute(line) | |
149 |
|
223 | |||
150 | def parallel_execute(self, cell, block=None, groupby='type', save_name=None): |
|
224 | def parallel_execute(self, cell, block=None, groupby='type', save_name=None): | |
151 | """implementation used by %px and %%parallel""" |
|
225 | """implementation used by %px and %%parallel""" | |
152 |
|
226 | |||
153 | if self.active_view is None: |
|
|||
154 | raise UsageError(NO_ACTIVE_VIEW) |
|
|||
155 |
|
||||
156 | # defaults: |
|
227 | # defaults: | |
157 |
block = self. |
|
228 | block = self.view.block if block is None else block | |
158 |
|
229 | |||
159 | base = "Parallel" if block else "Async parallel" |
|
230 | base = "Parallel" if block else "Async parallel" | |
160 |
|
231 | |||
161 |
targets = self. |
|
232 | targets = self.view.targets | |
162 | if isinstance(targets, list) and len(targets) > 10: |
|
233 | if isinstance(targets, list) and len(targets) > 10: | |
163 | str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:] |
|
234 | str_targets = str(targets[:4])[:-1] + ', ..., ' + str(targets[-4:])[1:] | |
164 | else: |
|
235 | else: | |
165 | str_targets = str(targets) |
|
236 | str_targets = str(targets) | |
166 | print base + " execution on engine(s): %s" % str_targets |
|
237 | print base + " execution on engine(s): %s" % str_targets | |
167 |
|
238 | |||
168 |
result = self. |
|
239 | result = self.view.execute(cell, silent=False, block=False) | |
169 | self.last_result = result |
|
240 | self.last_result = result | |
170 |
|
241 | |||
171 | if save_name: |
|
242 | if save_name: | |
@@ -178,45 +249,16 b' class ParallelMagics(Magics):' | |||||
178 | # return AsyncResult only on non-blocking submission |
|
249 | # return AsyncResult only on non-blocking submission | |
179 | return result |
|
250 | return result | |
180 |
|
251 | |||
|
252 | @magic_arguments.magic_arguments() | |||
|
253 | @exec_args | |||
|
254 | @output_args | |||
181 | @skip_doctest |
|
255 | @skip_doctest | |
182 | @cell_magic('px') |
|
|||
183 | def cell_px(self, line='', cell=None): |
|
256 | def cell_px(self, line='', cell=None): | |
184 |
"""Executes the |
|
257 | """Executes the cell in parallel. | |
185 |
|
||||
186 | Cell magic usage: |
|
|||
187 |
|
||||
188 | %%px [-o] [-e] [--group-options=type|engine|order] [--[no]block] [--out name] |
|
|||
189 |
|
||||
190 | Options: |
|
|||
191 |
|
||||
192 | --out <name>: store the AsyncResult object for this computation |
|
|||
193 | in the global namespace. |
|
|||
194 |
|
||||
195 | -o: collate outputs in order (same as group-outputs=order) |
|
|||
196 |
|
||||
197 | -e: group outputs by engine (same as group-outputs=engine) |
|
|||
198 |
|
||||
199 | --group-outputs=type [default behavior]: |
|
|||
200 | each output type (stdout, stderr, displaypub) for all engines |
|
|||
201 | displayed together. |
|
|||
202 |
|
||||
203 | --group-outputs=order: |
|
|||
204 | The same as 'type', but individual displaypub outputs (e.g. plots) |
|
|||
205 | will be interleaved, so it will display all of the first plots, |
|
|||
206 | then all of the second plots, etc. |
|
|||
207 |
|
||||
208 | --group-outputs=engine: |
|
|||
209 | All of an engine's output is displayed before moving on to the next. |
|
|||
210 |
|
||||
211 | --[no]block: |
|
|||
212 | Whether or not to block for the execution to complete |
|
|||
213 | (and display the results). If unspecified, the active view's |
|
|||
214 |
|
258 | |||
215 |
|
259 | Examples | ||
216 | To use this a :class:`DirectView` instance must be created |
|
260 | -------- | |
217 | and then activated by calling its :meth:`activate` method. |
|
261 | :: | |
218 |
|
||||
219 | Then you can do the following:: |
|
|||
220 |
|
262 | |||
221 | In [24]: %%px --noblock |
|
263 | In [24]: %%px --noblock | |
222 | ....: a = os.getpid() |
|
264 | ....: a = os.getpid() | |
@@ -230,38 +272,29 b' class ParallelMagics(Magics):' | |||||
230 | [stdout:3] 1237 |
|
272 | [stdout:3] 1237 | |
231 | """ |
|
273 | """ | |
232 |
|
274 | |||
233 | block = None |
|
275 | args = magic_arguments.parse_argstring(self.cell_px, line) | |
234 | groupby = 'type' |
|
276 | ||
235 | # as a cell magic, we accept args |
|
277 | if args.targets: | |
236 | opts, _ = self.parse_options(line, 'oe', 'group-outputs=', 'out=', 'block', 'noblock') |
|
278 | save_targets = self.view.targets | |
237 |
|
279 | self.view.targets = self._eval_target_str(args.targets) | ||
238 | if 'group-outputs' in opts: |
|
280 | try: | |
239 | groupby = opts['group-outputs'] |
|
281 | return self.parallel_execute(cell, block=args.block, | |
240 | elif 'o' in opts: |
|
282 | groupby=args.groupby, | |
241 | groupby = 'order' |
|
283 | save_name=args.save_name, | |
242 | elif 'e' in opts: |
|
284 | ) | |
243 | groupby = 'engine' |
|
285 | finally: | |
244 |
|
286 | if args.targets: | ||
245 | if 'block' in opts: |
|
287 | self.view.targets = save_targets | |
246 | block = True |
|
288 | ||
247 | elif 'noblock' in opts: |
|
|||
248 | block = False |
|
|||
249 |
|
||||
250 | save_name = opts.get('out') |
|
|||
251 |
|
||||
252 | return self.parallel_execute(cell, block=block, groupby=groupby, save_name=save_name) |
|
|||
253 |
|
||||
254 | @skip_doctest |
|
289 | @skip_doctest | |
255 | @line_magic |
|
290 | def autopx(self, line=''): | |
256 | def autopx(self, parameter_s=''): |
|
|||
257 | """Toggles auto parallel mode. |
|
291 | """Toggles auto parallel mode. | |
258 |
|
292 | |||
259 | To use this a :class:`DirectView` instance must be created |
|
293 | Once this is called, all commands typed at the command line are send to | |
260 | and then activated by calling its :meth:`activate` method. Once this |
|
294 | the engines to be executed in parallel. To control which engine are | |
261 | is called, all commands typed at the command line are send to |
|
295 | used, the ``targets`` attribute of the view before | |
262 | the engines to be executed in parallel. To control which engine |
|
296 | entering ``%autopx`` mode. | |
263 | are used, set the ``targets`` attributed of the multiengine client |
|
297 | ||
264 | before entering ``%autopx`` mode. |
|
|||
265 |
|
298 | |||
266 | Then you can do the following:: |
|
299 | Then you can do the following:: | |
267 |
|
300 | |||
@@ -290,9 +323,6 b' class ParallelMagics(Magics):' | |||||
290 | """Enable %autopx mode by saving the original run_cell and installing |
|
323 | """Enable %autopx mode by saving the original run_cell and installing | |
291 | pxrun_cell. |
|
324 | pxrun_cell. | |
292 | """ |
|
325 | """ | |
293 | if self.active_view is None: |
|
|||
294 | raise UsageError(NO_ACTIVE_VIEW) |
|
|||
295 |
|
||||
296 | # override run_cell |
|
326 | # override run_cell | |
297 | self._original_run_cell = self.shell.run_cell |
|
327 | self._original_run_cell = self.shell.run_cell | |
298 | self.shell.run_cell = self.pxrun_cell |
|
328 | self.shell.run_cell = self.pxrun_cell | |
@@ -356,12 +386,12 b' class ParallelMagics(Magics):' | |||||
356 | return False |
|
386 | return False | |
357 | else: |
|
387 | else: | |
358 | try: |
|
388 | try: | |
359 |
result = self. |
|
389 | result = self.view.execute(cell, silent=False, block=False) | |
360 | except: |
|
390 | except: | |
361 | ipself.showtraceback() |
|
391 | ipself.showtraceback() | |
362 | return True |
|
392 | return True | |
363 | else: |
|
393 | else: | |
364 |
if self. |
|
394 | if self.view.block: | |
365 | try: |
|
395 | try: | |
366 | result.get() |
|
396 | result.get() | |
367 | except: |
|
397 | except: | |
@@ -376,15 +406,6 b' class ParallelMagics(Magics):' | |||||
376 | __doc__ = __doc__.format( |
|
406 | __doc__ = __doc__.format( | |
377 | AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__, |
|
407 | AUTOPX_DOC = ' '*8 + ParallelMagics.autopx.__doc__, | |
378 | PX_DOC = ' '*8 + ParallelMagics.px.__doc__, |
|
408 | PX_DOC = ' '*8 + ParallelMagics.px.__doc__, | |
379 | RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__ |
|
409 | RESULT_DOC = ' '*8 + ParallelMagics.result.__doc__, | |
|
410 | CONFIG_DOC = ' '*8 + ParallelMagics.pxconfig.__doc__, | |||
380 | ) |
|
411 | ) | |
381 |
|
||||
382 | _loaded = False |
|
|||
383 |
|
||||
384 |
|
||||
385 | def load_ipython_extension(ip): |
|
|||
386 | """Load the extension in IPython.""" |
|
|||
387 | global _loaded |
|
|||
388 | if not _loaded: |
|
|||
389 | ip.register_magics(ParallelMagics) |
|
|||
390 | _loaded = True |
|
@@ -792,33 +792,37 b' class DirectView(View):' | |||||
792 | return self.client.kill(targets=targets, block=block) |
|
792 | return self.client.kill(targets=targets, block=block) | |
793 |
|
793 | |||
794 | #---------------------------------------- |
|
794 | #---------------------------------------- | |
795 | # activate for %px,%autopx magics |
|
795 | # activate for %px, %autopx, etc. magics | |
796 | #---------------------------------------- |
|
796 | #---------------------------------------- | |
797 | def activate(self): |
|
|||
798 | """Make this `View` active for parallel magic commands. |
|
|||
799 |
|
797 | |||
800 | IPython has a magic command syntax to work with `MultiEngineClient` objects. |
|
798 | def activate(self, suffix=''): | |
801 | In a given IPython session there is a single active one. While |
|
799 | """Activate IPython magics associated with this View | |
802 | there can be many `Views` created and used by the user, |
|
800 | ||
803 | there is only one active one. The active `View` is used whenever |
|
801 | Defines the magics `%px, %autopx, %pxresult, %%px, %pxconfig` | |
804 | the magic commands %px and %autopx are used. |
|
802 | ||
805 |
|
803 | Parameters | ||
806 | The activate() method is called on a given `View` to make it |
|
804 | ---------- | |
807 | active. Once this has been done, the magic commands can be used. |
|
805 | ||
|
806 | suffix: str [default: ''] | |||
|
807 | The suffix, if any, for the magics. This allows you to have | |||
|
808 | multiple views associated with parallel magics at the same time. | |||
|
809 | ||||
|
810 | e.g. ``rc[::2].activate(suffix='_even')`` will give you | |||
|
811 | the magics ``%px_even``, ``%pxresult_even``, etc. for running magics | |||
|
812 | on the even engines. | |||
808 | """ |
|
813 | """ | |
809 |
|
814 | |||
|
815 | from IPython.parallel.client.magics import ParallelMagics | |||
|
816 | ||||
810 | try: |
|
817 | try: | |
811 | # This is injected into __builtins__. |
|
818 | # This is injected into __builtins__. | |
812 | ip = get_ipython() |
|
819 | ip = get_ipython() | |
813 | except NameError: |
|
820 | except NameError: | |
814 |
print "The IPython parallel magics ( |
|
821 | print "The IPython parallel magics (%px, etc.) only work within IPython." | |
815 | else: |
|
822 | return | |
816 | pmagic = ip.magics_manager.registry.get('ParallelMagics') |
|
823 | ||
817 | if pmagic is None: |
|
824 | M = ParallelMagics(ip, self, suffix) | |
818 | ip.magic('load_ext parallelmagic') |
|
825 | ip.magics_manager.register(M) | |
819 | pmagic = ip.magics_manager.registry.get('ParallelMagics') |
|
|||
820 |
|
||||
821 | pmagic.active_view = self |
|
|||
822 |
|
826 | |||
823 |
|
827 | |||
824 | @skip_doctest |
|
828 | @skip_doctest |
@@ -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 | |
@@ -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') |
@@ -301,20 +301,13 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):' | |||||
301 | data = dict(a=111,b=222) |
|
301 | data = dict(a=111,b=222) | |
302 | v.push(data, block=True) |
|
302 | v.push(data, block=True) | |
303 |
|
303 | |||
304 | ip.magic('px a') |
|
304 | for name in ('a', 'b'): | |
305 |
ip.magic('px |
|
305 | 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: |
|
306 | with capture_output() as io: | |
314 |
ip.magic('result |
|
307 | ip.magic('pxresult') | |
315 | output = io.stdout |
|
308 | output = io.stdout | |
316 | msg = "expected %s output to include %s, but got: %s" % \ |
|
309 | msg = "expected %s output to include %s, but got: %s" % \ | |
317 |
('%result |
|
310 | ('%pxresult', str(data[name]), output) | |
318 | self.assertTrue(str(data[name]) in output, msg) |
|
311 | self.assertTrue(str(data[name]) in output, msg) | |
319 |
|
312 | |||
320 | @dec.skipif_not_matplotlib |
|
313 | @dec.skipif_not_matplotlib | |
@@ -336,5 +329,17 b' class TestParallelMagics(ClusterTestCase, ParametricTestCase):' | |||||
336 |
|
329 | |||
337 | self.assertTrue('Out[' in io.stdout, io.stdout) |
|
330 | self.assertTrue('Out[' in io.stdout, io.stdout) | |
338 | self.assertTrue('matplotlib.lines' in io.stdout, io.stdout) |
|
331 | self.assertTrue('matplotlib.lines' in io.stdout, io.stdout) | |
|
332 | ||||
|
333 | def test_pxconfig(self): | |||
|
334 | ip = get_ipython() | |||
|
335 | rc = self.client | |||
|
336 | v = rc.activate(-1, '_tst') | |||
|
337 | self.assertEquals(v.targets, rc.ids[-1]) | |||
|
338 | ip.magic("%pxconfig_tst -t :") | |||
|
339 | self.assertEquals(v.targets, rc.ids) | |||
|
340 | ip.magic("%pxconfig_tst --block") | |||
|
341 | self.assertEquals(v.block, True) | |||
|
342 | ip.magic("%pxconfig_tst --noblock") | |||
|
343 | self.assertEquals(v.block, False) | |||
339 |
|
344 | |||
340 |
|
345 |
General Comments 0
You need to be logged in to leave comments.
Login now