##// END OF EJS Templates
Merge pull request #5766 from minrk/map-length-0...
Thomas Kluyver -
r16513:7aeb0546 merge
parent child Browse files
Show More
@@ -1,285 +1,276 b''
1 """Remote Functions and decorators for Views.
1 """Remote Functions and decorators for Views."""
2
2
3 Authors:
3 # Copyright (c) IPython Development Team.
4
4 # Distributed under the terms of the Modified BSD License.
5 * Brian Granger
6 * Min RK
7 """
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2010-2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
14
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18
5
19 from __future__ import division
6 from __future__ import division
20
7
21 import sys
8 import sys
22 import warnings
9 import warnings
23
10
24 from IPython.external.decorator import decorator
11 from IPython.external.decorator import decorator
25 from IPython.testing.skipdoctest import skip_doctest
12 from IPython.testing.skipdoctest import skip_doctest
26
13
27 from . import map as Map
14 from . import map as Map
28 from .asyncresult import AsyncMapResult
15 from .asyncresult import AsyncMapResult
29
16
30 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
31 # Functions and Decorators
18 # Functions and Decorators
32 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
33
20
34 @skip_doctest
21 @skip_doctest
35 def remote(view, block=None, **flags):
22 def remote(view, block=None, **flags):
36 """Turn a function into a remote function.
23 """Turn a function into a remote function.
37
24
38 This method can be used for map:
25 This method can be used for map:
39
26
40 In [1]: @remote(view,block=True)
27 In [1]: @remote(view,block=True)
41 ...: def func(a):
28 ...: def func(a):
42 ...: pass
29 ...: pass
43 """
30 """
44
31
45 def remote_function(f):
32 def remote_function(f):
46 return RemoteFunction(view, f, block=block, **flags)
33 return RemoteFunction(view, f, block=block, **flags)
47 return remote_function
34 return remote_function
48
35
49 @skip_doctest
36 @skip_doctest
50 def parallel(view, dist='b', block=None, ordered=True, **flags):
37 def parallel(view, dist='b', block=None, ordered=True, **flags):
51 """Turn a function into a parallel remote function.
38 """Turn a function into a parallel remote function.
52
39
53 This method can be used for map:
40 This method can be used for map:
54
41
55 In [1]: @parallel(view, block=True)
42 In [1]: @parallel(view, block=True)
56 ...: def func(a):
43 ...: def func(a):
57 ...: pass
44 ...: pass
58 """
45 """
59
46
60 def parallel_function(f):
47 def parallel_function(f):
61 return ParallelFunction(view, f, dist=dist, block=block, ordered=ordered, **flags)
48 return ParallelFunction(view, f, dist=dist, block=block, ordered=ordered, **flags)
62 return parallel_function
49 return parallel_function
63
50
64 def getname(f):
51 def getname(f):
65 """Get the name of an object.
52 """Get the name of an object.
66
53
67 For use in case of callables that are not functions, and
54 For use in case of callables that are not functions, and
68 thus may not have __name__ defined.
55 thus may not have __name__ defined.
69
56
70 Order: f.__name__ > f.name > str(f)
57 Order: f.__name__ > f.name > str(f)
71 """
58 """
72 try:
59 try:
73 return f.__name__
60 return f.__name__
74 except:
61 except:
75 pass
62 pass
76 try:
63 try:
77 return f.name
64 return f.name
78 except:
65 except:
79 pass
66 pass
80
67
81 return str(f)
68 return str(f)
82
69
83 @decorator
70 @decorator
84 def sync_view_results(f, self, *args, **kwargs):
71 def sync_view_results(f, self, *args, **kwargs):
85 """sync relevant results from self.client to our results attribute.
72 """sync relevant results from self.client to our results attribute.
86
73
87 This is a clone of view.sync_results, but for remote functions
74 This is a clone of view.sync_results, but for remote functions
88 """
75 """
89 view = self.view
76 view = self.view
90 if view._in_sync_results:
77 if view._in_sync_results:
91 return f(self, *args, **kwargs)
78 return f(self, *args, **kwargs)
92 view._in_sync_results = True
79 view._in_sync_results = True
93 try:
80 try:
94 ret = f(self, *args, **kwargs)
81 ret = f(self, *args, **kwargs)
95 finally:
82 finally:
96 view._in_sync_results = False
83 view._in_sync_results = False
97 view._sync_results()
84 view._sync_results()
98 return ret
85 return ret
99
86
100 #--------------------------------------------------------------------------
87 #--------------------------------------------------------------------------
101 # Classes
88 # Classes
102 #--------------------------------------------------------------------------
89 #--------------------------------------------------------------------------
103
90
104 class RemoteFunction(object):
91 class RemoteFunction(object):
105 """Turn an existing function into a remote function.
92 """Turn an existing function into a remote function.
106
93
107 Parameters
94 Parameters
108 ----------
95 ----------
109
96
110 view : View instance
97 view : View instance
111 The view to be used for execution
98 The view to be used for execution
112 f : callable
99 f : callable
113 The function to be wrapped into a remote function
100 The function to be wrapped into a remote function
114 block : bool [default: None]
101 block : bool [default: None]
115 Whether to wait for results or not. The default behavior is
102 Whether to wait for results or not. The default behavior is
116 to use the current `block` attribute of `view`
103 to use the current `block` attribute of `view`
117
104
118 **flags : remaining kwargs are passed to View.temp_flags
105 **flags : remaining kwargs are passed to View.temp_flags
119 """
106 """
120
107
121 view = None # the remote connection
108 view = None # the remote connection
122 func = None # the wrapped function
109 func = None # the wrapped function
123 block = None # whether to block
110 block = None # whether to block
124 flags = None # dict of extra kwargs for temp_flags
111 flags = None # dict of extra kwargs for temp_flags
125
112
126 def __init__(self, view, f, block=None, **flags):
113 def __init__(self, view, f, block=None, **flags):
127 self.view = view
114 self.view = view
128 self.func = f
115 self.func = f
129 self.block=block
116 self.block=block
130 self.flags=flags
117 self.flags=flags
131
118
132 def __call__(self, *args, **kwargs):
119 def __call__(self, *args, **kwargs):
133 block = self.view.block if self.block is None else self.block
120 block = self.view.block if self.block is None else self.block
134 with self.view.temp_flags(block=block, **self.flags):
121 with self.view.temp_flags(block=block, **self.flags):
135 return self.view.apply(self.func, *args, **kwargs)
122 return self.view.apply(self.func, *args, **kwargs)
136
123
137
124
138 class ParallelFunction(RemoteFunction):
125 class ParallelFunction(RemoteFunction):
139 """Class for mapping a function to sequences.
126 """Class for mapping a function to sequences.
140
127
141 This will distribute the sequences according the a mapper, and call
128 This will distribute the sequences according the a mapper, and call
142 the function on each sub-sequence. If called via map, then the function
129 the function on each sub-sequence. If called via map, then the function
143 will be called once on each element, rather that each sub-sequence.
130 will be called once on each element, rather that each sub-sequence.
144
131
145 Parameters
132 Parameters
146 ----------
133 ----------
147
134
148 view : View instance
135 view : View instance
149 The view to be used for execution
136 The view to be used for execution
150 f : callable
137 f : callable
151 The function to be wrapped into a remote function
138 The function to be wrapped into a remote function
152 dist : str [default: 'b']
139 dist : str [default: 'b']
153 The key for which mapObject to use to distribute sequences
140 The key for which mapObject to use to distribute sequences
154 options are:
141 options are:
155
142
156 * 'b' : use contiguous chunks in order
143 * 'b' : use contiguous chunks in order
157 * 'r' : use round-robin striping
144 * 'r' : use round-robin striping
158
145
159 block : bool [default: None]
146 block : bool [default: None]
160 Whether to wait for results or not. The default behavior is
147 Whether to wait for results or not. The default behavior is
161 to use the current `block` attribute of `view`
148 to use the current `block` attribute of `view`
162 chunksize : int or None
149 chunksize : int or None
163 The size of chunk to use when breaking up sequences in a load-balanced manner
150 The size of chunk to use when breaking up sequences in a load-balanced manner
164 ordered : bool [default: True]
151 ordered : bool [default: True]
165 Whether the result should be kept in order. If False,
152 Whether the result should be kept in order. If False,
166 results become available as they arrive, regardless of submission order.
153 results become available as they arrive, regardless of submission order.
167 **flags
154 **flags
168 remaining kwargs are passed to View.temp_flags
155 remaining kwargs are passed to View.temp_flags
169 """
156 """
170
157
171 chunksize = None
158 chunksize = None
172 ordered = None
159 ordered = None
173 mapObject = None
160 mapObject = None
174 _mapping = False
161 _mapping = False
175
162
176 def __init__(self, view, f, dist='b', block=None, chunksize=None, ordered=True, **flags):
163 def __init__(self, view, f, dist='b', block=None, chunksize=None, ordered=True, **flags):
177 super(ParallelFunction, self).__init__(view, f, block=block, **flags)
164 super(ParallelFunction, self).__init__(view, f, block=block, **flags)
178 self.chunksize = chunksize
165 self.chunksize = chunksize
179 self.ordered = ordered
166 self.ordered = ordered
180
167
181 mapClass = Map.dists[dist]
168 mapClass = Map.dists[dist]
182 self.mapObject = mapClass()
169 self.mapObject = mapClass()
183
170
184 @sync_view_results
171 @sync_view_results
185 def __call__(self, *sequences):
172 def __call__(self, *sequences):
186 client = self.view.client
173 client = self.view.client
187
174
188 lens = []
175 lens = []
189 maxlen = minlen = -1
176 maxlen = minlen = -1
190 for i, seq in enumerate(sequences):
177 for i, seq in enumerate(sequences):
191 try:
178 try:
192 n = len(seq)
179 n = len(seq)
193 except Exception:
180 except Exception:
194 seq = list(seq)
181 seq = list(seq)
195 if isinstance(sequences, tuple):
182 if isinstance(sequences, tuple):
196 # can't alter a tuple
183 # can't alter a tuple
197 sequences = list(sequences)
184 sequences = list(sequences)
198 sequences[i] = seq
185 sequences[i] = seq
199 n = len(seq)
186 n = len(seq)
200 if n > maxlen:
187 if n > maxlen:
201 maxlen = n
188 maxlen = n
202 if minlen == -1 or n < minlen:
189 if minlen == -1 or n < minlen:
203 minlen = n
190 minlen = n
204 lens.append(n)
191 lens.append(n)
205
192
193 if maxlen == 0:
194 # nothing to iterate over
195 return []
196
206 # check that the length of sequences match
197 # check that the length of sequences match
207 if not self._mapping and minlen != maxlen:
198 if not self._mapping and minlen != maxlen:
208 msg = 'all sequences must have equal length, but have %s' % lens
199 msg = 'all sequences must have equal length, but have %s' % lens
209 raise ValueError(msg)
200 raise ValueError(msg)
210
201
211 balanced = 'Balanced' in self.view.__class__.__name__
202 balanced = 'Balanced' in self.view.__class__.__name__
212 if balanced:
203 if balanced:
213 if self.chunksize:
204 if self.chunksize:
214 nparts = maxlen // self.chunksize + int(maxlen % self.chunksize > 0)
205 nparts = maxlen // self.chunksize + int(maxlen % self.chunksize > 0)
215 else:
206 else:
216 nparts = maxlen
207 nparts = maxlen
217 targets = [None]*nparts
208 targets = [None]*nparts
218 else:
209 else:
219 if self.chunksize:
210 if self.chunksize:
220 warnings.warn("`chunksize` is ignored unless load balancing", UserWarning)
211 warnings.warn("`chunksize` is ignored unless load balancing", UserWarning)
221 # multiplexed:
212 # multiplexed:
222 targets = self.view.targets
213 targets = self.view.targets
223 # 'all' is lazily evaluated at execution time, which is now:
214 # 'all' is lazily evaluated at execution time, which is now:
224 if targets == 'all':
215 if targets == 'all':
225 targets = client._build_targets(targets)[1]
216 targets = client._build_targets(targets)[1]
226 elif isinstance(targets, int):
217 elif isinstance(targets, int):
227 # single-engine view, targets must be iterable
218 # single-engine view, targets must be iterable
228 targets = [targets]
219 targets = [targets]
229 nparts = len(targets)
220 nparts = len(targets)
230
221
231 msg_ids = []
222 msg_ids = []
232 for index, t in enumerate(targets):
223 for index, t in enumerate(targets):
233 args = []
224 args = []
234 for seq in sequences:
225 for seq in sequences:
235 part = self.mapObject.getPartition(seq, index, nparts, maxlen)
226 part = self.mapObject.getPartition(seq, index, nparts, maxlen)
236 args.append(part)
227 args.append(part)
237
228
238 if sum([len(arg) for arg in args]) == 0:
229 if sum([len(arg) for arg in args]) == 0:
239 continue
230 continue
240
231
241 if self._mapping:
232 if self._mapping:
242 if sys.version_info[0] >= 3:
233 if sys.version_info[0] >= 3:
243 f = lambda f, *sequences: list(map(f, *sequences))
234 f = lambda f, *sequences: list(map(f, *sequences))
244 else:
235 else:
245 f = map
236 f = map
246 args = [self.func] + args
237 args = [self.func] + args
247 else:
238 else:
248 f=self.func
239 f=self.func
249
240
250 view = self.view if balanced else client[t]
241 view = self.view if balanced else client[t]
251 with view.temp_flags(block=False, **self.flags):
242 with view.temp_flags(block=False, **self.flags):
252 ar = view.apply(f, *args)
243 ar = view.apply(f, *args)
253
244
254 msg_ids.extend(ar.msg_ids)
245 msg_ids.extend(ar.msg_ids)
255
246
256 r = AsyncMapResult(self.view.client, msg_ids, self.mapObject,
247 r = AsyncMapResult(self.view.client, msg_ids, self.mapObject,
257 fname=getname(self.func),
248 fname=getname(self.func),
258 ordered=self.ordered
249 ordered=self.ordered
259 )
250 )
260
251
261 if self.block:
252 if self.block:
262 try:
253 try:
263 return r.get()
254 return r.get()
264 except KeyboardInterrupt:
255 except KeyboardInterrupt:
265 return r
256 return r
266 else:
257 else:
267 return r
258 return r
268
259
269 def map(self, *sequences):
260 def map(self, *sequences):
270 """call a function on each element of one or more sequence(s) remotely.
261 """call a function on each element of one or more sequence(s) remotely.
271 This should behave very much like the builtin map, but return an AsyncMapResult
262 This should behave very much like the builtin map, but return an AsyncMapResult
272 if self.block is False.
263 if self.block is False.
273
264
274 That means it can take generators (will be cast to lists locally),
265 That means it can take generators (will be cast to lists locally),
275 and mismatched sequence lengths will be padded with None.
266 and mismatched sequence lengths will be padded with None.
276 """
267 """
277 # set _mapping as a flag for use inside self.__call__
268 # set _mapping as a flag for use inside self.__call__
278 self._mapping = True
269 self._mapping = True
279 try:
270 try:
280 ret = self(*sequences)
271 ret = self(*sequences)
281 finally:
272 finally:
282 self._mapping = False
273 self._mapping = False
283 return ret
274 return ret
284
275
285 __all__ = ['remote', 'parallel', 'RemoteFunction', 'ParallelFunction']
276 __all__ = ['remote', 'parallel', 'RemoteFunction', 'ParallelFunction']
@@ -1,849 +1,842 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """test View objects
2 """test View objects"""
3
3
4 Authors:
4 # Copyright (c) IPython Development Team.
5
5 # Distributed under the terms of the Modified BSD License.
6 * Min RK
7 """
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
14
15 #-------------------------------------------------------------------------------
16 # Imports
17 #-------------------------------------------------------------------------------
18
6
19 import base64
7 import base64
20 import sys
8 import sys
21 import platform
9 import platform
22 import time
10 import time
23 from collections import namedtuple
11 from collections import namedtuple
24 from tempfile import NamedTemporaryFile
12 from tempfile import NamedTemporaryFile
25
13
26 import zmq
14 import zmq
27 from nose.plugins.attrib import attr
15 from nose.plugins.attrib import attr
28
16
29 from IPython.testing import decorators as dec
17 from IPython.testing import decorators as dec
30 from IPython.utils.io import capture_output
18 from IPython.utils.io import capture_output
31 from IPython.utils.py3compat import unicode_type
19 from IPython.utils.py3compat import unicode_type
32
20
33 from IPython import parallel as pmod
21 from IPython import parallel as pmod
34 from IPython.parallel import error
22 from IPython.parallel import error
35 from IPython.parallel import AsyncResult, AsyncHubResult, AsyncMapResult
23 from IPython.parallel import AsyncResult, AsyncHubResult, AsyncMapResult
36 from IPython.parallel.util import interactive
24 from IPython.parallel.util import interactive
37
25
38 from IPython.parallel.tests import add_engines
26 from IPython.parallel.tests import add_engines
39
27
40 from .clienttest import ClusterTestCase, crash, wait, skip_without
28 from .clienttest import ClusterTestCase, crash, wait, skip_without
41
29
42 def setup():
30 def setup():
43 add_engines(3, total=True)
31 add_engines(3, total=True)
44
32
45 point = namedtuple("point", "x y")
33 point = namedtuple("point", "x y")
46
34
47 class TestView(ClusterTestCase):
35 class TestView(ClusterTestCase):
48
36
49 def setUp(self):
37 def setUp(self):
50 # On Win XP, wait for resource cleanup, else parallel test group fails
38 # On Win XP, wait for resource cleanup, else parallel test group fails
51 if platform.system() == "Windows" and platform.win32_ver()[0] == "XP":
39 if platform.system() == "Windows" and platform.win32_ver()[0] == "XP":
52 # 1 sec fails. 1.5 sec seems ok. Using 2 sec for margin of safety
40 # 1 sec fails. 1.5 sec seems ok. Using 2 sec for margin of safety
53 time.sleep(2)
41 time.sleep(2)
54 super(TestView, self).setUp()
42 super(TestView, self).setUp()
55
43
56 @attr('crash')
44 @attr('crash')
57 def test_z_crash_mux(self):
45 def test_z_crash_mux(self):
58 """test graceful handling of engine death (direct)"""
46 """test graceful handling of engine death (direct)"""
59 # self.add_engines(1)
47 # self.add_engines(1)
60 eid = self.client.ids[-1]
48 eid = self.client.ids[-1]
61 ar = self.client[eid].apply_async(crash)
49 ar = self.client[eid].apply_async(crash)
62 self.assertRaisesRemote(error.EngineError, ar.get, 10)
50 self.assertRaisesRemote(error.EngineError, ar.get, 10)
63 eid = ar.engine_id
51 eid = ar.engine_id
64 tic = time.time()
52 tic = time.time()
65 while eid in self.client.ids and time.time()-tic < 5:
53 while eid in self.client.ids and time.time()-tic < 5:
66 time.sleep(.01)
54 time.sleep(.01)
67 self.client.spin()
55 self.client.spin()
68 self.assertFalse(eid in self.client.ids, "Engine should have died")
56 self.assertFalse(eid in self.client.ids, "Engine should have died")
69
57
70 def test_push_pull(self):
58 def test_push_pull(self):
71 """test pushing and pulling"""
59 """test pushing and pulling"""
72 data = dict(a=10, b=1.05, c=list(range(10)), d={'e':(1,2),'f':'hi'})
60 data = dict(a=10, b=1.05, c=list(range(10)), d={'e':(1,2),'f':'hi'})
73 t = self.client.ids[-1]
61 t = self.client.ids[-1]
74 v = self.client[t]
62 v = self.client[t]
75 push = v.push
63 push = v.push
76 pull = v.pull
64 pull = v.pull
77 v.block=True
65 v.block=True
78 nengines = len(self.client)
66 nengines = len(self.client)
79 push({'data':data})
67 push({'data':data})
80 d = pull('data')
68 d = pull('data')
81 self.assertEqual(d, data)
69 self.assertEqual(d, data)
82 self.client[:].push({'data':data})
70 self.client[:].push({'data':data})
83 d = self.client[:].pull('data', block=True)
71 d = self.client[:].pull('data', block=True)
84 self.assertEqual(d, nengines*[data])
72 self.assertEqual(d, nengines*[data])
85 ar = push({'data':data}, block=False)
73 ar = push({'data':data}, block=False)
86 self.assertTrue(isinstance(ar, AsyncResult))
74 self.assertTrue(isinstance(ar, AsyncResult))
87 r = ar.get()
75 r = ar.get()
88 ar = self.client[:].pull('data', block=False)
76 ar = self.client[:].pull('data', block=False)
89 self.assertTrue(isinstance(ar, AsyncResult))
77 self.assertTrue(isinstance(ar, AsyncResult))
90 r = ar.get()
78 r = ar.get()
91 self.assertEqual(r, nengines*[data])
79 self.assertEqual(r, nengines*[data])
92 self.client[:].push(dict(a=10,b=20))
80 self.client[:].push(dict(a=10,b=20))
93 r = self.client[:].pull(('a','b'), block=True)
81 r = self.client[:].pull(('a','b'), block=True)
94 self.assertEqual(r, nengines*[[10,20]])
82 self.assertEqual(r, nengines*[[10,20]])
95
83
96 def test_push_pull_function(self):
84 def test_push_pull_function(self):
97 "test pushing and pulling functions"
85 "test pushing and pulling functions"
98 def testf(x):
86 def testf(x):
99 return 2.0*x
87 return 2.0*x
100
88
101 t = self.client.ids[-1]
89 t = self.client.ids[-1]
102 v = self.client[t]
90 v = self.client[t]
103 v.block=True
91 v.block=True
104 push = v.push
92 push = v.push
105 pull = v.pull
93 pull = v.pull
106 execute = v.execute
94 execute = v.execute
107 push({'testf':testf})
95 push({'testf':testf})
108 r = pull('testf')
96 r = pull('testf')
109 self.assertEqual(r(1.0), testf(1.0))
97 self.assertEqual(r(1.0), testf(1.0))
110 execute('r = testf(10)')
98 execute('r = testf(10)')
111 r = pull('r')
99 r = pull('r')
112 self.assertEqual(r, testf(10))
100 self.assertEqual(r, testf(10))
113 ar = self.client[:].push({'testf':testf}, block=False)
101 ar = self.client[:].push({'testf':testf}, block=False)
114 ar.get()
102 ar.get()
115 ar = self.client[:].pull('testf', block=False)
103 ar = self.client[:].pull('testf', block=False)
116 rlist = ar.get()
104 rlist = ar.get()
117 for r in rlist:
105 for r in rlist:
118 self.assertEqual(r(1.0), testf(1.0))
106 self.assertEqual(r(1.0), testf(1.0))
119 execute("def g(x): return x*x")
107 execute("def g(x): return x*x")
120 r = pull(('testf','g'))
108 r = pull(('testf','g'))
121 self.assertEqual((r[0](10),r[1](10)), (testf(10), 100))
109 self.assertEqual((r[0](10),r[1](10)), (testf(10), 100))
122
110
123 def test_push_function_globals(self):
111 def test_push_function_globals(self):
124 """test that pushed functions have access to globals"""
112 """test that pushed functions have access to globals"""
125 @interactive
113 @interactive
126 def geta():
114 def geta():
127 return a
115 return a
128 # self.add_engines(1)
116 # self.add_engines(1)
129 v = self.client[-1]
117 v = self.client[-1]
130 v.block=True
118 v.block=True
131 v['f'] = geta
119 v['f'] = geta
132 self.assertRaisesRemote(NameError, v.execute, 'b=f()')
120 self.assertRaisesRemote(NameError, v.execute, 'b=f()')
133 v.execute('a=5')
121 v.execute('a=5')
134 v.execute('b=f()')
122 v.execute('b=f()')
135 self.assertEqual(v['b'], 5)
123 self.assertEqual(v['b'], 5)
136
124
137 def test_push_function_defaults(self):
125 def test_push_function_defaults(self):
138 """test that pushed functions preserve default args"""
126 """test that pushed functions preserve default args"""
139 def echo(a=10):
127 def echo(a=10):
140 return a
128 return a
141 v = self.client[-1]
129 v = self.client[-1]
142 v.block=True
130 v.block=True
143 v['f'] = echo
131 v['f'] = echo
144 v.execute('b=f()')
132 v.execute('b=f()')
145 self.assertEqual(v['b'], 10)
133 self.assertEqual(v['b'], 10)
146
134
147 def test_get_result(self):
135 def test_get_result(self):
148 """test getting results from the Hub."""
136 """test getting results from the Hub."""
149 c = pmod.Client(profile='iptest')
137 c = pmod.Client(profile='iptest')
150 # self.add_engines(1)
138 # self.add_engines(1)
151 t = c.ids[-1]
139 t = c.ids[-1]
152 v = c[t]
140 v = c[t]
153 v2 = self.client[t]
141 v2 = self.client[t]
154 ar = v.apply_async(wait, 1)
142 ar = v.apply_async(wait, 1)
155 # give the monitor time to notice the message
143 # give the monitor time to notice the message
156 time.sleep(.25)
144 time.sleep(.25)
157 ahr = v2.get_result(ar.msg_ids[0])
145 ahr = v2.get_result(ar.msg_ids[0])
158 self.assertTrue(isinstance(ahr, AsyncHubResult))
146 self.assertTrue(isinstance(ahr, AsyncHubResult))
159 self.assertEqual(ahr.get(), ar.get())
147 self.assertEqual(ahr.get(), ar.get())
160 ar2 = v2.get_result(ar.msg_ids[0])
148 ar2 = v2.get_result(ar.msg_ids[0])
161 self.assertFalse(isinstance(ar2, AsyncHubResult))
149 self.assertFalse(isinstance(ar2, AsyncHubResult))
162 c.spin()
150 c.spin()
163 c.close()
151 c.close()
164
152
165 def test_run_newline(self):
153 def test_run_newline(self):
166 """test that run appends newline to files"""
154 """test that run appends newline to files"""
167 with NamedTemporaryFile('w', delete=False) as f:
155 with NamedTemporaryFile('w', delete=False) as f:
168 f.write("""def g():
156 f.write("""def g():
169 return 5
157 return 5
170 """)
158 """)
171 v = self.client[-1]
159 v = self.client[-1]
172 v.run(f.name, block=True)
160 v.run(f.name, block=True)
173 self.assertEqual(v.apply_sync(lambda f: f(), pmod.Reference('g')), 5)
161 self.assertEqual(v.apply_sync(lambda f: f(), pmod.Reference('g')), 5)
174
162
175 def test_apply_tracked(self):
163 def test_apply_tracked(self):
176 """test tracking for apply"""
164 """test tracking for apply"""
177 # self.add_engines(1)
165 # self.add_engines(1)
178 t = self.client.ids[-1]
166 t = self.client.ids[-1]
179 v = self.client[t]
167 v = self.client[t]
180 v.block=False
168 v.block=False
181 def echo(n=1024*1024, **kwargs):
169 def echo(n=1024*1024, **kwargs):
182 with v.temp_flags(**kwargs):
170 with v.temp_flags(**kwargs):
183 return v.apply(lambda x: x, 'x'*n)
171 return v.apply(lambda x: x, 'x'*n)
184 ar = echo(1, track=False)
172 ar = echo(1, track=False)
185 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
173 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
186 self.assertTrue(ar.sent)
174 self.assertTrue(ar.sent)
187 ar = echo(track=True)
175 ar = echo(track=True)
188 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
176 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
189 self.assertEqual(ar.sent, ar._tracker.done)
177 self.assertEqual(ar.sent, ar._tracker.done)
190 ar._tracker.wait()
178 ar._tracker.wait()
191 self.assertTrue(ar.sent)
179 self.assertTrue(ar.sent)
192
180
193 def test_push_tracked(self):
181 def test_push_tracked(self):
194 t = self.client.ids[-1]
182 t = self.client.ids[-1]
195 ns = dict(x='x'*1024*1024)
183 ns = dict(x='x'*1024*1024)
196 v = self.client[t]
184 v = self.client[t]
197 ar = v.push(ns, block=False, track=False)
185 ar = v.push(ns, block=False, track=False)
198 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
186 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
199 self.assertTrue(ar.sent)
187 self.assertTrue(ar.sent)
200
188
201 ar = v.push(ns, block=False, track=True)
189 ar = v.push(ns, block=False, track=True)
202 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
190 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
203 ar._tracker.wait()
191 ar._tracker.wait()
204 self.assertEqual(ar.sent, ar._tracker.done)
192 self.assertEqual(ar.sent, ar._tracker.done)
205 self.assertTrue(ar.sent)
193 self.assertTrue(ar.sent)
206 ar.get()
194 ar.get()
207
195
208 def test_scatter_tracked(self):
196 def test_scatter_tracked(self):
209 t = self.client.ids
197 t = self.client.ids
210 x='x'*1024*1024
198 x='x'*1024*1024
211 ar = self.client[t].scatter('x', x, block=False, track=False)
199 ar = self.client[t].scatter('x', x, block=False, track=False)
212 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
200 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
213 self.assertTrue(ar.sent)
201 self.assertTrue(ar.sent)
214
202
215 ar = self.client[t].scatter('x', x, block=False, track=True)
203 ar = self.client[t].scatter('x', x, block=False, track=True)
216 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
204 self.assertTrue(isinstance(ar._tracker, zmq.MessageTracker))
217 self.assertEqual(ar.sent, ar._tracker.done)
205 self.assertEqual(ar.sent, ar._tracker.done)
218 ar._tracker.wait()
206 ar._tracker.wait()
219 self.assertTrue(ar.sent)
207 self.assertTrue(ar.sent)
220 ar.get()
208 ar.get()
221
209
222 def test_remote_reference(self):
210 def test_remote_reference(self):
223 v = self.client[-1]
211 v = self.client[-1]
224 v['a'] = 123
212 v['a'] = 123
225 ra = pmod.Reference('a')
213 ra = pmod.Reference('a')
226 b = v.apply_sync(lambda x: x, ra)
214 b = v.apply_sync(lambda x: x, ra)
227 self.assertEqual(b, 123)
215 self.assertEqual(b, 123)
228
216
229
217
230 def test_scatter_gather(self):
218 def test_scatter_gather(self):
231 view = self.client[:]
219 view = self.client[:]
232 seq1 = list(range(16))
220 seq1 = list(range(16))
233 view.scatter('a', seq1)
221 view.scatter('a', seq1)
234 seq2 = view.gather('a', block=True)
222 seq2 = view.gather('a', block=True)
235 self.assertEqual(seq2, seq1)
223 self.assertEqual(seq2, seq1)
236 self.assertRaisesRemote(NameError, view.gather, 'asdf', block=True)
224 self.assertRaisesRemote(NameError, view.gather, 'asdf', block=True)
237
225
238 @skip_without('numpy')
226 @skip_without('numpy')
239 def test_scatter_gather_numpy(self):
227 def test_scatter_gather_numpy(self):
240 import numpy
228 import numpy
241 from numpy.testing.utils import assert_array_equal
229 from numpy.testing.utils import assert_array_equal
242 view = self.client[:]
230 view = self.client[:]
243 a = numpy.arange(64)
231 a = numpy.arange(64)
244 view.scatter('a', a, block=True)
232 view.scatter('a', a, block=True)
245 b = view.gather('a', block=True)
233 b = view.gather('a', block=True)
246 assert_array_equal(b, a)
234 assert_array_equal(b, a)
247
235
248 def test_scatter_gather_lazy(self):
236 def test_scatter_gather_lazy(self):
249 """scatter/gather with targets='all'"""
237 """scatter/gather with targets='all'"""
250 view = self.client.direct_view(targets='all')
238 view = self.client.direct_view(targets='all')
251 x = list(range(64))
239 x = list(range(64))
252 view.scatter('x', x)
240 view.scatter('x', x)
253 gathered = view.gather('x', block=True)
241 gathered = view.gather('x', block=True)
254 self.assertEqual(gathered, x)
242 self.assertEqual(gathered, x)
255
243
256
244
257 @dec.known_failure_py3
245 @dec.known_failure_py3
258 @skip_without('numpy')
246 @skip_without('numpy')
259 def test_push_numpy_nocopy(self):
247 def test_push_numpy_nocopy(self):
260 import numpy
248 import numpy
261 view = self.client[:]
249 view = self.client[:]
262 a = numpy.arange(64)
250 a = numpy.arange(64)
263 view['A'] = a
251 view['A'] = a
264 @interactive
252 @interactive
265 def check_writeable(x):
253 def check_writeable(x):
266 return x.flags.writeable
254 return x.flags.writeable
267
255
268 for flag in view.apply_sync(check_writeable, pmod.Reference('A')):
256 for flag in view.apply_sync(check_writeable, pmod.Reference('A')):
269 self.assertFalse(flag, "array is writeable, push shouldn't have pickled it")
257 self.assertFalse(flag, "array is writeable, push shouldn't have pickled it")
270
258
271 view.push(dict(B=a))
259 view.push(dict(B=a))
272 for flag in view.apply_sync(check_writeable, pmod.Reference('B')):
260 for flag in view.apply_sync(check_writeable, pmod.Reference('B')):
273 self.assertFalse(flag, "array is writeable, push shouldn't have pickled it")
261 self.assertFalse(flag, "array is writeable, push shouldn't have pickled it")
274
262
275 @skip_without('numpy')
263 @skip_without('numpy')
276 def test_apply_numpy(self):
264 def test_apply_numpy(self):
277 """view.apply(f, ndarray)"""
265 """view.apply(f, ndarray)"""
278 import numpy
266 import numpy
279 from numpy.testing.utils import assert_array_equal
267 from numpy.testing.utils import assert_array_equal
280
268
281 A = numpy.random.random((100,100))
269 A = numpy.random.random((100,100))
282 view = self.client[-1]
270 view = self.client[-1]
283 for dt in [ 'int32', 'uint8', 'float32', 'float64' ]:
271 for dt in [ 'int32', 'uint8', 'float32', 'float64' ]:
284 B = A.astype(dt)
272 B = A.astype(dt)
285 C = view.apply_sync(lambda x:x, B)
273 C = view.apply_sync(lambda x:x, B)
286 assert_array_equal(B,C)
274 assert_array_equal(B,C)
287
275
288 @skip_without('numpy')
276 @skip_without('numpy')
289 def test_apply_numpy_object_dtype(self):
277 def test_apply_numpy_object_dtype(self):
290 """view.apply(f, ndarray) with dtype=object"""
278 """view.apply(f, ndarray) with dtype=object"""
291 import numpy
279 import numpy
292 from numpy.testing.utils import assert_array_equal
280 from numpy.testing.utils import assert_array_equal
293 view = self.client[-1]
281 view = self.client[-1]
294
282
295 A = numpy.array([dict(a=5)])
283 A = numpy.array([dict(a=5)])
296 B = view.apply_sync(lambda x:x, A)
284 B = view.apply_sync(lambda x:x, A)
297 assert_array_equal(A,B)
285 assert_array_equal(A,B)
298
286
299 A = numpy.array([(0, dict(b=10))], dtype=[('i', int), ('o', object)])
287 A = numpy.array([(0, dict(b=10))], dtype=[('i', int), ('o', object)])
300 B = view.apply_sync(lambda x:x, A)
288 B = view.apply_sync(lambda x:x, A)
301 assert_array_equal(A,B)
289 assert_array_equal(A,B)
302
290
303 @skip_without('numpy')
291 @skip_without('numpy')
304 def test_push_pull_recarray(self):
292 def test_push_pull_recarray(self):
305 """push/pull recarrays"""
293 """push/pull recarrays"""
306 import numpy
294 import numpy
307 from numpy.testing.utils import assert_array_equal
295 from numpy.testing.utils import assert_array_equal
308
296
309 view = self.client[-1]
297 view = self.client[-1]
310
298
311 R = numpy.array([
299 R = numpy.array([
312 (1, 'hi', 0.),
300 (1, 'hi', 0.),
313 (2**30, 'there', 2.5),
301 (2**30, 'there', 2.5),
314 (-99999, 'world', -12345.6789),
302 (-99999, 'world', -12345.6789),
315 ], [('n', int), ('s', '|S10'), ('f', float)])
303 ], [('n', int), ('s', '|S10'), ('f', float)])
316
304
317 view['RR'] = R
305 view['RR'] = R
318 R2 = view['RR']
306 R2 = view['RR']
319
307
320 r_dtype, r_shape = view.apply_sync(interactive(lambda : (RR.dtype, RR.shape)))
308 r_dtype, r_shape = view.apply_sync(interactive(lambda : (RR.dtype, RR.shape)))
321 self.assertEqual(r_dtype, R.dtype)
309 self.assertEqual(r_dtype, R.dtype)
322 self.assertEqual(r_shape, R.shape)
310 self.assertEqual(r_shape, R.shape)
323 self.assertEqual(R2.dtype, R.dtype)
311 self.assertEqual(R2.dtype, R.dtype)
324 self.assertEqual(R2.shape, R.shape)
312 self.assertEqual(R2.shape, R.shape)
325 assert_array_equal(R2, R)
313 assert_array_equal(R2, R)
326
314
327 @skip_without('pandas')
315 @skip_without('pandas')
328 def test_push_pull_timeseries(self):
316 def test_push_pull_timeseries(self):
329 """push/pull pandas.TimeSeries"""
317 """push/pull pandas.TimeSeries"""
330 import pandas
318 import pandas
331
319
332 ts = pandas.TimeSeries(list(range(10)))
320 ts = pandas.TimeSeries(list(range(10)))
333
321
334 view = self.client[-1]
322 view = self.client[-1]
335
323
336 view.push(dict(ts=ts), block=True)
324 view.push(dict(ts=ts), block=True)
337 rts = view['ts']
325 rts = view['ts']
338
326
339 self.assertEqual(type(rts), type(ts))
327 self.assertEqual(type(rts), type(ts))
340 self.assertTrue((ts == rts).all())
328 self.assertTrue((ts == rts).all())
341
329
342 def test_map(self):
330 def test_map(self):
343 view = self.client[:]
331 view = self.client[:]
344 def f(x):
332 def f(x):
345 return x**2
333 return x**2
346 data = list(range(16))
334 data = list(range(16))
347 r = view.map_sync(f, data)
335 r = view.map_sync(f, data)
348 self.assertEqual(r, list(map(f, data)))
336 self.assertEqual(r, list(map(f, data)))
349
337
338 def test_map_empty_sequence(self):
339 view = self.client[:]
340 r = view.map_sync(lambda x: x, [])
341 self.assertEqual(r, [])
342
350 def test_map_iterable(self):
343 def test_map_iterable(self):
351 """test map on iterables (direct)"""
344 """test map on iterables (direct)"""
352 view = self.client[:]
345 view = self.client[:]
353 # 101 is prime, so it won't be evenly distributed
346 # 101 is prime, so it won't be evenly distributed
354 arr = range(101)
347 arr = range(101)
355 # ensure it will be an iterator, even in Python 3
348 # ensure it will be an iterator, even in Python 3
356 it = iter(arr)
349 it = iter(arr)
357 r = view.map_sync(lambda x: x, it)
350 r = view.map_sync(lambda x: x, it)
358 self.assertEqual(r, list(arr))
351 self.assertEqual(r, list(arr))
359
352
360 @skip_without('numpy')
353 @skip_without('numpy')
361 def test_map_numpy(self):
354 def test_map_numpy(self):
362 """test map on numpy arrays (direct)"""
355 """test map on numpy arrays (direct)"""
363 import numpy
356 import numpy
364 from numpy.testing.utils import assert_array_equal
357 from numpy.testing.utils import assert_array_equal
365
358
366 view = self.client[:]
359 view = self.client[:]
367 # 101 is prime, so it won't be evenly distributed
360 # 101 is prime, so it won't be evenly distributed
368 arr = numpy.arange(101)
361 arr = numpy.arange(101)
369 r = view.map_sync(lambda x: x, arr)
362 r = view.map_sync(lambda x: x, arr)
370 assert_array_equal(r, arr)
363 assert_array_equal(r, arr)
371
364
372 def test_scatter_gather_nonblocking(self):
365 def test_scatter_gather_nonblocking(self):
373 data = list(range(16))
366 data = list(range(16))
374 view = self.client[:]
367 view = self.client[:]
375 view.scatter('a', data, block=False)
368 view.scatter('a', data, block=False)
376 ar = view.gather('a', block=False)
369 ar = view.gather('a', block=False)
377 self.assertEqual(ar.get(), data)
370 self.assertEqual(ar.get(), data)
378
371
379 @skip_without('numpy')
372 @skip_without('numpy')
380 def test_scatter_gather_numpy_nonblocking(self):
373 def test_scatter_gather_numpy_nonblocking(self):
381 import numpy
374 import numpy
382 from numpy.testing.utils import assert_array_equal
375 from numpy.testing.utils import assert_array_equal
383 a = numpy.arange(64)
376 a = numpy.arange(64)
384 view = self.client[:]
377 view = self.client[:]
385 ar = view.scatter('a', a, block=False)
378 ar = view.scatter('a', a, block=False)
386 self.assertTrue(isinstance(ar, AsyncResult))
379 self.assertTrue(isinstance(ar, AsyncResult))
387 amr = view.gather('a', block=False)
380 amr = view.gather('a', block=False)
388 self.assertTrue(isinstance(amr, AsyncMapResult))
381 self.assertTrue(isinstance(amr, AsyncMapResult))
389 assert_array_equal(amr.get(), a)
382 assert_array_equal(amr.get(), a)
390
383
391 def test_execute(self):
384 def test_execute(self):
392 view = self.client[:]
385 view = self.client[:]
393 # self.client.debug=True
386 # self.client.debug=True
394 execute = view.execute
387 execute = view.execute
395 ar = execute('c=30', block=False)
388 ar = execute('c=30', block=False)
396 self.assertTrue(isinstance(ar, AsyncResult))
389 self.assertTrue(isinstance(ar, AsyncResult))
397 ar = execute('d=[0,1,2]', block=False)
390 ar = execute('d=[0,1,2]', block=False)
398 self.client.wait(ar, 1)
391 self.client.wait(ar, 1)
399 self.assertEqual(len(ar.get()), len(self.client))
392 self.assertEqual(len(ar.get()), len(self.client))
400 for c in view['c']:
393 for c in view['c']:
401 self.assertEqual(c, 30)
394 self.assertEqual(c, 30)
402
395
403 def test_abort(self):
396 def test_abort(self):
404 view = self.client[-1]
397 view = self.client[-1]
405 ar = view.execute('import time; time.sleep(1)', block=False)
398 ar = view.execute('import time; time.sleep(1)', block=False)
406 ar2 = view.apply_async(lambda : 2)
399 ar2 = view.apply_async(lambda : 2)
407 ar3 = view.apply_async(lambda : 3)
400 ar3 = view.apply_async(lambda : 3)
408 view.abort(ar2)
401 view.abort(ar2)
409 view.abort(ar3.msg_ids)
402 view.abort(ar3.msg_ids)
410 self.assertRaises(error.TaskAborted, ar2.get)
403 self.assertRaises(error.TaskAborted, ar2.get)
411 self.assertRaises(error.TaskAborted, ar3.get)
404 self.assertRaises(error.TaskAborted, ar3.get)
412
405
413 def test_abort_all(self):
406 def test_abort_all(self):
414 """view.abort() aborts all outstanding tasks"""
407 """view.abort() aborts all outstanding tasks"""
415 view = self.client[-1]
408 view = self.client[-1]
416 ars = [ view.apply_async(time.sleep, 0.25) for i in range(10) ]
409 ars = [ view.apply_async(time.sleep, 0.25) for i in range(10) ]
417 view.abort()
410 view.abort()
418 view.wait(timeout=5)
411 view.wait(timeout=5)
419 for ar in ars[5:]:
412 for ar in ars[5:]:
420 self.assertRaises(error.TaskAborted, ar.get)
413 self.assertRaises(error.TaskAborted, ar.get)
421
414
422 def test_temp_flags(self):
415 def test_temp_flags(self):
423 view = self.client[-1]
416 view = self.client[-1]
424 view.block=True
417 view.block=True
425 with view.temp_flags(block=False):
418 with view.temp_flags(block=False):
426 self.assertFalse(view.block)
419 self.assertFalse(view.block)
427 self.assertTrue(view.block)
420 self.assertTrue(view.block)
428
421
429 @dec.known_failure_py3
422 @dec.known_failure_py3
430 def test_importer(self):
423 def test_importer(self):
431 view = self.client[-1]
424 view = self.client[-1]
432 view.clear(block=True)
425 view.clear(block=True)
433 with view.importer:
426 with view.importer:
434 import re
427 import re
435
428
436 @interactive
429 @interactive
437 def findall(pat, s):
430 def findall(pat, s):
438 # this globals() step isn't necessary in real code
431 # this globals() step isn't necessary in real code
439 # only to prevent a closure in the test
432 # only to prevent a closure in the test
440 re = globals()['re']
433 re = globals()['re']
441 return re.findall(pat, s)
434 return re.findall(pat, s)
442
435
443 self.assertEqual(view.apply_sync(findall, '\w+', 'hello world'), 'hello world'.split())
436 self.assertEqual(view.apply_sync(findall, '\w+', 'hello world'), 'hello world'.split())
444
437
445 def test_unicode_execute(self):
438 def test_unicode_execute(self):
446 """test executing unicode strings"""
439 """test executing unicode strings"""
447 v = self.client[-1]
440 v = self.client[-1]
448 v.block=True
441 v.block=True
449 if sys.version_info[0] >= 3:
442 if sys.version_info[0] >= 3:
450 code="a='é'"
443 code="a='é'"
451 else:
444 else:
452 code=u"a=u'é'"
445 code=u"a=u'é'"
453 v.execute(code)
446 v.execute(code)
454 self.assertEqual(v['a'], u'é')
447 self.assertEqual(v['a'], u'é')
455
448
456 def test_unicode_apply_result(self):
449 def test_unicode_apply_result(self):
457 """test unicode apply results"""
450 """test unicode apply results"""
458 v = self.client[-1]
451 v = self.client[-1]
459 r = v.apply_sync(lambda : u'é')
452 r = v.apply_sync(lambda : u'é')
460 self.assertEqual(r, u'é')
453 self.assertEqual(r, u'é')
461
454
462 def test_unicode_apply_arg(self):
455 def test_unicode_apply_arg(self):
463 """test passing unicode arguments to apply"""
456 """test passing unicode arguments to apply"""
464 v = self.client[-1]
457 v = self.client[-1]
465
458
466 @interactive
459 @interactive
467 def check_unicode(a, check):
460 def check_unicode(a, check):
468 assert not isinstance(a, bytes), "%r is bytes, not unicode"%a
461 assert not isinstance(a, bytes), "%r is bytes, not unicode"%a
469 assert isinstance(check, bytes), "%r is not bytes"%check
462 assert isinstance(check, bytes), "%r is not bytes"%check
470 assert a.encode('utf8') == check, "%s != %s"%(a,check)
463 assert a.encode('utf8') == check, "%s != %s"%(a,check)
471
464
472 for s in [ u'é', u'ßø®∫',u'asdf' ]:
465 for s in [ u'é', u'ßø®∫',u'asdf' ]:
473 try:
466 try:
474 v.apply_sync(check_unicode, s, s.encode('utf8'))
467 v.apply_sync(check_unicode, s, s.encode('utf8'))
475 except error.RemoteError as e:
468 except error.RemoteError as e:
476 if e.ename == 'AssertionError':
469 if e.ename == 'AssertionError':
477 self.fail(e.evalue)
470 self.fail(e.evalue)
478 else:
471 else:
479 raise e
472 raise e
480
473
481 def test_map_reference(self):
474 def test_map_reference(self):
482 """view.map(<Reference>, *seqs) should work"""
475 """view.map(<Reference>, *seqs) should work"""
483 v = self.client[:]
476 v = self.client[:]
484 v.scatter('n', self.client.ids, flatten=True)
477 v.scatter('n', self.client.ids, flatten=True)
485 v.execute("f = lambda x,y: x*y")
478 v.execute("f = lambda x,y: x*y")
486 rf = pmod.Reference('f')
479 rf = pmod.Reference('f')
487 nlist = list(range(10))
480 nlist = list(range(10))
488 mlist = nlist[::-1]
481 mlist = nlist[::-1]
489 expected = [ m*n for m,n in zip(mlist, nlist) ]
482 expected = [ m*n for m,n in zip(mlist, nlist) ]
490 result = v.map_sync(rf, mlist, nlist)
483 result = v.map_sync(rf, mlist, nlist)
491 self.assertEqual(result, expected)
484 self.assertEqual(result, expected)
492
485
493 def test_apply_reference(self):
486 def test_apply_reference(self):
494 """view.apply(<Reference>, *args) should work"""
487 """view.apply(<Reference>, *args) should work"""
495 v = self.client[:]
488 v = self.client[:]
496 v.scatter('n', self.client.ids, flatten=True)
489 v.scatter('n', self.client.ids, flatten=True)
497 v.execute("f = lambda x: n*x")
490 v.execute("f = lambda x: n*x")
498 rf = pmod.Reference('f')
491 rf = pmod.Reference('f')
499 result = v.apply_sync(rf, 5)
492 result = v.apply_sync(rf, 5)
500 expected = [ 5*id for id in self.client.ids ]
493 expected = [ 5*id for id in self.client.ids ]
501 self.assertEqual(result, expected)
494 self.assertEqual(result, expected)
502
495
503 def test_eval_reference(self):
496 def test_eval_reference(self):
504 v = self.client[self.client.ids[0]]
497 v = self.client[self.client.ids[0]]
505 v['g'] = list(range(5))
498 v['g'] = list(range(5))
506 rg = pmod.Reference('g[0]')
499 rg = pmod.Reference('g[0]')
507 echo = lambda x:x
500 echo = lambda x:x
508 self.assertEqual(v.apply_sync(echo, rg), 0)
501 self.assertEqual(v.apply_sync(echo, rg), 0)
509
502
510 def test_reference_nameerror(self):
503 def test_reference_nameerror(self):
511 v = self.client[self.client.ids[0]]
504 v = self.client[self.client.ids[0]]
512 r = pmod.Reference('elvis_has_left')
505 r = pmod.Reference('elvis_has_left')
513 echo = lambda x:x
506 echo = lambda x:x
514 self.assertRaisesRemote(NameError, v.apply_sync, echo, r)
507 self.assertRaisesRemote(NameError, v.apply_sync, echo, r)
515
508
516 def test_single_engine_map(self):
509 def test_single_engine_map(self):
517 e0 = self.client[self.client.ids[0]]
510 e0 = self.client[self.client.ids[0]]
518 r = list(range(5))
511 r = list(range(5))
519 check = [ -1*i for i in r ]
512 check = [ -1*i for i in r ]
520 result = e0.map_sync(lambda x: -1*x, r)
513 result = e0.map_sync(lambda x: -1*x, r)
521 self.assertEqual(result, check)
514 self.assertEqual(result, check)
522
515
523 def test_len(self):
516 def test_len(self):
524 """len(view) makes sense"""
517 """len(view) makes sense"""
525 e0 = self.client[self.client.ids[0]]
518 e0 = self.client[self.client.ids[0]]
526 self.assertEqual(len(e0), 1)
519 self.assertEqual(len(e0), 1)
527 v = self.client[:]
520 v = self.client[:]
528 self.assertEqual(len(v), len(self.client.ids))
521 self.assertEqual(len(v), len(self.client.ids))
529 v = self.client.direct_view('all')
522 v = self.client.direct_view('all')
530 self.assertEqual(len(v), len(self.client.ids))
523 self.assertEqual(len(v), len(self.client.ids))
531 v = self.client[:2]
524 v = self.client[:2]
532 self.assertEqual(len(v), 2)
525 self.assertEqual(len(v), 2)
533 v = self.client[:1]
526 v = self.client[:1]
534 self.assertEqual(len(v), 1)
527 self.assertEqual(len(v), 1)
535 v = self.client.load_balanced_view()
528 v = self.client.load_balanced_view()
536 self.assertEqual(len(v), len(self.client.ids))
529 self.assertEqual(len(v), len(self.client.ids))
537
530
538
531
539 # begin execute tests
532 # begin execute tests
540
533
541 def test_execute_reply(self):
534 def test_execute_reply(self):
542 e0 = self.client[self.client.ids[0]]
535 e0 = self.client[self.client.ids[0]]
543 e0.block = True
536 e0.block = True
544 ar = e0.execute("5", silent=False)
537 ar = e0.execute("5", silent=False)
545 er = ar.get()
538 er = ar.get()
546 self.assertEqual(str(er), "<ExecuteReply[%i]: 5>" % er.execution_count)
539 self.assertEqual(str(er), "<ExecuteReply[%i]: 5>" % er.execution_count)
547 self.assertEqual(er.pyout['data']['text/plain'], '5')
540 self.assertEqual(er.pyout['data']['text/plain'], '5')
548
541
549 def test_execute_reply_rich(self):
542 def test_execute_reply_rich(self):
550 e0 = self.client[self.client.ids[0]]
543 e0 = self.client[self.client.ids[0]]
551 e0.block = True
544 e0.block = True
552 e0.execute("from IPython.display import Image, HTML")
545 e0.execute("from IPython.display import Image, HTML")
553 ar = e0.execute("Image(data=b'garbage', format='png', width=10)", silent=False)
546 ar = e0.execute("Image(data=b'garbage', format='png', width=10)", silent=False)
554 er = ar.get()
547 er = ar.get()
555 b64data = base64.encodestring(b'garbage').decode('ascii')
548 b64data = base64.encodestring(b'garbage').decode('ascii')
556 self.assertEqual(er._repr_png_(), (b64data, dict(width=10)))
549 self.assertEqual(er._repr_png_(), (b64data, dict(width=10)))
557 ar = e0.execute("HTML('<b>bold</b>')", silent=False)
550 ar = e0.execute("HTML('<b>bold</b>')", silent=False)
558 er = ar.get()
551 er = ar.get()
559 self.assertEqual(er._repr_html_(), "<b>bold</b>")
552 self.assertEqual(er._repr_html_(), "<b>bold</b>")
560
553
561 def test_execute_reply_stdout(self):
554 def test_execute_reply_stdout(self):
562 e0 = self.client[self.client.ids[0]]
555 e0 = self.client[self.client.ids[0]]
563 e0.block = True
556 e0.block = True
564 ar = e0.execute("print (5)", silent=False)
557 ar = e0.execute("print (5)", silent=False)
565 er = ar.get()
558 er = ar.get()
566 self.assertEqual(er.stdout.strip(), '5')
559 self.assertEqual(er.stdout.strip(), '5')
567
560
568 def test_execute_pyout(self):
561 def test_execute_pyout(self):
569 """execute triggers pyout with silent=False"""
562 """execute triggers pyout with silent=False"""
570 view = self.client[:]
563 view = self.client[:]
571 ar = view.execute("5", silent=False, block=True)
564 ar = view.execute("5", silent=False, block=True)
572
565
573 expected = [{'text/plain' : '5'}] * len(view)
566 expected = [{'text/plain' : '5'}] * len(view)
574 mimes = [ out['data'] for out in ar.pyout ]
567 mimes = [ out['data'] for out in ar.pyout ]
575 self.assertEqual(mimes, expected)
568 self.assertEqual(mimes, expected)
576
569
577 def test_execute_silent(self):
570 def test_execute_silent(self):
578 """execute does not trigger pyout with silent=True"""
571 """execute does not trigger pyout with silent=True"""
579 view = self.client[:]
572 view = self.client[:]
580 ar = view.execute("5", block=True)
573 ar = view.execute("5", block=True)
581 expected = [None] * len(view)
574 expected = [None] * len(view)
582 self.assertEqual(ar.pyout, expected)
575 self.assertEqual(ar.pyout, expected)
583
576
584 def test_execute_magic(self):
577 def test_execute_magic(self):
585 """execute accepts IPython commands"""
578 """execute accepts IPython commands"""
586 view = self.client[:]
579 view = self.client[:]
587 view.execute("a = 5")
580 view.execute("a = 5")
588 ar = view.execute("%whos", block=True)
581 ar = view.execute("%whos", block=True)
589 # this will raise, if that failed
582 # this will raise, if that failed
590 ar.get(5)
583 ar.get(5)
591 for stdout in ar.stdout:
584 for stdout in ar.stdout:
592 lines = stdout.splitlines()
585 lines = stdout.splitlines()
593 self.assertEqual(lines[0].split(), ['Variable', 'Type', 'Data/Info'])
586 self.assertEqual(lines[0].split(), ['Variable', 'Type', 'Data/Info'])
594 found = False
587 found = False
595 for line in lines[2:]:
588 for line in lines[2:]:
596 split = line.split()
589 split = line.split()
597 if split == ['a', 'int', '5']:
590 if split == ['a', 'int', '5']:
598 found = True
591 found = True
599 break
592 break
600 self.assertTrue(found, "whos output wrong: %s" % stdout)
593 self.assertTrue(found, "whos output wrong: %s" % stdout)
601
594
602 def test_execute_displaypub(self):
595 def test_execute_displaypub(self):
603 """execute tracks display_pub output"""
596 """execute tracks display_pub output"""
604 view = self.client[:]
597 view = self.client[:]
605 view.execute("from IPython.core.display import *")
598 view.execute("from IPython.core.display import *")
606 ar = view.execute("[ display(i) for i in range(5) ]", block=True)
599 ar = view.execute("[ display(i) for i in range(5) ]", block=True)
607
600
608 expected = [ {u'text/plain' : unicode_type(j)} for j in range(5) ]
601 expected = [ {u'text/plain' : unicode_type(j)} for j in range(5) ]
609 for outputs in ar.outputs:
602 for outputs in ar.outputs:
610 mimes = [ out['data'] for out in outputs ]
603 mimes = [ out['data'] for out in outputs ]
611 self.assertEqual(mimes, expected)
604 self.assertEqual(mimes, expected)
612
605
613 def test_apply_displaypub(self):
606 def test_apply_displaypub(self):
614 """apply tracks display_pub output"""
607 """apply tracks display_pub output"""
615 view = self.client[:]
608 view = self.client[:]
616 view.execute("from IPython.core.display import *")
609 view.execute("from IPython.core.display import *")
617
610
618 @interactive
611 @interactive
619 def publish():
612 def publish():
620 [ display(i) for i in range(5) ]
613 [ display(i) for i in range(5) ]
621
614
622 ar = view.apply_async(publish)
615 ar = view.apply_async(publish)
623 ar.get(5)
616 ar.get(5)
624 expected = [ {u'text/plain' : unicode_type(j)} for j in range(5) ]
617 expected = [ {u'text/plain' : unicode_type(j)} for j in range(5) ]
625 for outputs in ar.outputs:
618 for outputs in ar.outputs:
626 mimes = [ out['data'] for out in outputs ]
619 mimes = [ out['data'] for out in outputs ]
627 self.assertEqual(mimes, expected)
620 self.assertEqual(mimes, expected)
628
621
629 def test_execute_raises(self):
622 def test_execute_raises(self):
630 """exceptions in execute requests raise appropriately"""
623 """exceptions in execute requests raise appropriately"""
631 view = self.client[-1]
624 view = self.client[-1]
632 ar = view.execute("1/0")
625 ar = view.execute("1/0")
633 self.assertRaisesRemote(ZeroDivisionError, ar.get, 2)
626 self.assertRaisesRemote(ZeroDivisionError, ar.get, 2)
634
627
635 def test_remoteerror_render_exception(self):
628 def test_remoteerror_render_exception(self):
636 """RemoteErrors get nice tracebacks"""
629 """RemoteErrors get nice tracebacks"""
637 view = self.client[-1]
630 view = self.client[-1]
638 ar = view.execute("1/0")
631 ar = view.execute("1/0")
639 ip = get_ipython()
632 ip = get_ipython()
640 ip.user_ns['ar'] = ar
633 ip.user_ns['ar'] = ar
641 with capture_output() as io:
634 with capture_output() as io:
642 ip.run_cell("ar.get(2)")
635 ip.run_cell("ar.get(2)")
643
636
644 self.assertTrue('ZeroDivisionError' in io.stdout, io.stdout)
637 self.assertTrue('ZeroDivisionError' in io.stdout, io.stdout)
645
638
646 def test_compositeerror_render_exception(self):
639 def test_compositeerror_render_exception(self):
647 """CompositeErrors get nice tracebacks"""
640 """CompositeErrors get nice tracebacks"""
648 view = self.client[:]
641 view = self.client[:]
649 ar = view.execute("1/0")
642 ar = view.execute("1/0")
650 ip = get_ipython()
643 ip = get_ipython()
651 ip.user_ns['ar'] = ar
644 ip.user_ns['ar'] = ar
652
645
653 with capture_output() as io:
646 with capture_output() as io:
654 ip.run_cell("ar.get(2)")
647 ip.run_cell("ar.get(2)")
655
648
656 count = min(error.CompositeError.tb_limit, len(view))
649 count = min(error.CompositeError.tb_limit, len(view))
657
650
658 self.assertEqual(io.stdout.count('ZeroDivisionError'), count * 2, io.stdout)
651 self.assertEqual(io.stdout.count('ZeroDivisionError'), count * 2, io.stdout)
659 self.assertEqual(io.stdout.count('by zero'), count, io.stdout)
652 self.assertEqual(io.stdout.count('by zero'), count, io.stdout)
660 self.assertEqual(io.stdout.count(':execute'), count, io.stdout)
653 self.assertEqual(io.stdout.count(':execute'), count, io.stdout)
661
654
662 def test_compositeerror_truncate(self):
655 def test_compositeerror_truncate(self):
663 """Truncate CompositeErrors with many exceptions"""
656 """Truncate CompositeErrors with many exceptions"""
664 view = self.client[:]
657 view = self.client[:]
665 msg_ids = []
658 msg_ids = []
666 for i in range(10):
659 for i in range(10):
667 ar = view.execute("1/0")
660 ar = view.execute("1/0")
668 msg_ids.extend(ar.msg_ids)
661 msg_ids.extend(ar.msg_ids)
669
662
670 ar = self.client.get_result(msg_ids)
663 ar = self.client.get_result(msg_ids)
671 try:
664 try:
672 ar.get()
665 ar.get()
673 except error.CompositeError as _e:
666 except error.CompositeError as _e:
674 e = _e
667 e = _e
675 else:
668 else:
676 self.fail("Should have raised CompositeError")
669 self.fail("Should have raised CompositeError")
677
670
678 lines = e.render_traceback()
671 lines = e.render_traceback()
679 with capture_output() as io:
672 with capture_output() as io:
680 e.print_traceback()
673 e.print_traceback()
681
674
682 self.assertTrue("more exceptions" in lines[-1])
675 self.assertTrue("more exceptions" in lines[-1])
683 count = e.tb_limit
676 count = e.tb_limit
684
677
685 self.assertEqual(io.stdout.count('ZeroDivisionError'), 2 * count, io.stdout)
678 self.assertEqual(io.stdout.count('ZeroDivisionError'), 2 * count, io.stdout)
686 self.assertEqual(io.stdout.count('by zero'), count, io.stdout)
679 self.assertEqual(io.stdout.count('by zero'), count, io.stdout)
687 self.assertEqual(io.stdout.count(':execute'), count, io.stdout)
680 self.assertEqual(io.stdout.count(':execute'), count, io.stdout)
688
681
689 @dec.skipif_not_matplotlib
682 @dec.skipif_not_matplotlib
690 def test_magic_pylab(self):
683 def test_magic_pylab(self):
691 """%pylab works on engines"""
684 """%pylab works on engines"""
692 view = self.client[-1]
685 view = self.client[-1]
693 ar = view.execute("%pylab inline")
686 ar = view.execute("%pylab inline")
694 # at least check if this raised:
687 # at least check if this raised:
695 reply = ar.get(5)
688 reply = ar.get(5)
696 # include imports, in case user config
689 # include imports, in case user config
697 ar = view.execute("plot(rand(100))", silent=False)
690 ar = view.execute("plot(rand(100))", silent=False)
698 reply = ar.get(5)
691 reply = ar.get(5)
699 self.assertEqual(len(reply.outputs), 1)
692 self.assertEqual(len(reply.outputs), 1)
700 output = reply.outputs[0]
693 output = reply.outputs[0]
701 self.assertTrue("data" in output)
694 self.assertTrue("data" in output)
702 data = output['data']
695 data = output['data']
703 self.assertTrue("image/png" in data)
696 self.assertTrue("image/png" in data)
704
697
705 def test_func_default_func(self):
698 def test_func_default_func(self):
706 """interactively defined function as apply func default"""
699 """interactively defined function as apply func default"""
707 def foo():
700 def foo():
708 return 'foo'
701 return 'foo'
709
702
710 def bar(f=foo):
703 def bar(f=foo):
711 return f()
704 return f()
712
705
713 view = self.client[-1]
706 view = self.client[-1]
714 ar = view.apply_async(bar)
707 ar = view.apply_async(bar)
715 r = ar.get(10)
708 r = ar.get(10)
716 self.assertEqual(r, 'foo')
709 self.assertEqual(r, 'foo')
717 def test_data_pub_single(self):
710 def test_data_pub_single(self):
718 view = self.client[-1]
711 view = self.client[-1]
719 ar = view.execute('\n'.join([
712 ar = view.execute('\n'.join([
720 'from IPython.kernel.zmq.datapub import publish_data',
713 'from IPython.kernel.zmq.datapub import publish_data',
721 'for i in range(5):',
714 'for i in range(5):',
722 ' publish_data(dict(i=i))'
715 ' publish_data(dict(i=i))'
723 ]), block=False)
716 ]), block=False)
724 self.assertTrue(isinstance(ar.data, dict))
717 self.assertTrue(isinstance(ar.data, dict))
725 ar.get(5)
718 ar.get(5)
726 self.assertEqual(ar.data, dict(i=4))
719 self.assertEqual(ar.data, dict(i=4))
727
720
728 def test_data_pub(self):
721 def test_data_pub(self):
729 view = self.client[:]
722 view = self.client[:]
730 ar = view.execute('\n'.join([
723 ar = view.execute('\n'.join([
731 'from IPython.kernel.zmq.datapub import publish_data',
724 'from IPython.kernel.zmq.datapub import publish_data',
732 'for i in range(5):',
725 'for i in range(5):',
733 ' publish_data(dict(i=i))'
726 ' publish_data(dict(i=i))'
734 ]), block=False)
727 ]), block=False)
735 self.assertTrue(all(isinstance(d, dict) for d in ar.data))
728 self.assertTrue(all(isinstance(d, dict) for d in ar.data))
736 ar.get(5)
729 ar.get(5)
737 self.assertEqual(ar.data, [dict(i=4)] * len(ar))
730 self.assertEqual(ar.data, [dict(i=4)] * len(ar))
738
731
739 def test_can_list_arg(self):
732 def test_can_list_arg(self):
740 """args in lists are canned"""
733 """args in lists are canned"""
741 view = self.client[-1]
734 view = self.client[-1]
742 view['a'] = 128
735 view['a'] = 128
743 rA = pmod.Reference('a')
736 rA = pmod.Reference('a')
744 ar = view.apply_async(lambda x: x, [rA])
737 ar = view.apply_async(lambda x: x, [rA])
745 r = ar.get(5)
738 r = ar.get(5)
746 self.assertEqual(r, [128])
739 self.assertEqual(r, [128])
747
740
748 def test_can_dict_arg(self):
741 def test_can_dict_arg(self):
749 """args in dicts are canned"""
742 """args in dicts are canned"""
750 view = self.client[-1]
743 view = self.client[-1]
751 view['a'] = 128
744 view['a'] = 128
752 rA = pmod.Reference('a')
745 rA = pmod.Reference('a')
753 ar = view.apply_async(lambda x: x, dict(foo=rA))
746 ar = view.apply_async(lambda x: x, dict(foo=rA))
754 r = ar.get(5)
747 r = ar.get(5)
755 self.assertEqual(r, dict(foo=128))
748 self.assertEqual(r, dict(foo=128))
756
749
757 def test_can_list_kwarg(self):
750 def test_can_list_kwarg(self):
758 """kwargs in lists are canned"""
751 """kwargs in lists are canned"""
759 view = self.client[-1]
752 view = self.client[-1]
760 view['a'] = 128
753 view['a'] = 128
761 rA = pmod.Reference('a')
754 rA = pmod.Reference('a')
762 ar = view.apply_async(lambda x=5: x, x=[rA])
755 ar = view.apply_async(lambda x=5: x, x=[rA])
763 r = ar.get(5)
756 r = ar.get(5)
764 self.assertEqual(r, [128])
757 self.assertEqual(r, [128])
765
758
766 def test_can_dict_kwarg(self):
759 def test_can_dict_kwarg(self):
767 """kwargs in dicts are canned"""
760 """kwargs in dicts are canned"""
768 view = self.client[-1]
761 view = self.client[-1]
769 view['a'] = 128
762 view['a'] = 128
770 rA = pmod.Reference('a')
763 rA = pmod.Reference('a')
771 ar = view.apply_async(lambda x=5: x, dict(foo=rA))
764 ar = view.apply_async(lambda x=5: x, dict(foo=rA))
772 r = ar.get(5)
765 r = ar.get(5)
773 self.assertEqual(r, dict(foo=128))
766 self.assertEqual(r, dict(foo=128))
774
767
775 def test_map_ref(self):
768 def test_map_ref(self):
776 """view.map works with references"""
769 """view.map works with references"""
777 view = self.client[:]
770 view = self.client[:]
778 ranks = sorted(self.client.ids)
771 ranks = sorted(self.client.ids)
779 view.scatter('rank', ranks, flatten=True)
772 view.scatter('rank', ranks, flatten=True)
780 rrank = pmod.Reference('rank')
773 rrank = pmod.Reference('rank')
781
774
782 amr = view.map_async(lambda x: x*2, [rrank] * len(view))
775 amr = view.map_async(lambda x: x*2, [rrank] * len(view))
783 drank = amr.get(5)
776 drank = amr.get(5)
784 self.assertEqual(drank, [ r*2 for r in ranks ])
777 self.assertEqual(drank, [ r*2 for r in ranks ])
785
778
786 def test_nested_getitem_setitem(self):
779 def test_nested_getitem_setitem(self):
787 """get and set with view['a.b']"""
780 """get and set with view['a.b']"""
788 view = self.client[-1]
781 view = self.client[-1]
789 view.execute('\n'.join([
782 view.execute('\n'.join([
790 'class A(object): pass',
783 'class A(object): pass',
791 'a = A()',
784 'a = A()',
792 'a.b = 128',
785 'a.b = 128',
793 ]), block=True)
786 ]), block=True)
794 ra = pmod.Reference('a')
787 ra = pmod.Reference('a')
795
788
796 r = view.apply_sync(lambda x: x.b, ra)
789 r = view.apply_sync(lambda x: x.b, ra)
797 self.assertEqual(r, 128)
790 self.assertEqual(r, 128)
798 self.assertEqual(view['a.b'], 128)
791 self.assertEqual(view['a.b'], 128)
799
792
800 view['a.b'] = 0
793 view['a.b'] = 0
801
794
802 r = view.apply_sync(lambda x: x.b, ra)
795 r = view.apply_sync(lambda x: x.b, ra)
803 self.assertEqual(r, 0)
796 self.assertEqual(r, 0)
804 self.assertEqual(view['a.b'], 0)
797 self.assertEqual(view['a.b'], 0)
805
798
806 def test_return_namedtuple(self):
799 def test_return_namedtuple(self):
807 def namedtuplify(x, y):
800 def namedtuplify(x, y):
808 from IPython.parallel.tests.test_view import point
801 from IPython.parallel.tests.test_view import point
809 return point(x, y)
802 return point(x, y)
810
803
811 view = self.client[-1]
804 view = self.client[-1]
812 p = view.apply_sync(namedtuplify, 1, 2)
805 p = view.apply_sync(namedtuplify, 1, 2)
813 self.assertEqual(p.x, 1)
806 self.assertEqual(p.x, 1)
814 self.assertEqual(p.y, 2)
807 self.assertEqual(p.y, 2)
815
808
816 def test_apply_namedtuple(self):
809 def test_apply_namedtuple(self):
817 def echoxy(p):
810 def echoxy(p):
818 return p.y, p.x
811 return p.y, p.x
819
812
820 view = self.client[-1]
813 view = self.client[-1]
821 tup = view.apply_sync(echoxy, point(1, 2))
814 tup = view.apply_sync(echoxy, point(1, 2))
822 self.assertEqual(tup, (2,1))
815 self.assertEqual(tup, (2,1))
823
816
824 def test_sync_imports(self):
817 def test_sync_imports(self):
825 view = self.client[-1]
818 view = self.client[-1]
826 with capture_output() as io:
819 with capture_output() as io:
827 with view.sync_imports():
820 with view.sync_imports():
828 import IPython
821 import IPython
829 self.assertIn("IPython", io.stdout)
822 self.assertIn("IPython", io.stdout)
830
823
831 @interactive
824 @interactive
832 def find_ipython():
825 def find_ipython():
833 return 'IPython' in globals()
826 return 'IPython' in globals()
834
827
835 assert view.apply_sync(find_ipython)
828 assert view.apply_sync(find_ipython)
836
829
837 def test_sync_imports_quiet(self):
830 def test_sync_imports_quiet(self):
838 view = self.client[-1]
831 view = self.client[-1]
839 with capture_output() as io:
832 with capture_output() as io:
840 with view.sync_imports(quiet=True):
833 with view.sync_imports(quiet=True):
841 import IPython
834 import IPython
842 self.assertEqual(io.stdout, '')
835 self.assertEqual(io.stdout, '')
843
836
844 @interactive
837 @interactive
845 def find_ipython():
838 def find_ipython():
846 return 'IPython' in globals()
839 return 'IPython' in globals()
847
840
848 assert view.apply_sync(find_ipython)
841 assert view.apply_sync(find_ipython)
849
842
General Comments 0
You need to be logged in to leave comments. Login now