##// END OF EJS Templates
Fixing small bugs for the release....
Brian Granger -
Show More
@@ -1,91 +1,92 b''
1 1 # encoding: utf-8
2 2 """This file contains unittests for the
3 3 IPython.frontend.cocoa.cocoa_frontend module.
4 4 """
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #---------------------------------------------------------------------------
8 8 # Copyright (C) 2005 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #---------------------------------------------------------------------------
13 13
14 14 #---------------------------------------------------------------------------
15 15 # Imports
16 16 #---------------------------------------------------------------------------
17 17 from IPython.kernel.core.interpreter import Interpreter
18 18 import IPython.kernel.engineservice as es
19 19 from IPython.testing.util import DeferredTestCase
20 20 from twisted.internet.defer import succeed
21 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
22
23 from Foundation import NSMakeRect
24 from AppKit import NSTextView, NSScrollView
25 21
26 class TestIPythonCocoaControler(DeferredTestCase):
27 """Tests for IPythonCocoaController"""
22 try:
23 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
24 from Foundation import NSMakeRect
25 from AppKit import NSTextView, NSScrollView
26 except ImportError:
27 class TestIPythonCocoaControler(DeferredTestCase):
28 """Tests for IPythonCocoaController"""
28 29
29 def setUp(self):
30 self.controller = IPythonCocoaController.alloc().init()
31 self.engine = es.EngineService()
32 self.engine.startService()
30 def setUp(self):
31 self.controller = IPythonCocoaController.alloc().init()
32 self.engine = es.EngineService()
33 self.engine.startService()
33 34
34 35
35 def tearDown(self):
36 self.controller = None
37 self.engine.stopService()
36 def tearDown(self):
37 self.controller = None
38 self.engine.stopService()
38 39
39 def testControllerExecutesCode(self):
40 code ="""5+5"""
41 expected = Interpreter().execute(code)
42 del expected['number']
43 def removeNumberAndID(result):
44 del result['number']
45 del result['id']
46 return result
47 self.assertDeferredEquals(
48 self.controller.execute(code).addCallback(removeNumberAndID),
49 expected)
40 def testControllerExecutesCode(self):
41 code ="""5+5"""
42 expected = Interpreter().execute(code)
43 del expected['number']
44 def removeNumberAndID(result):
45 del result['number']
46 del result['id']
47 return result
48 self.assertDeferredEquals(
49 self.controller.execute(code).addCallback(removeNumberAndID),
50 expected)
50 51
51 def testControllerMirrorsUserNSWithValuesAsStrings(self):
52 code = """userns1=1;userns2=2"""
53 def testControllerUserNS(result):
54 self.assertEquals(self.controller.userNS['userns1'], 1)
55 self.assertEquals(self.controller.userNS['userns2'], 2)
52 def testControllerMirrorsUserNSWithValuesAsStrings(self):
53 code = """userns1=1;userns2=2"""
54 def testControllerUserNS(result):
55 self.assertEquals(self.controller.userNS['userns1'], 1)
56 self.assertEquals(self.controller.userNS['userns2'], 2)
56 57
57 self.controller.execute(code).addCallback(testControllerUserNS)
58 self.controller.execute(code).addCallback(testControllerUserNS)
58 59
59 60
60 def testControllerInstantiatesIEngine(self):
61 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
61 def testControllerInstantiatesIEngine(self):
62 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
62 63
63 def testControllerCompletesToken(self):
64 code = """longNameVariable=10"""
65 def testCompletes(result):
66 self.assert_("longNameVariable" in result)
64 def testControllerCompletesToken(self):
65 code = """longNameVariable=10"""
66 def testCompletes(result):
67 self.assert_("longNameVariable" in result)
67 68
68 def testCompleteToken(result):
69 self.controller.complete("longNa").addCallback(testCompletes)
69 def testCompleteToken(result):
70 self.controller.complete("longNa").addCallback(testCompletes)
70 71
71 self.controller.execute(code).addCallback(testCompletes)
72 self.controller.execute(code).addCallback(testCompletes)
72 73
73 74
74 def testCurrentIndent(self):
75 """test that current_indent_string returns current indent or None.
76 Uses _indent_for_block for direct unit testing.
77 """
75 def testCurrentIndent(self):
76 """test that current_indent_string returns current indent or None.
77 Uses _indent_for_block for direct unit testing.
78 """
78 79
79 self.controller.tabUsesSpaces = True
80 self.assert_(self.controller._indent_for_block("""a=3""") == None)
81 self.assert_(self.controller._indent_for_block("") == None)
82 block = """def test():\n a=3"""
83 self.assert_(self.controller._indent_for_block(block) == \
84 ' ' * self.controller.tabSpaces)
80 self.controller.tabUsesSpaces = True
81 self.assert_(self.controller._indent_for_block("""a=3""") == None)
82 self.assert_(self.controller._indent_for_block("") == None)
83 block = """def test():\n a=3"""
84 self.assert_(self.controller._indent_for_block(block) == \
85 ' ' * self.controller.tabSpaces)
85 86
86 block = """if(True):\n%sif(False):\n%spass""" % \
87 (' '*self.controller.tabSpaces,
88 2*' '*self.controller.tabSpaces)
89 self.assert_(self.controller._indent_for_block(block) == \
90 2*(' '*self.controller.tabSpaces))
87 block = """if(True):\n%sif(False):\n%spass""" % \
88 (' '*self.controller.tabSpaces,
89 2*' '*self.controller.tabSpaces)
90 self.assert_(self.controller._indent_for_block(block) == \
91 2*(' '*self.controller.tabSpaces))
91 92
@@ -1,828 +1,827 b''
1 1 # encoding: utf-8
2 2
3 3 """"""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 7 #-------------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 12 #-------------------------------------------------------------------------------
13 13
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 18 from twisted.internet import defer
19 19
20 20 from IPython.kernel import engineservice as es
21 21 from IPython.kernel import multiengine as me
22 22 from IPython.kernel import newserialized
23 from IPython.kernel.error import NotDefined
24 23 from IPython.testing import util
25 24 from IPython.testing.parametric import parametric, Parametric
26 25 from IPython.kernel import newserialized
27 26 from IPython.kernel.util import printer
28 27 from IPython.kernel.error import (InvalidEngineID,
29 28 NoEnginesRegistered,
30 29 CompositeError,
31 30 InvalidDeferredID)
32 31 from IPython.kernel.tests.engineservicetest import validCommands, invalidCommands
33 32 from IPython.kernel.core.interpreter import Interpreter
34 33
35 34
36 35 #-------------------------------------------------------------------------------
37 36 # Base classes and utilities
38 37 #-------------------------------------------------------------------------------
39 38
40 39 class IMultiEngineBaseTestCase(object):
41 40 """Basic utilities for working with multiengine tests.
42 41
43 42 Some subclass should define:
44 43
45 44 * self.multiengine
46 45 * self.engines to keep track of engines for clean up"""
47 46
48 47 def createShell(self):
49 48 return Interpreter()
50 49
51 50 def addEngine(self, n=1):
52 51 for i in range(n):
53 52 e = es.EngineService()
54 53 e.startService()
55 54 regDict = self.controller.register_engine(es.QueuedEngine(e), None)
56 55 e.id = regDict['id']
57 56 self.engines.append(e)
58 57
59 58
60 59 def testf(x):
61 60 return 2.0*x
62 61
63 62
64 63 globala = 99
65 64
66 65
67 66 def testg(x):
68 67 return globala*x
69 68
70 69
71 70 def isdid(did):
72 71 if not isinstance(did, str):
73 72 return False
74 73 if not len(did)==40:
75 74 return False
76 75 return True
77 76
78 77
79 78 def _raise_it(f):
80 79 try:
81 80 f.raiseException()
82 81 except CompositeError, e:
83 82 e.raise_exception()
84 83
85 84 #-------------------------------------------------------------------------------
86 85 # IMultiEngineTestCase
87 86 #-------------------------------------------------------------------------------
88 87
89 88 class IMultiEngineTestCase(IMultiEngineBaseTestCase):
90 89 """A test for any object that implements IEngineMultiplexer.
91 90
92 91 self.multiengine must be defined and implement IEngineMultiplexer.
93 92 """
94 93
95 94 def testIMultiEngineInterface(self):
96 95 """Does self.engine claim to implement IEngineCore?"""
97 96 self.assert_(me.IEngineMultiplexer.providedBy(self.multiengine))
98 97 self.assert_(me.IMultiEngine.providedBy(self.multiengine))
99 98
100 99 def testIEngineMultiplexerInterfaceMethods(self):
101 100 """Does self.engine have the methods and attributes in IEngineCore."""
102 101 for m in list(me.IEngineMultiplexer):
103 102 self.assert_(hasattr(self.multiengine, m))
104 103
105 104 def testIEngineMultiplexerDeferreds(self):
106 105 self.addEngine(1)
107 106 d= self.multiengine.execute('a=5', targets=0)
108 107 d.addCallback(lambda _: self.multiengine.push(dict(a=5),targets=0))
109 108 d.addCallback(lambda _: self.multiengine.push(dict(a=5, b='asdf', c=[1,2,3]),targets=0))
110 109 d.addCallback(lambda _: self.multiengine.pull(('a','b','c'),targets=0))
111 110 d.addCallback(lambda _: self.multiengine.get_result(targets=0))
112 111 d.addCallback(lambda _: self.multiengine.reset(targets=0))
113 112 d.addCallback(lambda _: self.multiengine.keys(targets=0))
114 113 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)),targets=0))
115 114 d.addCallback(lambda _: self.multiengine.pull_serialized('a',targets=0))
116 115 d.addCallback(lambda _: self.multiengine.clear_queue(targets=0))
117 116 d.addCallback(lambda _: self.multiengine.queue_status(targets=0))
118 117 return d
119 118
120 119 def testInvalidEngineID(self):
121 120 self.addEngine(1)
122 121 badID = 100
123 122 d = self.multiengine.execute('a=5', targets=badID)
124 123 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
125 124 d.addCallback(lambda _: self.multiengine.push(dict(a=5), targets=badID))
126 125 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
127 126 d.addCallback(lambda _: self.multiengine.pull('a', targets=badID))
128 127 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
129 128 d.addCallback(lambda _: self.multiengine.reset(targets=badID))
130 129 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
131 130 d.addCallback(lambda _: self.multiengine.keys(targets=badID))
132 131 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
133 132 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)), targets=badID))
134 133 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
135 134 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=badID))
136 135 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
137 136 d.addCallback(lambda _: self.multiengine.queue_status(targets=badID))
138 137 d.addErrback(lambda f: self.assertRaises(InvalidEngineID, f.raiseException))
139 138 return d
140 139
141 140 def testNoEnginesRegistered(self):
142 141 badID = 'all'
143 142 d= self.multiengine.execute('a=5', targets=badID)
144 143 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
145 144 d.addCallback(lambda _: self.multiengine.push(dict(a=5), targets=badID))
146 145 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
147 146 d.addCallback(lambda _: self.multiengine.pull('a', targets=badID))
148 147 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
149 148 d.addCallback(lambda _: self.multiengine.get_result(targets=badID))
150 149 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
151 150 d.addCallback(lambda _: self.multiengine.reset(targets=badID))
152 151 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
153 152 d.addCallback(lambda _: self.multiengine.keys(targets=badID))
154 153 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
155 154 d.addCallback(lambda _: self.multiengine.push_serialized(dict(a=newserialized.serialize(10)), targets=badID))
156 155 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
157 156 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=badID))
158 157 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
159 158 d.addCallback(lambda _: self.multiengine.queue_status(targets=badID))
160 159 d.addErrback(lambda f: self.assertRaises(NoEnginesRegistered, f.raiseException))
161 160 return d
162 161
163 162 def runExecuteAll(self, d, cmd, shell):
164 163 actual = shell.execute(cmd)
165 164 d.addCallback(lambda _: self.multiengine.execute(cmd))
166 165 def compare(result):
167 166 for r in result:
168 167 actual['id'] = r['id']
169 168 self.assertEquals(r, actual)
170 169 d.addCallback(compare)
171 170
172 171 def testExecuteAll(self):
173 172 self.addEngine(4)
174 173 d= defer.Deferred()
175 174 shell = Interpreter()
176 175 for cmd in validCommands:
177 176 self.runExecuteAll(d, cmd, shell)
178 177 d.callback(None)
179 178 return d
180 179
181 180 # The following two methods show how to do parametrized
182 181 # tests. This is really slick! Same is used above.
183 182 def runExecuteFailures(self, cmd, exc):
184 183 self.addEngine(4)
185 184 d= self.multiengine.execute(cmd)
186 185 d.addErrback(lambda f: self.assertRaises(exc, _raise_it, f))
187 186 return d
188 187
189 188 @parametric
190 189 def testExecuteFailuresMultiEng(cls):
191 190 return [(cls.runExecuteFailures,cmd,exc) for
192 191 cmd,exc in invalidCommands]
193 192
194 193 def testPushPull(self):
195 194 self.addEngine(1)
196 195 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
197 196 d= self.multiengine.push(dict(key=objs[0]), targets=0)
198 197 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
199 198 d.addCallback(lambda r: self.assertEquals(r, [objs[0]]))
200 199 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[1]), targets=0))
201 200 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
202 201 d.addCallback(lambda r: self.assertEquals(r, [objs[1]]))
203 202 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[2]), targets=0))
204 203 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
205 204 d.addCallback(lambda r: self.assertEquals(r, [objs[2]]))
206 205 d.addCallback(lambda _: self.multiengine.push(dict(key=objs[3]), targets=0))
207 206 d.addCallback(lambda _: self.multiengine.pull('key', targets=0))
208 207 d.addCallback(lambda r: self.assertEquals(r, [objs[3]]))
209 208 d.addCallback(lambda _: self.multiengine.reset(targets=0))
210 209 d.addCallback(lambda _: self.multiengine.pull('a', targets=0))
211 210 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
212 211 d.addCallback(lambda _: self.multiengine.push(dict(a=10,b=20)))
213 212 d.addCallback(lambda _: self.multiengine.pull(('a','b')))
214 213 d.addCallback(lambda r: self.assertEquals(r, [[10,20]]))
215 214 return d
216 215
217 216 def testPushPullAll(self):
218 217 self.addEngine(4)
219 218 d= self.multiengine.push(dict(a=10))
220 219 d.addCallback(lambda _: self.multiengine.pull('a'))
221 220 d.addCallback(lambda r: self.assert_(r==[10,10,10,10]))
222 221 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20)))
223 222 d.addCallback(lambda _: self.multiengine.pull(('a','b')))
224 223 d.addCallback(lambda r: self.assert_(r==4*[[10,20]]))
225 224 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20), targets=0))
226 225 d.addCallback(lambda _: self.multiengine.pull(('a','b'), targets=0))
227 226 d.addCallback(lambda r: self.assert_(r==[[10,20]]))
228 227 d.addCallback(lambda _: self.multiengine.push(dict(a=None, b=None), targets=0))
229 228 d.addCallback(lambda _: self.multiengine.pull(('a','b'), targets=0))
230 229 d.addCallback(lambda r: self.assert_(r==[[None,None]]))
231 230 return d
232 231
233 232 def testPushPullSerialized(self):
234 233 self.addEngine(1)
235 234 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
236 235 d= self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[0])), targets=0)
237 236 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
238 237 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
239 238 d.addCallback(lambda r: self.assertEquals(r, objs[0]))
240 239 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[1])), targets=0))
241 240 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
242 241 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
243 242 d.addCallback(lambda r: self.assertEquals(r, objs[1]))
244 243 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[2])), targets=0))
245 244 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
246 245 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
247 246 d.addCallback(lambda r: self.assertEquals(r, objs[2]))
248 247 d.addCallback(lambda _: self.multiengine.push_serialized(dict(key=newserialized.serialize(objs[3])), targets=0))
249 248 d.addCallback(lambda _: self.multiengine.pull_serialized('key', targets=0))
250 249 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
251 250 d.addCallback(lambda r: self.assertEquals(r, objs[3]))
252 251 d.addCallback(lambda _: self.multiengine.push(dict(a=10,b=range(5)), targets=0))
253 252 d.addCallback(lambda _: self.multiengine.pull_serialized(('a','b'), targets=0))
254 253 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
255 254 d.addCallback(lambda r: self.assertEquals(r, [10, range(5)]))
256 255 d.addCallback(lambda _: self.multiengine.reset(targets=0))
257 256 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
258 257 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
259 258 return d
260 259
261 260 objs = [10,"hi there",1.2342354,{"p":(1,2)}]
262 261 d= defer.succeed(None)
263 262 for o in objs:
264 263 self.multiengine.push_serialized(0, key=newserialized.serialize(o))
265 264 value = self.multiengine.pull_serialized(0, 'key')
266 265 value.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
267 266 d = self.assertDeferredEquals(value,o,d)
268 267 return d
269 268
270 269 def runGetResultAll(self, d, cmd, shell):
271 270 actual = shell.execute(cmd)
272 271 d.addCallback(lambda _: self.multiengine.execute(cmd))
273 272 d.addCallback(lambda _: self.multiengine.get_result())
274 273 def compare(result):
275 274 for r in result:
276 275 actual['id'] = r['id']
277 276 self.assertEquals(r, actual)
278 277 d.addCallback(compare)
279 278
280 279 def testGetResultAll(self):
281 280 self.addEngine(4)
282 281 d= defer.Deferred()
283 282 shell = Interpreter()
284 283 for cmd in validCommands:
285 284 self.runGetResultAll(d, cmd, shell)
286 285 d.callback(None)
287 286 return d
288 287
289 288 def testGetResultDefault(self):
290 289 self.addEngine(1)
291 290 target = 0
292 291 cmd = 'a=5'
293 292 shell = self.createShell()
294 293 shellResult = shell.execute(cmd)
295 294 def popit(dikt, key):
296 295 dikt.pop(key)
297 296 return dikt
298 297 d= self.multiengine.execute(cmd, targets=target)
299 298 d.addCallback(lambda _: self.multiengine.get_result(targets=target))
300 299 d.addCallback(lambda r: self.assertEquals(shellResult, popit(r[0],'id')))
301 300 return d
302 301
303 302 def testGetResultFailure(self):
304 303 self.addEngine(1)
305 304 d= self.multiengine.get_result(None, targets=0)
306 305 d.addErrback(lambda f: self.assertRaises(IndexError, _raise_it, f))
307 306 d.addCallback(lambda _: self.multiengine.get_result(10, targets=0))
308 307 d.addErrback(lambda f: self.assertRaises(IndexError, _raise_it, f))
309 308 return d
310 309
311 310 def testPushFunction(self):
312 311 self.addEngine(1)
313 312 d= self.multiengine.push_function(dict(f=testf), targets=0)
314 313 d.addCallback(lambda _: self.multiengine.execute('result = f(10)', targets=0))
315 314 d.addCallback(lambda _: self.multiengine.pull('result', targets=0))
316 315 d.addCallback(lambda r: self.assertEquals(r[0], testf(10)))
317 316 d.addCallback(lambda _: self.multiengine.push(dict(globala=globala), targets=0))
318 317 d.addCallback(lambda _: self.multiengine.push_function(dict(g=testg), targets=0))
319 318 d.addCallback(lambda _: self.multiengine.execute('result = g(10)', targets=0))
320 319 d.addCallback(lambda _: self.multiengine.pull('result', targets=0))
321 320 d.addCallback(lambda r: self.assertEquals(r[0], testg(10)))
322 321 return d
323 322
324 323 def testPullFunction(self):
325 324 self.addEngine(1)
326 325 d= self.multiengine.push(dict(a=globala), targets=0)
327 326 d.addCallback(lambda _: self.multiengine.push_function(dict(f=testf), targets=0))
328 327 d.addCallback(lambda _: self.multiengine.pull_function('f', targets=0))
329 328 d.addCallback(lambda r: self.assertEquals(r[0](10), testf(10)))
330 329 d.addCallback(lambda _: self.multiengine.execute("def g(x): return x*x", targets=0))
331 330 d.addCallback(lambda _: self.multiengine.pull_function(('f','g'),targets=0))
332 331 d.addCallback(lambda r: self.assertEquals((r[0][0](10),r[0][1](10)), (testf(10), 100)))
333 332 return d
334 333
335 334 def testPushFunctionAll(self):
336 335 self.addEngine(4)
337 336 d= self.multiengine.push_function(dict(f=testf))
338 337 d.addCallback(lambda _: self.multiengine.execute('result = f(10)'))
339 338 d.addCallback(lambda _: self.multiengine.pull('result'))
340 339 d.addCallback(lambda r: self.assertEquals(r, 4*[testf(10)]))
341 340 d.addCallback(lambda _: self.multiengine.push(dict(globala=globala)))
342 341 d.addCallback(lambda _: self.multiengine.push_function(dict(testg=testg)))
343 342 d.addCallback(lambda _: self.multiengine.execute('result = testg(10)'))
344 343 d.addCallback(lambda _: self.multiengine.pull('result'))
345 344 d.addCallback(lambda r: self.assertEquals(r, 4*[testg(10)]))
346 345 return d
347 346
348 347 def testPullFunctionAll(self):
349 348 self.addEngine(4)
350 349 d= self.multiengine.push_function(dict(f=testf))
351 350 d.addCallback(lambda _: self.multiengine.pull_function('f'))
352 351 d.addCallback(lambda r: self.assertEquals([func(10) for func in r], 4*[testf(10)]))
353 352 return d
354 353
355 354 def testGetIDs(self):
356 355 self.addEngine(1)
357 356 d= self.multiengine.get_ids()
358 357 d.addCallback(lambda r: self.assertEquals(r, [0]))
359 358 d.addCallback(lambda _: self.addEngine(3))
360 359 d.addCallback(lambda _: self.multiengine.get_ids())
361 360 d.addCallback(lambda r: self.assertEquals(r, [0,1,2,3]))
362 361 return d
363 362
364 363 def testClearQueue(self):
365 364 self.addEngine(4)
366 365 d= self.multiengine.clear_queue()
367 366 d.addCallback(lambda r: self.assertEquals(r,4*[None]))
368 367 return d
369 368
370 369 def testQueueStatus(self):
371 370 self.addEngine(4)
372 371 d= self.multiengine.queue_status(targets=0)
373 372 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
374 373 return d
375 374
376 375 def testGetSetProperties(self):
377 376 self.addEngine(4)
378 377 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
379 378 d= self.multiengine.set_properties(dikt)
380 379 d.addCallback(lambda r: self.multiengine.get_properties())
381 380 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
382 381 d.addCallback(lambda r: self.multiengine.get_properties(('c',)))
383 382 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
384 383 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False)))
385 384 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd')))
386 385 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
387 386 return d
388 387
389 388 def testClearProperties(self):
390 389 self.addEngine(4)
391 390 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
392 391 d= self.multiengine.set_properties(dikt)
393 392 d.addCallback(lambda r: self.multiengine.clear_properties())
394 393 d.addCallback(lambda r: self.multiengine.get_properties())
395 394 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
396 395 return d
397 396
398 397 def testDelHasProperties(self):
399 398 self.addEngine(4)
400 399 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
401 400 d= self.multiengine.set_properties(dikt)
402 401 d.addCallback(lambda r: self.multiengine.del_properties(('b','e')))
403 402 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e')))
404 403 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
405 404 return d
406 405
407 406 Parametric(IMultiEngineTestCase)
408 407
409 408 #-------------------------------------------------------------------------------
410 409 # ISynchronousMultiEngineTestCase
411 410 #-------------------------------------------------------------------------------
412 411
413 412 class ISynchronousMultiEngineTestCase(IMultiEngineBaseTestCase):
414 413
415 414 def testISynchronousMultiEngineInterface(self):
416 415 """Does self.engine claim to implement IEngineCore?"""
417 416 self.assert_(me.ISynchronousEngineMultiplexer.providedBy(self.multiengine))
418 417 self.assert_(me.ISynchronousMultiEngine.providedBy(self.multiengine))
419 418
420 419 def testExecute(self):
421 420 self.addEngine(4)
422 421 execute = self.multiengine.execute
423 422 d= execute('a=5', targets=0, block=True)
424 423 d.addCallback(lambda r: self.assert_(len(r)==1))
425 424 d.addCallback(lambda _: execute('b=10'))
426 425 d.addCallback(lambda r: self.assert_(len(r)==4))
427 426 d.addCallback(lambda _: execute('c=30', block=False))
428 427 d.addCallback(lambda did: self.assert_(isdid(did)))
429 428 d.addCallback(lambda _: execute('d=[0,1,2]', block=False))
430 429 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
431 430 d.addCallback(lambda r: self.assert_(len(r)==4))
432 431 return d
433 432
434 433 def testPushPull(self):
435 434 data = dict(a=10, b=1.05, c=range(10), d={'e':(1,2),'f':'hi'})
436 435 self.addEngine(4)
437 436 push = self.multiengine.push
438 437 pull = self.multiengine.pull
439 438 d= push({'data':data}, targets=0)
440 439 d.addCallback(lambda r: pull('data', targets=0))
441 440 d.addCallback(lambda r: self.assertEqual(r,[data]))
442 441 d.addCallback(lambda _: push({'data':data}))
443 442 d.addCallback(lambda r: pull('data'))
444 443 d.addCallback(lambda r: self.assertEqual(r,4*[data]))
445 444 d.addCallback(lambda _: push({'data':data}, block=False))
446 445 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
447 446 d.addCallback(lambda _: pull('data', block=False))
448 447 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
449 448 d.addCallback(lambda r: self.assertEqual(r,4*[data]))
450 449 d.addCallback(lambda _: push(dict(a=10,b=20)))
451 450 d.addCallback(lambda _: pull(('a','b')))
452 451 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,20]]))
453 452 return d
454 453
455 454 def testPushPullFunction(self):
456 455 self.addEngine(4)
457 456 pushf = self.multiengine.push_function
458 457 pullf = self.multiengine.pull_function
459 458 push = self.multiengine.push
460 459 pull = self.multiengine.pull
461 460 execute = self.multiengine.execute
462 461 d= pushf({'testf':testf}, targets=0)
463 462 d.addCallback(lambda r: pullf('testf', targets=0))
464 463 d.addCallback(lambda r: self.assertEqual(r[0](1.0), testf(1.0)))
465 464 d.addCallback(lambda _: execute('r = testf(10)', targets=0))
466 465 d.addCallback(lambda _: pull('r', targets=0))
467 466 d.addCallback(lambda r: self.assertEquals(r[0], testf(10)))
468 467 d.addCallback(lambda _: pushf({'testf':testf}, block=False))
469 468 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
470 469 d.addCallback(lambda _: pullf('testf', block=False))
471 470 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
472 471 d.addCallback(lambda r: self.assertEqual(r[0](1.0), testf(1.0)))
473 472 d.addCallback(lambda _: execute("def g(x): return x*x", targets=0))
474 473 d.addCallback(lambda _: pullf(('testf','g'),targets=0))
475 474 d.addCallback(lambda r: self.assertEquals((r[0][0](10),r[0][1](10)), (testf(10), 100)))
476 475 return d
477 476
478 477 def testGetResult(self):
479 478 shell = Interpreter()
480 479 result1 = shell.execute('a=10')
481 480 result1['id'] = 0
482 481 result2 = shell.execute('b=20')
483 482 result2['id'] = 0
484 483 execute= self.multiengine.execute
485 484 get_result = self.multiengine.get_result
486 485 self.addEngine(1)
487 486 d= execute('a=10')
488 487 d.addCallback(lambda _: get_result())
489 488 d.addCallback(lambda r: self.assertEquals(r[0], result1))
490 489 d.addCallback(lambda _: execute('b=20'))
491 490 d.addCallback(lambda _: get_result(1))
492 491 d.addCallback(lambda r: self.assertEquals(r[0], result1))
493 492 d.addCallback(lambda _: get_result(2, block=False))
494 493 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
495 494 d.addCallback(lambda r: self.assertEquals(r[0], result2))
496 495 return d
497 496
498 497 def testResetAndKeys(self):
499 498 self.addEngine(1)
500 499
501 500 #Blocking mode
502 501 d= self.multiengine.push(dict(a=10, b=20, c=range(10)), targets=0)
503 502 d.addCallback(lambda _: self.multiengine.keys(targets=0))
504 503 def keys_found(keys):
505 504 self.assert_('a' in keys[0])
506 505 self.assert_('b' in keys[0])
507 506 self.assert_('b' in keys[0])
508 507 d.addCallback(keys_found)
509 508 d.addCallback(lambda _: self.multiengine.reset(targets=0))
510 509 d.addCallback(lambda _: self.multiengine.keys(targets=0))
511 510 def keys_not_found(keys):
512 511 self.assert_('a' not in keys[0])
513 512 self.assert_('b' not in keys[0])
514 513 self.assert_('b' not in keys[0])
515 514 d.addCallback(keys_not_found)
516 515
517 516 #Non-blocking mode
518 517 d.addCallback(lambda _: self.multiengine.push(dict(a=10, b=20, c=range(10)), targets=0))
519 518 d.addCallback(lambda _: self.multiengine.keys(targets=0, block=False))
520 519 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
521 520 def keys_found(keys):
522 521 self.assert_('a' in keys[0])
523 522 self.assert_('b' in keys[0])
524 523 self.assert_('b' in keys[0])
525 524 d.addCallback(keys_found)
526 525 d.addCallback(lambda _: self.multiengine.reset(targets=0, block=False))
527 526 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
528 527 d.addCallback(lambda _: self.multiengine.keys(targets=0, block=False))
529 528 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
530 529 def keys_not_found(keys):
531 530 self.assert_('a' not in keys[0])
532 531 self.assert_('b' not in keys[0])
533 532 self.assert_('b' not in keys[0])
534 533 d.addCallback(keys_not_found)
535 534
536 535 return d
537 536
538 537 def testPushPullSerialized(self):
539 538 self.addEngine(1)
540 539 dikt = dict(a=10,b='hi there',c=1.2345,d={'p':(1,2)})
541 540 sdikt = {}
542 541 for k,v in dikt.iteritems():
543 542 sdikt[k] = newserialized.serialize(v)
544 543 d= self.multiengine.push_serialized(dict(a=sdikt['a']), targets=0)
545 544 d.addCallback(lambda _: self.multiengine.pull('a',targets=0))
546 545 d.addCallback(lambda r: self.assertEquals(r[0], dikt['a']))
547 546 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
548 547 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
549 548 d.addCallback(lambda r: self.assertEquals(r, dikt['a']))
550 549 d.addCallback(lambda _: self.multiengine.push_serialized(sdikt, targets=0))
551 550 d.addCallback(lambda _: self.multiengine.pull_serialized(sdikt.keys(), targets=0))
552 551 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
553 552 d.addCallback(lambda r: self.assertEquals(r, dikt.values()))
554 553 d.addCallback(lambda _: self.multiengine.reset(targets=0))
555 554 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0))
556 555 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
557 556
558 557 #Non-blocking mode
559 558 d.addCallback(lambda r: self.multiengine.push_serialized(dict(a=sdikt['a']), targets=0, block=False))
560 559 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
561 560 d.addCallback(lambda _: self.multiengine.pull('a',targets=0))
562 561 d.addCallback(lambda r: self.assertEquals(r[0], dikt['a']))
563 562 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0, block=False))
564 563 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
565 564 d.addCallback(lambda serial: newserialized.IUnSerialized(serial[0]).getObject())
566 565 d.addCallback(lambda r: self.assertEquals(r, dikt['a']))
567 566 d.addCallback(lambda _: self.multiengine.push_serialized(sdikt, targets=0, block=False))
568 567 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
569 568 d.addCallback(lambda _: self.multiengine.pull_serialized(sdikt.keys(), targets=0, block=False))
570 569 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
571 570 d.addCallback(lambda serial: [newserialized.IUnSerialized(s).getObject() for s in serial[0]])
572 571 d.addCallback(lambda r: self.assertEquals(r, dikt.values()))
573 572 d.addCallback(lambda _: self.multiengine.reset(targets=0))
574 573 d.addCallback(lambda _: self.multiengine.pull_serialized('a', targets=0, block=False))
575 574 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
576 575 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
577 576 return d
578 577
579 578 def testClearQueue(self):
580 579 self.addEngine(4)
581 580 d= self.multiengine.clear_queue()
582 581 d.addCallback(lambda r: self.multiengine.clear_queue(block=False))
583 582 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
584 583 d.addCallback(lambda r: self.assertEquals(r,4*[None]))
585 584 return d
586 585
587 586 def testQueueStatus(self):
588 587 self.addEngine(4)
589 588 d= self.multiengine.queue_status(targets=0)
590 589 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
591 590 d.addCallback(lambda r: self.multiengine.queue_status(targets=0, block=False))
592 591 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
593 592 d.addCallback(lambda r: self.assert_(isinstance(r[0],tuple)))
594 593 return d
595 594
596 595 def testGetIDs(self):
597 596 self.addEngine(1)
598 597 d= self.multiengine.get_ids()
599 598 d.addCallback(lambda r: self.assertEquals(r, [0]))
600 599 d.addCallback(lambda _: self.addEngine(3))
601 600 d.addCallback(lambda _: self.multiengine.get_ids())
602 601 d.addCallback(lambda r: self.assertEquals(r, [0,1,2,3]))
603 602 return d
604 603
605 604 def testGetSetProperties(self):
606 605 self.addEngine(4)
607 606 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
608 607 d= self.multiengine.set_properties(dikt)
609 608 d.addCallback(lambda r: self.multiengine.get_properties())
610 609 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
611 610 d.addCallback(lambda r: self.multiengine.get_properties(('c',)))
612 611 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
613 612 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False)))
614 613 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd')))
615 614 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
616 615
617 616 #Non-blocking
618 617 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
619 618 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
620 619 d.addCallback(lambda r: self.multiengine.get_properties(block=False))
621 620 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
622 621 d.addCallback(lambda r: self.assertEquals(r, 4*[dikt]))
623 622 d.addCallback(lambda r: self.multiengine.get_properties(('c',), block=False))
624 623 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
625 624 d.addCallback(lambda r: self.assertEquals(r, 4*[{'c': dikt['c']}]))
626 625 d.addCallback(lambda r: self.multiengine.set_properties(dict(c=False), block=False))
627 626 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
628 627 d.addCallback(lambda r: self.multiengine.get_properties(('c', 'd'), block=False))
629 628 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
630 629 d.addCallback(lambda r: self.assertEquals(r, 4*[dict(c=False, d=None)]))
631 630 return d
632 631
633 632 def testClearProperties(self):
634 633 self.addEngine(4)
635 634 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
636 635 d= self.multiengine.set_properties(dikt)
637 636 d.addCallback(lambda r: self.multiengine.clear_properties())
638 637 d.addCallback(lambda r: self.multiengine.get_properties())
639 638 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
640 639
641 640 #Non-blocking
642 641 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
643 642 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
644 643 d.addCallback(lambda r: self.multiengine.clear_properties(block=False))
645 644 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
646 645 d.addCallback(lambda r: self.multiengine.get_properties(block=False))
647 646 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
648 647 d.addCallback(lambda r: self.assertEquals(r, 4*[{}]))
649 648 return d
650 649
651 650 def testDelHasProperties(self):
652 651 self.addEngine(4)
653 652 dikt = dict(a=5, b='asdf', c=True, d=None, e=range(5))
654 653 d= self.multiengine.set_properties(dikt)
655 654 d.addCallback(lambda r: self.multiengine.del_properties(('b','e')))
656 655 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e')))
657 656 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
658 657
659 658 #Non-blocking
660 659 d.addCallback(lambda r: self.multiengine.set_properties(dikt, block=False))
661 660 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
662 661 d.addCallback(lambda r: self.multiengine.del_properties(('b','e'), block=False))
663 662 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
664 663 d.addCallback(lambda r: self.multiengine.has_properties(('a','b','c','d','e'), block=False))
665 664 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
666 665 d.addCallback(lambda r: self.assertEquals(r, 4*[[True, False, True, True, False]]))
667 666 return d
668 667
669 668 def test_clear_pending_deferreds(self):
670 669 self.addEngine(4)
671 670 did_list = []
672 671 d= self.multiengine.execute('a=10',block=False)
673 672 d.addCallback(lambda did: did_list.append(did))
674 673 d.addCallback(lambda _: self.multiengine.push(dict(b=10),block=False))
675 674 d.addCallback(lambda did: did_list.append(did))
676 675 d.addCallback(lambda _: self.multiengine.pull(('a','b'),block=False))
677 676 d.addCallback(lambda did: did_list.append(did))
678 677 d.addCallback(lambda _: self.multiengine.clear_pending_deferreds())
679 678 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[0],True))
680 679 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
681 680 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[1],True))
682 681 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
683 682 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[2],True))
684 683 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
685 684 return d
686 685
687 686 #-------------------------------------------------------------------------------
688 687 # Coordinator test cases
689 688 #-------------------------------------------------------------------------------
690 689
691 690 class IMultiEngineCoordinatorTestCase(object):
692 691
693 692 def testScatterGather(self):
694 693 self.addEngine(4)
695 694 d= self.multiengine.scatter('a', range(16))
696 695 d.addCallback(lambda r: self.multiengine.gather('a'))
697 696 d.addCallback(lambda r: self.assertEquals(r, range(16)))
698 697 d.addCallback(lambda _: self.multiengine.gather('asdf'))
699 698 d.addErrback(lambda f: self.assertRaises(NameError, _raise_it, f))
700 699 return d
701 700
702 701 def testScatterGatherNumpy(self):
703 702 try:
704 703 import numpy
705 704 from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
706 705 except:
707 706 return
708 707 else:
709 708 self.addEngine(4)
710 709 a = numpy.arange(16)
711 710 d = self.multiengine.scatter('a', a)
712 711 d.addCallback(lambda r: self.multiengine.gather('a'))
713 712 d.addCallback(lambda r: assert_array_equal(r, a))
714 713 return d
715 714
716 715 def testMap(self):
717 716 self.addEngine(4)
718 717 def f(x):
719 718 return x**2
720 719 data = range(16)
721 720 d= self.multiengine.map(f, data)
722 721 d.addCallback(lambda r: self.assertEquals(r,[f(x) for x in data]))
723 722 return d
724 723
725 724
726 725 class ISynchronousMultiEngineCoordinatorTestCase(IMultiEngineCoordinatorTestCase):
727 726
728 727 def testScatterGatherNonblocking(self):
729 728 self.addEngine(4)
730 729 d= self.multiengine.scatter('a', range(16), block=False)
731 730 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
732 731 d.addCallback(lambda r: self.multiengine.gather('a', block=False))
733 732 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
734 733 d.addCallback(lambda r: self.assertEquals(r, range(16)))
735 734 return d
736 735
737 736 def testScatterGatherNumpyNonblocking(self):
738 737 try:
739 738 import numpy
740 739 from numpy.testing.utils import assert_array_equal, assert_array_almost_equal
741 740 except:
742 741 return
743 742 else:
744 743 self.addEngine(4)
745 744 a = numpy.arange(16)
746 745 d = self.multiengine.scatter('a', a, block=False)
747 746 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
748 747 d.addCallback(lambda r: self.multiengine.gather('a', block=False))
749 748 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
750 749 d.addCallback(lambda r: assert_array_equal(r, a))
751 750 return d
752 751
753 752 def test_clear_pending_deferreds(self):
754 753 self.addEngine(4)
755 754 did_list = []
756 755 d= self.multiengine.scatter('a',range(16),block=False)
757 756 d.addCallback(lambda did: did_list.append(did))
758 757 d.addCallback(lambda _: self.multiengine.gather('a',block=False))
759 758 d.addCallback(lambda did: did_list.append(did))
760 759 d.addCallback(lambda _: self.multiengine.map(lambda x: x, range(16),block=False))
761 760 d.addCallback(lambda did: did_list.append(did))
762 761 d.addCallback(lambda _: self.multiengine.clear_pending_deferreds())
763 762 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[0],True))
764 763 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
765 764 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[1],True))
766 765 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
767 766 d.addCallback(lambda _: self.multiengine.get_pending_deferred(did_list[2],True))
768 767 d.addErrback(lambda f: self.assertRaises(InvalidDeferredID, f.raiseException))
769 768 return d
770 769
771 770 #-------------------------------------------------------------------------------
772 771 # Extras test cases
773 772 #-------------------------------------------------------------------------------
774 773
775 774 class IMultiEngineExtrasTestCase(object):
776 775
777 776 def testZipPull(self):
778 777 self.addEngine(4)
779 778 d= self.multiengine.push(dict(a=10,b=20))
780 779 d.addCallback(lambda r: self.multiengine.zip_pull(('a','b')))
781 780 d.addCallback(lambda r: self.assert_(r, [4*[10],4*[20]]))
782 781 return d
783 782
784 783 def testRun(self):
785 784 self.addEngine(4)
786 785 import tempfile
787 786 fname = tempfile.mktemp('foo.py')
788 787 f= open(fname, 'w')
789 788 f.write('a = 10\nb=30')
790 789 f.close()
791 790 d= self.multiengine.run(fname)
792 791 d.addCallback(lambda r: self.multiengine.pull(('a','b')))
793 792 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,30]]))
794 793 return d
795 794
796 795
797 796 class ISynchronousMultiEngineExtrasTestCase(IMultiEngineExtrasTestCase):
798 797
799 798 def testZipPullNonblocking(self):
800 799 self.addEngine(4)
801 800 d= self.multiengine.push(dict(a=10,b=20))
802 801 d.addCallback(lambda r: self.multiengine.zip_pull(('a','b'), block=False))
803 802 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
804 803 d.addCallback(lambda r: self.assert_(r, [4*[10],4*[20]]))
805 804 return d
806 805
807 806 def testRunNonblocking(self):
808 807 self.addEngine(4)
809 808 import tempfile
810 809 fname = tempfile.mktemp('foo.py')
811 810 f= open(fname, 'w')
812 811 f.write('a = 10\nb=30')
813 812 f.close()
814 813 d= self.multiengine.run(fname, block=False)
815 814 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
816 815 d.addCallback(lambda r: self.multiengine.pull(('a','b')))
817 816 d.addCallback(lambda r: self.assertEquals(r, 4*[[10,30]]))
818 817 return d
819 818
820 819
821 820 #-------------------------------------------------------------------------------
822 821 # IFullSynchronousMultiEngineTestCase
823 822 #-------------------------------------------------------------------------------
824 823
825 824 class IFullSynchronousMultiEngineTestCase(ISynchronousMultiEngineTestCase,
826 825 ISynchronousMultiEngineCoordinatorTestCase,
827 826 ISynchronousMultiEngineExtrasTestCase):
828 827 pass
@@ -1,360 +1,393 b''
1 1 .. _development:
2 2
3 3 ==================================
4 4 IPython development guidelines
5 5 ==================================
6 6
7 7 .. contents::
8 8
9 9
10 10 Overview
11 11 ========
12 12
13 13 IPython is the next generation of IPython. It is named such for two reasons:
14 14
15 15 - Eventually, IPython will become IPython version 1.0.
16 16 - This new code base needs to be able to co-exist with the existing IPython until
17 17 it is a full replacement for it. Thus we needed a different name. We couldn't
18 18 use ``ipython`` (lowercase) as some files systems are case insensitive.
19 19
20 20 There are two, no three, main goals of the IPython effort:
21 21
22 22 1. Clean up the existing codebase and write lots of tests.
23 23 2. Separate the core functionality of IPython from the terminal to enable IPython
24 24 to be used from within a variety of GUI applications.
25 25 3. Implement a system for interactive parallel computing.
26 26
27 27 While the third goal may seem a bit unrelated to the main focus of IPython, it turns
28 28 out that the technologies required for this goal are nearly identical with those
29 29 required for goal two. This is the main reason the interactive parallel computing
30 30 capabilities are being put into IPython proper. Currently the third of these goals is
31 31 furthest along.
32 32
33 33 This document describes IPython from the perspective of developers.
34 34
35 35
36 36 Project organization
37 37 ====================
38 38
39 39 Subpackages
40 40 -----------
41 41
42 42 IPython is organized into semi self-contained subpackages. Each of the subpackages will have its own:
43 43
44 44 - **Dependencies**. One of the most important things to keep in mind in
45 45 partitioning code amongst subpackages, is that they should be used to cleanly
46 46 encapsulate dependencies.
47 47 - **Tests**. Each subpackage shoud have its own ``tests`` subdirectory that
48 48 contains all of the tests for that package. For information about writing tests
49 49 for IPython, see the `Testing System`_ section of this document.
50 50 - **Configuration**. Each subpackage should have its own ``config`` subdirectory
51 51 that contains the configuration information for the components of the
52 52 subpackage. For information about how the IPython configuration system
53 53 works, see the `Configuration System`_ section of this document.
54 54 - **Scripts**. Each subpackage should have its own ``scripts`` subdirectory that
55 55 contains all of the command line scripts associated with the subpackage.
56 56
57 57 Installation and dependencies
58 58 -----------------------------
59 59
60 60 IPython will not use `setuptools`_ for installation. Instead, we will use standard
61 61 ``setup.py`` scripts that use `distutils`_. While there are a number a extremely nice
62 62 features that `setuptools`_ has (like namespace packages), the current implementation
63 63 of `setuptools`_ has performance problems, particularly on shared file systems. In
64 64 particular, when Python packages are installed on NSF file systems, import times
65 65 become much too long (up towards 10 seconds).
66 66
67 67 Because IPython is being used extensively in the context of high performance
68 68 computing, where performance is critical but shared file systems are common, we feel
69 69 these performance hits are not acceptable. Thus, until the performance problems
70 70 associated with `setuptools`_ are addressed, we will stick with plain `distutils`_. We
71 71 are hopeful that these problems will be addressed and that we will eventually begin
72 72 using `setuptools`_. Because of this, we are trying to organize IPython in a way that
73 73 will make the eventual transition to `setuptools`_ as painless as possible.
74 74
75 75 Because we will be using `distutils`_, there will be no method for automatically installing dependencies. Instead, we are following the approach of `Matplotlib`_ which can be summarized as follows:
76 76
77 77 - Distinguish between required and optional dependencies. However, the required
78 78 dependencies for IPython should be only the Python standard library.
79 79 - Upon installation check to see which optional dependencies are present and tell
80 80 the user which parts of IPython need which optional dependencies.
81 81
82 82 It is absolutely critical that each subpackage of IPython has a clearly specified set
83 83 of dependencies and that dependencies are not carelessly inherited from other IPython
84 84 subpackages. Furthermore, tests that have certain dependencies should not fail if
85 85 those dependencies are not present. Instead they should be skipped and print a
86 86 message.
87 87
88 88 .. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools
89 89 .. _distutils: http://docs.python.org/lib/module-distutils.html
90 90 .. _Matplotlib: http://matplotlib.sourceforge.net/
91 91
92 92 Specific subpackages
93 93 --------------------
94 94
95 95 ``core``
96 96 This is the core functionality of IPython that is independent of the
97 97 terminal, network and GUIs. Most of the code that is in the current
98 98 IPython trunk will be refactored, cleaned up and moved here.
99 99
100 100 ``kernel``
101 101 The enables the IPython core to be expose to a the network. This is
102 102 also where all of the parallel computing capabilities are to be found.
103 103
104 104 ``config``
105 105 The configuration package used by IPython.
106 106
107 107 ``frontends``
108 108 The various frontends for IPython. A frontend is the end-user application
109 109 that exposes the capabilities of IPython to the user. The most basic frontend
110 110 will simply be a terminal based application that looks just like today 's
111 111 IPython. Other frontends will likely be more powerful and based on GUI toolkits.
112 112
113 113 ``notebook``
114 114 An application that allows users to work with IPython notebooks.
115 115
116 116 ``tools``
117 117 This is where general utilities go.
118 118
119 119
120 120 Version control
121 121 ===============
122 122
123 123 In the past, IPython development has been done using `Subversion`__. Recently, we made the transition to using `Bazaar`__ and `Launchpad`__. This makes it much easier for people
124 124 to contribute code to IPython. Here is a sketch of how to use Bazaar for IPython
125 125 development. First, you should install Bazaar. After you have done that, make
126 126 sure that it is working by getting the latest main branch of IPython::
127 127
128 128 $ bzr branch lp:ipython
129 129
130 130 Now you can create a new branch for you to do your work in::
131 131
132 132 $ bzr branch ipython ipython-mybranch
133 133
134 134 The typical work cycle in this branch will be to make changes in `ipython-mybranch`
135 135 and then commit those changes using the commit command::
136 136
137 137 $ ...do work in ipython-mybranch...
138 138 $ bzr ci -m "the commit message goes here"
139 139
140 140 Please note that since we now don't use an old-style linear ChangeLog
141 141 (that tends to cause problems with distributed version control
142 142 systems), you should ensure that your log messages are reasonably
143 143 detailed. Use a docstring-like approach in the commit messages
144 144 (including the second line being left *blank*)::
145 145
146 146 Single line summary of changes being committed.
147 147
148 148 - more details when warranted ...
149 149 - including crediting outside contributors if they sent the
150 150 code/bug/idea!
151 151
152 152 If we couple this with a policy of making single commits for each
153 153 reasonably atomic change, the bzr log should give an excellent view of
154 154 the project, and the `--short` log option becomes a nice summary.
155 155
156 156 While working with this branch, it is a good idea to merge in changes that have been
157 157 made upstream in the parent branch. This can be done by doing::
158 158
159 159 $ bzr pull
160 160
161 161 If this command shows that the branches have diverged, then you should do a merge
162 162 instead::
163 163
164 164 $ bzr merge lp:ipython
165 165
166 166 If you want others to be able to see your branch, you can create an account with
167 167 launchpad and push the branch to your own workspace::
168 168
169 169 $ bzr push bzr+ssh://<me>@bazaar.launchpad.net/~<me>/+junk/ipython-mybranch
170 170
171 171 Finally, once the work in your branch is done, you can merge your changes back into
172 172 the `ipython` branch by using merge::
173 173
174 174 $ cd ipython
175 175 $ merge ../ipython-mybranch
176 176 [resolve any conflicts]
177 177 $ bzr ci -m "Fixing that bug"
178 178 $ bzr push
179 179
180 180 But this will require you to have write permissions to the `ipython` branch. It you don't
181 181 you can tell one of the IPython devs about your branch and they can do the merge for you.
182 182
183 183 More information about Bazaar workflows can be found `here`__.
184 184
185 185 .. __: http://subversion.tigris.org/
186 186 .. __: http://bazaar-vcs.org/
187 187 .. __: http://www.launchpad.net/ipython
188 188 .. __: http://doc.bazaar-vcs.org/bzr.dev/en/user-guide/index.html
189 189
190 190 Documentation
191 191 =============
192 192
193 193 Standalone documentation
194 194 ------------------------
195 195
196 196 All standalone documentation should be written in plain text (``.txt``) files using
197 197 `reStructuredText`_ for markup and formatting. All such documentation should be placed
198 198 in the top level directory ``docs`` of the IPython source tree. Or, when appropriate,
199 199 a suitably named subdirectory should be used. The documentation in this location will
200 200 serve as the main source for IPython documentation and all existing documentation
201 201 should be converted to this format.
202 202
203 203 In the future, the text files in the ``docs`` directory will be used to generate all
204 204 forms of documentation for IPython. This include documentation on the IPython website
205 205 as well as *pdf* documentation.
206 206
207 207 .. _reStructuredText: http://docutils.sourceforge.net/rst.html
208 208
209 209 Docstring format
210 210 ----------------
211 211
212 212 Good docstrings are very important. All new code will use `Epydoc`_ for generating API
213 213 docs, so we will follow the `Epydoc`_ conventions. More specifically, we will use
214 214 `reStructuredText`_ for markup and formatting, since it is understood by a wide
215 215 variety of tools. This means that if in the future we have any reason to change from
216 216 `Epydoc`_ to something else, we'll have fewer transition pains.
217 217
218 218 Details about using `reStructuredText`_ for docstrings can be found `here
219 219 <http://epydoc.sourceforge.net/manual-othermarkup.html>`_.
220 220
221 221 .. _Epydoc: http://epydoc.sourceforge.net/
222 222
223 223 Additional PEPs of interest regarding documentation of code:
224 224
225 225 - `Docstring Conventions <http://www.python.org/peps/pep-0257.html>`_
226 226 - `Docstring Processing System Framework <http://www.python.org/peps/pep-0256.html>`_
227 227 - `Docutils Design Specification <http://www.python.org/peps/pep-0258.html>`_
228 228
229 229
230 230 Coding conventions
231 231 ==================
232 232
233 233 General
234 234 -------
235 235
236 236 In general, we'll try to follow the standard Python style conventions as described here:
237 237
238 238 - `Style Guide for Python Code <http://www.python.org/peps/pep-0008.html>`_
239 239
240 240
241 241 Other comments:
242 242
243 243 - In a large file, top level classes and functions should be
244 244 separated by 2-3 lines to make it easier to separate them visually.
245 245 - Use 4 spaces for indentation.
246 246 - Keep the ordering of methods the same in classes that have the same
247 247 methods. This is particularly true for classes that implement
248 248 similar interfaces and for interfaces that are similar.
249 249
250 250 Naming conventions
251 251 ------------------
252 252
253 253 In terms of naming conventions, we'll follow the guidelines from the `Style Guide for
254 254 Python Code`_.
255 255
256 256 For all new IPython code (and much existing code is being refactored), we'll use:
257 257
258 258 - All ``lowercase`` module names.
259 259
260 260 - ``CamelCase`` for class names.
261 261
262 262 - ``lowercase_with_underscores`` for methods, functions, variables and attributes.
263 263
264 264 This may be confusing as most of the existing IPython codebase uses a different convention (``lowerCamelCase`` for methods and attributes). Slowly, we will move IPython over to the new
265 265 convention, providing shadow names for backward compatibility in public interfaces.
266 266
267 267 There are, however, some important exceptions to these rules. In some cases, IPython
268 268 code will interface with packages (Twisted, Wx, Qt) that use other conventions. At some level this makes it impossible to adhere to our own standards at all times. In particular, when subclassing classes that use other naming conventions, you must follow their naming conventions. To deal with cases like this, we propose the following policy:
269 269
270 270 - If you are subclassing a class that uses different conventions, use its
271 271 naming conventions throughout your subclass. Thus, if you are creating a
272 272 Twisted Protocol class, used Twisted's ``namingSchemeForMethodsAndAttributes.``
273 273
274 274 - All IPython's official interfaces should use our conventions. In some cases
275 275 this will mean that you need to provide shadow names (first implement ``fooBar``
276 276 and then ``foo_bar = fooBar``). We want to avoid this at all costs, but it
277 277 will probably be necessary at times. But, please use this sparingly!
278 278
279 279 Implementation-specific *private* methods will use ``_single_underscore_prefix``.
280 280 Names with a leading double underscore will *only* be used in special cases, as they
281 281 makes subclassing difficult (such names are not easily seen by child classes).
282 282
283 283 Occasionally some run-in lowercase names are used, but mostly for very short names or
284 284 where we are implementing methods very similar to existing ones in a base class (like
285 285 ``runlines()`` where ``runsource()`` and ``runcode()`` had established precedent).
286 286
287 287 The old IPython codebase has a big mix of classes and modules prefixed with an
288 288 explicit ``IP``. In Python this is mostly unnecessary, redundant and frowned upon, as
289 289 namespaces offer cleaner prefixing. The only case where this approach is justified is
290 290 for classes which are expected to be imported into external namespaces and a very
291 291 generic name (like Shell) is too likely to clash with something else. We'll need to
292 292 revisit this issue as we clean up and refactor the code, but in general we should
293 293 remove as many unnecessary ``IP``/``ip`` prefixes as possible. However, if a prefix
294 294 seems absolutely necessary the more specific ``IPY`` or ``ipy`` are preferred.
295 295
296 296 .. _devel_testing:
297 297
298 298 Testing system
299 299 ==============
300 300
301 301 It is extremely important that all code contributed to IPython has tests. Tests should
302 302 be written as unittests, doctests or as entities that the `Nose`_ testing package will
303 303 find. Regardless of how the tests are written, we will use `Nose`_ for discovering and
304 304 running the tests. `Nose`_ will be required to run the IPython test suite, but will
305 305 not be required to simply use IPython.
306 306
307 307 .. _Nose: http://code.google.com/p/python-nose/
308 308
309 309 Tests of `Twisted`__ using code should be written by subclassing the ``TestCase`` class
310 310 that comes with ``twisted.trial.unittest``. When this is done, `Nose`_ will be able to
311 311 run the tests and the twisted reactor will be handled correctly.
312 312
313 313 .. __: http://www.twistedmatrix.com
314 314
315 315 Each subpackage in IPython should have its own ``tests`` directory that contains all
316 316 of the tests for that subpackage. This allows each subpackage to be self-contained. If
317 317 a subpackage has any dependencies beyond the Python standard library, the tests for
318 318 that subpackage should be skipped if the dependencies are not found. This is very
319 319 important so users don't get tests failing simply because they don't have dependencies.
320 320
321 321 We also need to look into use Noses ability to tag tests to allow a more modular
322 322 approach of running tests.
323 323
324 324 .. _devel_config:
325 325
326 326 Configuration system
327 327 ====================
328 328
329 329 IPython uses `.ini`_ files for configuration purposes. This represents a huge
330 330 improvement over the configuration system used in IPython. IPython works with these
331 331 files using the `ConfigObj`_ package, which IPython includes as
332 332 ``ipython1/external/configobj.py``.
333 333
334 334 Currently, we are using raw `ConfigObj`_ objects themselves. Each subpackage of IPython
335 335 should contain a ``config`` subdirectory that contains all of the configuration
336 336 information for the subpackage. To see how configuration information is defined (along
337 337 with defaults) see at the examples in ``ipython1/kernel/config`` and
338 338 ``ipython1/core/config``. Likewise, to see how the configuration information is used,
339 339 see examples in ``ipython1/kernel/scripts/ipengine.py``.
340 340
341 341 Eventually, we will add a new layer on top of the raw `ConfigObj`_ objects. We are
342 342 calling this new layer, ``tconfig``, as it will use a `Traits`_-like validation model.
343 343 We won't actually use `Traits`_, but will implement something similar in pure Python.
344 344 But, even in this new system, we will still use `ConfigObj`_ and `.ini`_ files
345 345 underneath the hood. Talk to Fernando if you are interested in working on this part of
346 346 IPython. The current prototype of ``tconfig`` is located in the IPython sandbox.
347 347
348 348 .. _.ini: http://docs.python.org/lib/module-ConfigParser.html
349 349 .. _ConfigObj: http://www.voidspace.org.uk/python/configobj.html
350 350 .. _Traits: http://code.enthought.com/traits/
351 351
352 Installation and testing scenarios
353 ==================================
354
355 This section outlines the various scenarios that we need to test before we release an IPython version. These scenarios represent different ways of installing IPython and its dependencies.
356
357 Installation scenarios
358 ----------------------
359
360 1. Install from tarball using `python setup.py install`.
361 a. With only readline+nose dependencies installed (test1)
362 b. With all dependencies installed (readline, zope.interface,
363 Twisted, foolscap, Sphinx, nose, pyOpenSSL) (test2)
364 2. Install using easy_install.
365 a. With only readline+nose dependencies installed (test3)
366 i. Default dependencies.
367 ii. Optional dependency sets (kernel, doc, test, security)
368 easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]
369
370 b. With all dependencies already installed (test2)
371
372
373 Tests to run for these scenarios
374 --------------------------------
375
376 1. Run the full test suite.
377 2. Start a controller and engines and try a few things by hand.
378 a. Using ipcluster.
379 b. Using ipcontroller/ipengine by hand.
380 3. Run a few of the parallel examples.
381 4. Try the kernel with and without security with and without PyOpenSSL
382 installed.
383 5. Beat on the IPython terminal a bunch.
384 6. Make sure that furl files are being put in proper locations.
352 385
353 386
354 387
355 388
356 389
357 390
358 391
359 392
360 393
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now