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