##// END OF EJS Templates
Removed some tabs and added a new way of skipping tests that have...
Brian Granger -
Show More
@@ -1,92 +1,92
1 """
1 """
2 Base front end class for all async frontends.
2 Base front end class for all async frontends.
3 """
3 """
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 #-------------------------------------------------------------------------------
6 #-------------------------------------------------------------------------------
7 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12
12
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 import uuid
17 import uuid
18
18
19 try:
19 try:
20 from zope.interface import Interface, Attribute, implements, classProvides
20 from zope.interface import Interface, Attribute, implements, classProvides
21 except ImportError, e:
21 except ImportError, e:
22 e.message = """%s
22 e.message = """%s
23 ________________________________________________________________________________
23 ________________________________________________________________________________
24 zope.interface is required to run asynchronous frontends.""" % e.message
24 zope.interface is required to run asynchronous frontends.""" % e.message
25 e.args = (e.message, ) + e.args[1:]
25 e.args = (e.message, ) + e.args[1:]
26
26
27 from frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
27 from frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
28
28
29 from IPython.kernel.engineservice import IEngineCore
30 from IPython.kernel.core.history import FrontEndHistory
29 from IPython.kernel.core.history import FrontEndHistory
31
30
32 try:
31 try:
32 from IPython.kernel.engineservice import IEngineCore
33 from twisted.python.failure import Failure
33 from twisted.python.failure import Failure
34 except ImportError, e:
34 except ImportError, e:
35 e.message = """%s
35 e.message = """%s
36 ________________________________________________________________________________
36 ________________________________________________________________________________
37 twisted is required to run asynchronous frontends.""" % e.message
37 twisted is required to run asynchronous frontends.""" % e.message
38 e.args = (e.message, ) + e.args[1:]
38 e.args = (e.message, ) + e.args[1:]
39
39
40
40
41
41
42
42
43 class AsyncFrontEndBase(FrontEndBase):
43 class AsyncFrontEndBase(FrontEndBase):
44 """
44 """
45 Overrides FrontEndBase to wrap execute in a deferred result.
45 Overrides FrontEndBase to wrap execute in a deferred result.
46 All callbacks are made as callbacks on the deferred result.
46 All callbacks are made as callbacks on the deferred result.
47 """
47 """
48
48
49 implements(IFrontEnd)
49 implements(IFrontEnd)
50 classProvides(IFrontEndFactory)
50 classProvides(IFrontEndFactory)
51
51
52 def __init__(self, engine=None, history=None):
52 def __init__(self, engine=None, history=None):
53 assert(engine==None or IEngineCore.providedBy(engine))
53 assert(engine==None or IEngineCore.providedBy(engine))
54 self.engine = IEngineCore(engine)
54 self.engine = IEngineCore(engine)
55 if history is None:
55 if history is None:
56 self.history = FrontEndHistory(input_cache=[''])
56 self.history = FrontEndHistory(input_cache=[''])
57 else:
57 else:
58 self.history = history
58 self.history = history
59
59
60
60
61 def execute(self, block, blockID=None):
61 def execute(self, block, blockID=None):
62 """Execute the block and return the deferred result.
62 """Execute the block and return the deferred result.
63
63
64 Parameters:
64 Parameters:
65 block : {str, AST}
65 block : {str, AST}
66 blockID : any
66 blockID : any
67 Caller may provide an ID to identify this block.
67 Caller may provide an ID to identify this block.
68 result['blockID'] := blockID
68 result['blockID'] := blockID
69
69
70 Result:
70 Result:
71 Deferred result of self.interpreter.execute
71 Deferred result of self.interpreter.execute
72 """
72 """
73
73
74 if(not self.is_complete(block)):
74 if(not self.is_complete(block)):
75 return Failure(Exception("Block is not compilable"))
75 return Failure(Exception("Block is not compilable"))
76
76
77 if(blockID == None):
77 if(blockID == None):
78 blockID = uuid.uuid4() #random UUID
78 blockID = uuid.uuid4() #random UUID
79
79
80 d = self.engine.execute(block)
80 d = self.engine.execute(block)
81 d.addCallback(self._add_history, block=block)
81 d.addCallback(self._add_history, block=block)
82 d.addCallbacks(self._add_block_id_for_result,
82 d.addCallbacks(self._add_block_id_for_result,
83 errback=self._add_block_id_for_failure,
83 errback=self._add_block_id_for_failure,
84 callbackArgs=(blockID,),
84 callbackArgs=(blockID,),
85 errbackArgs=(blockID,))
85 errbackArgs=(blockID,))
86 d.addBoth(self.update_cell_prompt, blockID=blockID)
86 d.addBoth(self.update_cell_prompt, blockID=blockID)
87 d.addCallbacks(self.render_result,
87 d.addCallbacks(self.render_result,
88 errback=self.render_error)
88 errback=self.render_error)
89
89
90 return d
90 return d
91
91
92
92
@@ -1,94 +1,94
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
17
18 try:
18 try:
19 from IPython.kernel.core.interpreter import Interpreter
19 from IPython.kernel.core.interpreter import Interpreter
20 import IPython.kernel.engineservice as es
20 import IPython.kernel.engineservice as es
21 from IPython.testing.util import DeferredTestCase
21 from IPython.testing.util import DeferredTestCase
22 from twisted.internet.defer import succeed
22 from twisted.internet.defer import succeed
23 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
23 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
24 from Foundation import NSMakeRect
24 from Foundation import NSMakeRect
25 from AppKit import NSTextView, NSScrollView
25 from AppKit import NSTextView, NSScrollView
26 except ImportError:
26 except ImportError:
27 pass
27 import nose
28 else:
28 raise nose.SkipTest("This test requires zope.interface, Twisted, Foolscap and PyObjC")
29 class TestIPythonCocoaControler(DeferredTestCase):
29
30 """Tests for IPythonCocoaController"""
30 class TestIPythonCocoaControler(DeferredTestCase):
31
31 """Tests for IPythonCocoaController"""
32 def setUp(self):
32
33 self.controller = IPythonCocoaController.alloc().init()
33 def setUp(self):
34 self.engine = es.EngineService()
34 self.controller = IPythonCocoaController.alloc().init()
35 self.engine.startService()
35 self.engine = es.EngineService()
36
36 self.engine.startService()
37
37
38 def tearDown(self):
38 def tearDown(self):
39 self.controller = None
39 self.controller = None
40 self.engine.stopService()
40 self.engine.stopService()
41
41
42 def testControllerExecutesCode(self):
42 def testControllerExecutesCode(self):
43 code ="""5+5"""
43 code ="""5+5"""
44 expected = Interpreter().execute(code)
44 expected = Interpreter().execute(code)
45 del expected['number']
45 del expected['number']
46 def removeNumberAndID(result):
46 def removeNumberAndID(result):
47 del result['number']
47 del result['number']
48 del result['id']
48 del result['id']
49 return result
49 return result
50 self.assertDeferredEquals(
50 self.assertDeferredEquals(
51 self.controller.execute(code).addCallback(removeNumberAndID),
51 self.controller.execute(code).addCallback(removeNumberAndID),
52 expected)
52 expected)
53
53
54 def testControllerMirrorsUserNSWithValuesAsStrings(self):
54 def testControllerMirrorsUserNSWithValuesAsStrings(self):
55 code = """userns1=1;userns2=2"""
55 code = """userns1=1;userns2=2"""
56 def testControllerUserNS(result):
56 def testControllerUserNS(result):
57 self.assertEquals(self.controller.userNS['userns1'], 1)
57 self.assertEquals(self.controller.userNS['userns1'], 1)
58 self.assertEquals(self.controller.userNS['userns2'], 2)
58 self.assertEquals(self.controller.userNS['userns2'], 2)
59
59
60 self.controller.execute(code).addCallback(testControllerUserNS)
60 self.controller.execute(code).addCallback(testControllerUserNS)
61
61
62
62
63 def testControllerInstantiatesIEngine(self):
63 def testControllerInstantiatesIEngine(self):
64 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
64 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
65
65
66 def testControllerCompletesToken(self):
66 def testControllerCompletesToken(self):
67 code = """longNameVariable=10"""
67 code = """longNameVariable=10"""
68 def testCompletes(result):
68 def testCompletes(result):
69 self.assert_("longNameVariable" in result)
69 self.assert_("longNameVariable" in result)
70
70
71 def testCompleteToken(result):
71 def testCompleteToken(result):
72 self.controller.complete("longNa").addCallback(testCompletes)
72 self.controller.complete("longNa").addCallback(testCompletes)
73
73
74 self.controller.execute(code).addCallback(testCompletes)
74 self.controller.execute(code).addCallback(testCompletes)
75
75
76
76
77 def testCurrentIndent(self):
77 def testCurrentIndent(self):
78 """test that current_indent_string returns current indent or None.
78 """test that current_indent_string returns current indent or None.
79 Uses _indent_for_block for direct unit testing.
79 Uses _indent_for_block for direct unit testing.
80 """
80 """
81
81
82 self.controller.tabUsesSpaces = True
82 self.controller.tabUsesSpaces = True
83 self.assert_(self.controller._indent_for_block("""a=3""") == None)
83 self.assert_(self.controller._indent_for_block("""a=3""") == None)
84 self.assert_(self.controller._indent_for_block("") == None)
84 self.assert_(self.controller._indent_for_block("") == None)
85 block = """def test():\n a=3"""
85 block = """def test():\n a=3"""
86 self.assert_(self.controller._indent_for_block(block) == \
86 self.assert_(self.controller._indent_for_block(block) == \
87 ' ' * self.controller.tabSpaces)
87 ' ' * self.controller.tabSpaces)
88
88
89 block = """if(True):\n%sif(False):\n%spass""" % \
89 block = """if(True):\n%sif(False):\n%spass""" % \
90 (' '*self.controller.tabSpaces,
90 (' '*self.controller.tabSpaces,
91 2*' '*self.controller.tabSpaces)
91 2*' '*self.controller.tabSpaces)
92 self.assert_(self.controller._indent_for_block(block) == \
92 self.assert_(self.controller._indent_for_block(block) == \
93 2*(' '*self.controller.tabSpaces))
93 2*(' '*self.controller.tabSpaces))
94
94
@@ -1,157 +1,157
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the frontendbase module."""
3 """This file contains unittests for the frontendbase module."""
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 import unittest
18 import unittest
19
19
20 try:
20 try:
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
22 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
23 from IPython.kernel.engineservice import EngineService
24 except ImportError:
24 except ImportError:
25 import nose
25 import nose
26 raise nose.SkipTest("This test requires zope.interface and Twisted")
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27
27
28 class FrontEndCallbackChecker(AsyncFrontEndBase):
28 class FrontEndCallbackChecker(AsyncFrontEndBase):
29 """FrontEndBase subclass for checking callbacks"""
29 """FrontEndBase subclass for checking callbacks"""
30 def __init__(self, engine=None, history=None):
30 def __init__(self, engine=None, history=None):
31 super(FrontEndCallbackChecker, self).__init__(engine=engine,
31 super(FrontEndCallbackChecker, self).__init__(engine=engine,
32 history=history)
32 history=history)
33 self.updateCalled = False
33 self.updateCalled = False
34 self.renderResultCalled = False
34 self.renderResultCalled = False
35 self.renderErrorCalled = False
35 self.renderErrorCalled = False
36
36
37 def update_cell_prompt(self, result, blockID=None):
37 def update_cell_prompt(self, result, blockID=None):
38 self.updateCalled = True
38 self.updateCalled = True
39 return result
39 return result
40
40
41 def render_result(self, result):
41 def render_result(self, result):
42 self.renderResultCalled = True
42 self.renderResultCalled = True
43 return result
43 return result
44
44
45
45
46 def render_error(self, failure):
46 def render_error(self, failure):
47 self.renderErrorCalled = True
47 self.renderErrorCalled = True
48 return failure
48 return failure
49
49
50
50
51
51
52
52
53 class TestAsyncFrontendBase(unittest.TestCase):
53 class TestAsyncFrontendBase(unittest.TestCase):
54 def setUp(self):
54 def setUp(self):
55 """Setup the EngineService and FrontEndBase"""
55 """Setup the EngineService and FrontEndBase"""
56
56
57 self.fb = FrontEndCallbackChecker(engine=EngineService())
57 self.fb = FrontEndCallbackChecker(engine=EngineService())
58
58
59
59
60 def test_implements_IFrontEnd(self):
60 def test_implements_IFrontEnd(self):
61 assert(frontendbase.IFrontEnd.implementedBy(
61 assert(frontendbase.IFrontEnd.implementedBy(
62 AsyncFrontEndBase))
62 AsyncFrontEndBase))
63
63
64
64
65 def test_is_complete_returns_False_for_incomplete_block(self):
65 def test_is_complete_returns_False_for_incomplete_block(self):
66 """"""
66 """"""
67
67
68 block = """def test(a):"""
68 block = """def test(a):"""
69
69
70 assert(self.fb.is_complete(block) == False)
70 assert(self.fb.is_complete(block) == False)
71
71
72 def test_is_complete_returns_True_for_complete_block(self):
72 def test_is_complete_returns_True_for_complete_block(self):
73 """"""
73 """"""
74
74
75 block = """def test(a): pass"""
75 block = """def test(a): pass"""
76
76
77 assert(self.fb.is_complete(block))
77 assert(self.fb.is_complete(block))
78
78
79 block = """a=3"""
79 block = """a=3"""
80
80
81 assert(self.fb.is_complete(block))
81 assert(self.fb.is_complete(block))
82
82
83
83
84 def test_blockID_added_to_result(self):
84 def test_blockID_added_to_result(self):
85 block = """3+3"""
85 block = """3+3"""
86
86
87 d = self.fb.execute(block, blockID='TEST_ID')
87 d = self.fb.execute(block, blockID='TEST_ID')
88
88
89 d.addCallback(self.checkBlockID, expected='TEST_ID')
89 d.addCallback(self.checkBlockID, expected='TEST_ID')
90
90
91 def test_blockID_added_to_failure(self):
91 def test_blockID_added_to_failure(self):
92 block = "raise Exception()"
92 block = "raise Exception()"
93
93
94 d = self.fb.execute(block,blockID='TEST_ID')
94 d = self.fb.execute(block,blockID='TEST_ID')
95 d.addErrback(self.checkFailureID, expected='TEST_ID')
95 d.addErrback(self.checkFailureID, expected='TEST_ID')
96
96
97 def checkBlockID(self, result, expected=""):
97 def checkBlockID(self, result, expected=""):
98 assert(result['blockID'] == expected)
98 assert(result['blockID'] == expected)
99
99
100
100
101 def checkFailureID(self, failure, expected=""):
101 def checkFailureID(self, failure, expected=""):
102 assert(failure.blockID == expected)
102 assert(failure.blockID == expected)
103
103
104
104
105 def test_callbacks_added_to_execute(self):
105 def test_callbacks_added_to_execute(self):
106 """test that
106 """test that
107 update_cell_prompt
107 update_cell_prompt
108 render_result
108 render_result
109
109
110 are added to execute request
110 are added to execute request
111 """
111 """
112
112
113 d = self.fb.execute("10+10")
113 d = self.fb.execute("10+10")
114 d.addCallback(self.checkCallbacks)
114 d.addCallback(self.checkCallbacks)
115
115
116
116
117 def checkCallbacks(self, result):
117 def checkCallbacks(self, result):
118 assert(self.fb.updateCalled)
118 assert(self.fb.updateCalled)
119 assert(self.fb.renderResultCalled)
119 assert(self.fb.renderResultCalled)
120
120
121
121
122 def test_error_callback_added_to_execute(self):
122 def test_error_callback_added_to_execute(self):
123 """test that render_error called on execution error"""
123 """test that render_error called on execution error"""
124
124
125 d = self.fb.execute("raise Exception()")
125 d = self.fb.execute("raise Exception()")
126 d.addCallback(self.checkRenderError)
126 d.addCallback(self.checkRenderError)
127
127
128 def checkRenderError(self, result):
128 def checkRenderError(self, result):
129 assert(self.fb.renderErrorCalled)
129 assert(self.fb.renderErrorCalled)
130
130
131 def test_history_returns_expected_block(self):
131 def test_history_returns_expected_block(self):
132 """Make sure history browsing doesn't fail"""
132 """Make sure history browsing doesn't fail"""
133
133
134 blocks = ["a=1","a=2","a=3"]
134 blocks = ["a=1","a=2","a=3"]
135 for b in blocks:
135 for b in blocks:
136 d = self.fb.execute(b)
136 d = self.fb.execute(b)
137
137
138 # d is now the deferred for the last executed block
138 # d is now the deferred for the last executed block
139 d.addCallback(self.historyTests, blocks)
139 d.addCallback(self.historyTests, blocks)
140
140
141
141
142 def historyTests(self, result, blocks):
142 def historyTests(self, result, blocks):
143 """historyTests"""
143 """historyTests"""
144
144
145 assert(len(blocks) >= 3)
145 assert(len(blocks) >= 3)
146 assert(self.fb.get_history_previous("") == blocks[-2])
146 assert(self.fb.get_history_previous("") == blocks[-2])
147 assert(self.fb.get_history_previous("") == blocks[-3])
147 assert(self.fb.get_history_previous("") == blocks[-3])
148 assert(self.fb.get_history_next() == blocks[-2])
148 assert(self.fb.get_history_next() == blocks[-2])
149
149
150
150
151 def test_history_returns_none_at_startup(self):
151 def test_history_returns_none_at_startup(self):
152 """test_history_returns_none_at_startup"""
152 """test_history_returns_none_at_startup"""
153
153
154 assert(self.fb.get_history_previous("")==None)
154 assert(self.fb.get_history_previous("")==None)
155 assert(self.fb.get_history_next()==None)
155 assert(self.fb.get_history_next()==None)
156
156
157
157
@@ -1,43 +1,44
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the kernel.engineservice.py module.
3 """This file contains unittests for the kernel.engineservice.py module.
4
4
5 Things that should be tested:
5 Things that should be tested:
6
6
7 - Should the EngineService return Deferred objects?
7 - Should the EngineService return Deferred objects?
8 - Run the same tests that are run in shell.py.
8 - Run the same tests that are run in shell.py.
9 - Make sure that the Interface is really implemented.
9 - Make sure that the Interface is really implemented.
10 - The startService and stopService methods.
10 - The startService and stopService methods.
11 """
11 """
12
12
13 __docformat__ = "restructuredtext en"
13 __docformat__ = "restructuredtext en"
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
16 # Copyright (C) 2008 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 try:
26 try:
27 from twisted.application.service import IService
27 from twisted.application.service import IService
28 from IPython.kernel.controllerservice import ControllerService
28 from IPython.kernel.controllerservice import ControllerService
29 from IPython.kernel.tests import multienginetest as met
29 from IPython.kernel.tests import multienginetest as met
30 from controllertest import IControllerCoreTestCase
30 from controllertest import IControllerCoreTestCase
31 from IPython.testing.util import DeferredTestCase
31 from IPython.testing.util import DeferredTestCase
32 except ImportError:
32 except ImportError:
33 pass
33 import nose
34 else:
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35 class BasicControllerServiceTest(DeferredTestCase,
35
36 IControllerCoreTestCase):
36 class BasicControllerServiceTest(DeferredTestCase,
37
37 IControllerCoreTestCase):
38 def setUp(self):
38
39 self.controller = ControllerService()
39 def setUp(self):
40 self.controller.startService()
40 self.controller = ControllerService()
41
41 self.controller.startService()
42 def tearDown(self):
42
43 self.controller.stopService()
43 def tearDown(self):
44 self.controller.stopService()
@@ -1,91 +1,93
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the enginepb.py module."""
3 """This file contains unittests for the enginepb.py module."""
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 try:
18 try:
19 from twisted.python import components
19 from twisted.python import components
20 from twisted.internet import reactor, defer
20 from twisted.internet import reactor, defer
21 from twisted.spread import pb
21 from twisted.spread import pb
22 from twisted.internet.base import DelayedCall
22 from twisted.internet.base import DelayedCall
23 DelayedCall.debug = True
23 DelayedCall.debug = True
24
24
25 import zope.interface as zi
25 import zope.interface as zi
26
26
27 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
27 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
28 from IPython.kernel import engineservice as es
28 from IPython.kernel import engineservice as es
29 from IPython.testing.util import DeferredTestCase
29 from IPython.testing.util import DeferredTestCase
30 from IPython.kernel.controllerservice import IControllerBase
30 from IPython.kernel.controllerservice import IControllerBase
31 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
31 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
32 from IPython.kernel.engineservice import IEngineQueued
32 from IPython.kernel.engineservice import IEngineQueued
33 from IPython.kernel.engineconnector import EngineConnector
33 from IPython.kernel.engineconnector import EngineConnector
34
34
35 from IPython.kernel.tests.engineservicetest import \
35 from IPython.kernel.tests.engineservicetest import \
36 IEngineCoreTestCase, \
36 IEngineCoreTestCase, \
37 IEngineSerializedTestCase, \
37 IEngineSerializedTestCase, \
38 IEngineQueuedTestCase
38 IEngineQueuedTestCase
39 except ImportError:
39 except ImportError:
40 pass
40 import nose
41 else:
41 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
42 class EngineFCTest(DeferredTestCase,
42
43 IEngineCoreTestCase,
43
44 IEngineSerializedTestCase,
44 class EngineFCTest(DeferredTestCase,
45 IEngineQueuedTestCase
45 IEngineCoreTestCase,
46 ):
46 IEngineSerializedTestCase,
47
47 IEngineQueuedTestCase
48 zi.implements(IControllerBase)
48 ):
49
49
50 def setUp(self):
50 zi.implements(IControllerBase)
51
51
52 # Start a server and append to self.servers
52 def setUp(self):
53 self.controller_reference = FCRemoteEngineRefFromService(self)
53
54 self.controller_tub = Tub()
54 # Start a server and append to self.servers
55 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
55 self.controller_reference = FCRemoteEngineRefFromService(self)
56 self.controller_tub.setLocation('127.0.0.1:10105')
56 self.controller_tub = Tub()
57
57 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
58 furl = self.controller_tub.registerReference(self.controller_reference)
58 self.controller_tub.setLocation('127.0.0.1:10105')
59 self.controller_tub.startService()
59
60
60 furl = self.controller_tub.registerReference(self.controller_reference)
61 # Start an EngineService and append to services/client
61 self.controller_tub.startService()
62 self.engine_service = es.EngineService()
62
63 self.engine_service.startService()
63 # Start an EngineService and append to services/client
64 self.engine_tub = Tub()
64 self.engine_service = es.EngineService()
65 self.engine_tub.startService()
65 self.engine_service.startService()
66 engine_connector = EngineConnector(self.engine_tub)
66 self.engine_tub = Tub()
67 d = engine_connector.connect_to_controller(self.engine_service, furl)
67 self.engine_tub.startService()
68 # This deferred doesn't fire until after register_engine has returned and
68 engine_connector = EngineConnector(self.engine_tub)
69 # thus, self.engine has been defined and the tets can proceed.
69 d = engine_connector.connect_to_controller(self.engine_service, furl)
70 return d
70 # This deferred doesn't fire until after register_engine has returned and
71
71 # thus, self.engine has been defined and the tets can proceed.
72 def tearDown(self):
72 return d
73 dlist = []
73
74 # Shut down the engine
74 def tearDown(self):
75 d = self.engine_tub.stopService()
75 dlist = []
76 dlist.append(d)
76 # Shut down the engine
77 # Shut down the controller
77 d = self.engine_tub.stopService()
78 d = self.controller_tub.stopService()
78 dlist.append(d)
79 dlist.append(d)
79 # Shut down the controller
80 return defer.DeferredList(dlist)
80 d = self.controller_tub.stopService()
81
81 dlist.append(d)
82 #---------------------------------------------------------------------------
82 return defer.DeferredList(dlist)
83 # Make me look like a basic controller
83
84 #---------------------------------------------------------------------------
84 #---------------------------------------------------------------------------
85
85 # Make me look like a basic controller
86 def register_engine(self, engine_ref, id=None, ip=None, port=None, pid=None):
86 #---------------------------------------------------------------------------
87 self.engine = IEngineQueued(IEngineBase(engine_ref))
87
88 return {'id':id}
88 def register_engine(self, engine_ref, id=None, ip=None, port=None, pid=None):
89
89 self.engine = IEngineQueued(IEngineBase(engine_ref))
90 def unregister_engine(self, id):
90 return {'id':id}
91 pass No newline at end of file
91
92 def unregister_engine(self, id):
93 pass No newline at end of file
@@ -1,78 +1,80
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the kernel.engineservice.py module.
3 """This file contains unittests for the kernel.engineservice.py module.
4
4
5 Things that should be tested:
5 Things that should be tested:
6
6
7 - Should the EngineService return Deferred objects?
7 - Should the EngineService return Deferred objects?
8 - Run the same tests that are run in shell.py.
8 - Run the same tests that are run in shell.py.
9 - Make sure that the Interface is really implemented.
9 - Make sure that the Interface is really implemented.
10 - The startService and stopService methods.
10 - The startService and stopService methods.
11 """
11 """
12
12
13 __docformat__ = "restructuredtext en"
13 __docformat__ = "restructuredtext en"
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Copyright (C) 2008 The IPython Development Team
16 # Copyright (C) 2008 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-------------------------------------------------------------------------------
20 #-------------------------------------------------------------------------------
21
21
22 #-------------------------------------------------------------------------------
22 #-------------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 try:
26 try:
27 from twisted.internet import defer
27 from twisted.internet import defer
28 from twisted.application.service import IService
28 from twisted.application.service import IService
29
29
30 from IPython.kernel import engineservice as es
30 from IPython.kernel import engineservice as es
31 from IPython.testing.util import DeferredTestCase
31 from IPython.testing.util import DeferredTestCase
32 from IPython.kernel.tests.engineservicetest import \
32 from IPython.kernel.tests.engineservicetest import \
33 IEngineCoreTestCase, \
33 IEngineCoreTestCase, \
34 IEngineSerializedTestCase, \
34 IEngineSerializedTestCase, \
35 IEngineQueuedTestCase, \
35 IEngineQueuedTestCase, \
36 IEnginePropertiesTestCase
36 IEnginePropertiesTestCase
37 except ImportError:
37 except ImportError:
38 pass
38 import nose
39 else:
39 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
40 class BasicEngineServiceTest(DeferredTestCase,
40
41 IEngineCoreTestCase,
41
42 IEngineSerializedTestCase,
42 class BasicEngineServiceTest(DeferredTestCase,
43 IEnginePropertiesTestCase):
43 IEngineCoreTestCase,
44
44 IEngineSerializedTestCase,
45 def setUp(self):
45 IEnginePropertiesTestCase):
46 self.engine = es.EngineService()
46
47 self.engine.startService()
47 def setUp(self):
48
48 self.engine = es.EngineService()
49 def tearDown(self):
49 self.engine.startService()
50 return self.engine.stopService()
50
51
51 def tearDown(self):
52 class ThreadedEngineServiceTest(DeferredTestCase,
52 return self.engine.stopService()
53 IEngineCoreTestCase,
53
54 IEngineSerializedTestCase,
54 class ThreadedEngineServiceTest(DeferredTestCase,
55 IEnginePropertiesTestCase):
55 IEngineCoreTestCase,
56
56 IEngineSerializedTestCase,
57 def setUp(self):
57 IEnginePropertiesTestCase):
58 self.engine = es.ThreadedEngineService()
58
59 self.engine.startService()
59 def setUp(self):
60 self.engine = es.ThreadedEngineService()
61 self.engine.startService()
62
63 def tearDown(self):
64 return self.engine.stopService()
65
66 class QueuedEngineServiceTest(DeferredTestCase,
67 IEngineCoreTestCase,
68 IEngineSerializedTestCase,
69 IEnginePropertiesTestCase,
70 IEngineQueuedTestCase):
71
72 def setUp(self):
73 self.rawEngine = es.EngineService()
74 self.rawEngine.startService()
75 self.engine = es.IEngineQueued(self.rawEngine)
60
76
61 def tearDown(self):
77 def tearDown(self):
62 return self.engine.stopService()
78 return self.rawEngine.stopService()
63
79
64 class QueuedEngineServiceTest(DeferredTestCase,
80
65 IEngineCoreTestCase,
66 IEngineSerializedTestCase,
67 IEnginePropertiesTestCase,
68 IEngineQueuedTestCase):
69
70 def setUp(self):
71 self.rawEngine = es.EngineService()
72 self.rawEngine.startService()
73 self.engine = es.IEngineQueued(self.rawEngine)
74
75 def tearDown(self):
76 return self.rawEngine.stopService()
77
78
@@ -1,54 +1,56
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 try:
18 try:
19 from twisted.internet import defer
19 from twisted.internet import defer
20 from IPython.testing.util import DeferredTestCase
20 from IPython.testing.util import DeferredTestCase
21 from IPython.kernel.controllerservice import ControllerService
21 from IPython.kernel.controllerservice import ControllerService
22 from IPython.kernel import multiengine as me
22 from IPython.kernel import multiengine as me
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
24 ISynchronousMultiEngineTestCase)
24 ISynchronousMultiEngineTestCase)
25 except ImportError:
25 except ImportError:
26 pass
26 import nose
27 else:
27 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
28 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
28
29
30 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
31
32 def setUp(self):
33 self.controller = ControllerService()
34 self.controller.startService()
35 self.multiengine = me.IMultiEngine(self.controller)
36 self.engines = []
29
37
30 def setUp(self):
38 def tearDown(self):
31 self.controller = ControllerService()
39 self.controller.stopService()
32 self.controller.startService()
40 for e in self.engines:
33 self.multiengine = me.IMultiEngine(self.controller)
41 e.stopService()
34 self.engines = []
42
35
43
36 def tearDown(self):
44 class SynchronousMultiEngineTestCase(DeferredTestCase, ISynchronousMultiEngineTestCase):
37 self.controller.stopService()
45
38 for e in self.engines:
46 def setUp(self):
39 e.stopService()
47 self.controller = ControllerService()
40
48 self.controller.startService()
41
49 self.multiengine = me.ISynchronousMultiEngine(me.IMultiEngine(self.controller))
42 class SynchronousMultiEngineTestCase(DeferredTestCase, ISynchronousMultiEngineTestCase):
50 self.engines = []
43
51
44 def setUp(self):
52 def tearDown(self):
45 self.controller = ControllerService()
53 self.controller.stopService()
46 self.controller.startService()
54 for e in self.engines:
47 self.multiengine = me.ISynchronousMultiEngine(me.IMultiEngine(self.controller))
55 e.stopService()
48 self.engines = []
49
50 def tearDown(self):
51 self.controller.stopService()
52 for e in self.engines:
53 e.stopService()
54
56
@@ -1,144 +1,144
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 #-------------------------------------------------------------------------------
6 #-------------------------------------------------------------------------------
7 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 try:
17 try:
18 from twisted.internet import defer, reactor
18 from twisted.internet import defer, reactor
19
19
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
21
21
22 from IPython.testing.util import DeferredTestCase
22 from IPython.testing.util import DeferredTestCase
23 from IPython.kernel.controllerservice import ControllerService
23 from IPython.kernel.controllerservice import ControllerService
24 from IPython.kernel.multiengine import IMultiEngine
24 from IPython.kernel.multiengine import IMultiEngine
25 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
25 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
27 from IPython.kernel import multiengine as me
27 from IPython.kernel import multiengine as me
28 from IPython.kernel.clientconnector import ClientConnector
28 from IPython.kernel.clientconnector import ClientConnector
29 from IPython.kernel.parallelfunction import ParallelFunction
29 from IPython.kernel.parallelfunction import ParallelFunction
30 from IPython.kernel.error import CompositeError
30 from IPython.kernel.error import CompositeError
31 from IPython.kernel.util import printer
31 from IPython.kernel.util import printer
32 except ImportError:
32 except ImportError:
33 pass
33 import nose
34 else:
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35
35
36 def _raise_it(f):
36 def _raise_it(f):
37 try:
37 try:
38 f.raiseException()
38 f.raiseException()
39 except CompositeError, e:
39 except CompositeError, e:
40 e.raise_exception()
40 e.raise_exception()
41
42
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
44
45 def setUp(self):
41
46
47 self.engines = []
48
49 self.controller = ControllerService()
50 self.controller.startService()
51 self.imultiengine = IMultiEngine(self.controller)
52 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
53
54 self.controller_tub = Tub()
55 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.setLocation('127.0.0.1:10105')
42
57
43 class FullSynchronousMultiEngineTestCase(DeferredTestCase, IFullSynchronousMultiEngineTestCase):
58 furl = self.controller_tub.registerReference(self.mec_referenceable)
59 self.controller_tub.startService()
44
60
45 def setUp(self):
61 self.client_tub = ClientConnector()
46
62 d = self.client_tub.get_multiengine_client(furl)
47 self.engines = []
63 d.addCallback(self.handle_got_client)
48
64 return d
49 self.controller = ControllerService()
50 self.controller.startService()
51 self.imultiengine = IMultiEngine(self.controller)
52 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
53
54 self.controller_tub = Tub()
55 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
56 self.controller_tub.setLocation('127.0.0.1:10105')
57
58 furl = self.controller_tub.registerReference(self.mec_referenceable)
59 self.controller_tub.startService()
60
61 self.client_tub = ClientConnector()
62 d = self.client_tub.get_multiengine_client(furl)
63 d.addCallback(self.handle_got_client)
64 return d
65
66 def handle_got_client(self, client):
67 self.multiengine = client
68
65
69 def tearDown(self):
66 def handle_got_client(self, client):
70 dlist = []
67 self.multiengine = client
71 # Shut down the multiengine client
68
72 d = self.client_tub.tub.stopService()
69 def tearDown(self):
73 dlist.append(d)
70 dlist = []
74 # Shut down the engines
71 # Shut down the multiengine client
75 for e in self.engines:
72 d = self.client_tub.tub.stopService()
76 e.stopService()
73 dlist.append(d)
77 # Shut down the controller
74 # Shut down the engines
78 d = self.controller_tub.stopService()
75 for e in self.engines:
79 d.addBoth(lambda _: self.controller.stopService())
76 e.stopService()
80 dlist.append(d)
77 # Shut down the controller
81 return defer.DeferredList(dlist)
78 d = self.controller_tub.stopService()
79 d.addBoth(lambda _: self.controller.stopService())
80 dlist.append(d)
81 return defer.DeferredList(dlist)
82
82
83 def test_mapper(self):
83 def test_mapper(self):
84 self.addEngine(4)
84 self.addEngine(4)
85 m = self.multiengine.mapper()
85 m = self.multiengine.mapper()
86 self.assertEquals(m.multiengine,self.multiengine)
86 self.assertEquals(m.multiengine,self.multiengine)
87 self.assertEquals(m.dist,'b')
87 self.assertEquals(m.dist,'b')
88 self.assertEquals(m.targets,'all')
88 self.assertEquals(m.targets,'all')
89 self.assertEquals(m.block,True)
89 self.assertEquals(m.block,True)
90
90
91 def test_map_default(self):
91 def test_map_default(self):
92 self.addEngine(4)
92 self.addEngine(4)
93 m = self.multiengine.mapper()
93 m = self.multiengine.mapper()
94 d = m.map(lambda x: 2*x, range(10))
94 d = m.map(lambda x: 2*x, range(10))
95 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
95 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
96 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
96 d.addCallback(lambda _: self.multiengine.map(lambda x: 2*x, range(10)))
97 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
97 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
98 return d
98 return d
99
99
100 def test_map_noblock(self):
100 def test_map_noblock(self):
101 self.addEngine(4)
101 self.addEngine(4)
102 m = self.multiengine.mapper(block=False)
102 m = self.multiengine.mapper(block=False)
103 d = m.map(lambda x: 2*x, range(10))
103 d = m.map(lambda x: 2*x, range(10))
104 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
104 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
105 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
106 return d
106 return d
107
107
108 def test_mapper_fail(self):
108 def test_mapper_fail(self):
109 self.addEngine(4)
109 self.addEngine(4)
110 m = self.multiengine.mapper()
110 m = self.multiengine.mapper()
111 d = m.map(lambda x: 1/0, range(10))
111 d = m.map(lambda x: 1/0, range(10))
112 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
112 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
113 return d
113 return d
114
114
115 def test_parallel(self):
115 def test_parallel(self):
116 self.addEngine(4)
116 self.addEngine(4)
117 p = self.multiengine.parallel()
117 p = self.multiengine.parallel()
118 self.assert_(isinstance(p, ParallelFunction))
118 self.assert_(isinstance(p, ParallelFunction))
119 @p
119 @p
120 def f(x): return 2*x
120 def f(x): return 2*x
121 d = f(range(10))
121 d = f(range(10))
122 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
122 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
123 return d
123 return d
124
124
125 def test_parallel_noblock(self):
125 def test_parallel_noblock(self):
126 self.addEngine(1)
126 self.addEngine(1)
127 p = self.multiengine.parallel(block=False)
127 p = self.multiengine.parallel(block=False)
128 self.assert_(isinstance(p, ParallelFunction))
128 self.assert_(isinstance(p, ParallelFunction))
129 @p
129 @p
130 def f(x): return 2*x
130 def f(x): return 2*x
131 d = f(range(10))
131 d = f(range(10))
132 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
132 d.addCallback(lambda did: self.multiengine.get_pending_deferred(did, True))
133 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
133 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
134 return d
134 return d
135
135
136 def test_parallel_fail(self):
136 def test_parallel_fail(self):
137 self.addEngine(4)
137 self.addEngine(4)
138 p = self.multiengine.parallel()
138 p = self.multiengine.parallel()
139 self.assert_(isinstance(p, ParallelFunction))
139 self.assert_(isinstance(p, ParallelFunction))
140 @p
140 @p
141 def f(x): return 1/0
141 def f(x): return 1/0
142 d = f(range(10))
142 d = f(range(10))
143 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
143 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
144 return d No newline at end of file
144 return d No newline at end of file
@@ -1,102 +1,102
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the shell.py module."""
3 """This file contains unittests for the shell.py module."""
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 try:
18 try:
19 import zope.interface as zi
19 import zope.interface as zi
20 from twisted.trial import unittest
20 from twisted.trial import unittest
21 from IPython.testing.util import DeferredTestCase
21 from IPython.testing.util import DeferredTestCase
22
22
23 from IPython.kernel.newserialized import \
23 from IPython.kernel.newserialized import \
24 ISerialized, \
24 ISerialized, \
25 IUnSerialized, \
25 IUnSerialized, \
26 Serialized, \
26 Serialized, \
27 UnSerialized, \
27 UnSerialized, \
28 SerializeIt, \
28 SerializeIt, \
29 UnSerializeIt
29 UnSerializeIt
30 except ImportError:
30 except ImportError:
31 pass
31 import nose
32 else:
32 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
33 #-------------------------------------------------------------------------------
33
34 # Tests
34 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
35 # Tests
36 #-------------------------------------------------------------------------------
37
38 class SerializedTestCase(unittest.TestCase):
39
40 def setUp(self):
41 pass
36
42
37 class SerializedTestCase(unittest.TestCase):
43 def tearDown(self):
44 pass
38
45
39 def setUp(self):
46 def testSerializedInterfaces(self):
40 pass
41
42 def tearDown(self):
43 pass
44
45 def testSerializedInterfaces(self):
46
47
47 us = UnSerialized({'a':10, 'b':range(10)})
48 us = UnSerialized({'a':10, 'b':range(10)})
48 s = ISerialized(us)
49 s = ISerialized(us)
49 uss = IUnSerialized(s)
50 uss = IUnSerialized(s)
50 self.assert_(ISerialized.providedBy(s))
51 self.assert_(ISerialized.providedBy(s))
51 self.assert_(IUnSerialized.providedBy(us))
52 self.assert_(IUnSerialized.providedBy(us))
52 self.assert_(IUnSerialized.providedBy(uss))
53 self.assert_(IUnSerialized.providedBy(uss))
53 for m in list(ISerialized):
54 for m in list(ISerialized):
54 self.assert_(hasattr(s, m))
55 self.assert_(hasattr(s, m))
55 for m in list(IUnSerialized):
56 for m in list(IUnSerialized):
56 self.assert_(hasattr(us, m))
57 self.assert_(hasattr(us, m))
57 for m in list(IUnSerialized):
58 for m in list(IUnSerialized):
58 self.assert_(hasattr(uss, m))
59 self.assert_(hasattr(uss, m))
59
60
60 def testPickleSerialized(self):
61 def testPickleSerialized(self):
61 obj = {'a':1.45345, 'b':'asdfsdf', 'c':10000L}
62 obj = {'a':1.45345, 'b':'asdfsdf', 'c':10000L}
62 original = UnSerialized(obj)
63 original = UnSerialized(obj)
63 originalSer = ISerialized(original)
64 originalSer = ISerialized(original)
64 firstData = originalSer.getData()
65 firstData = originalSer.getData()
65 firstTD = originalSer.getTypeDescriptor()
66 firstTD = originalSer.getTypeDescriptor()
66 firstMD = originalSer.getMetadata()
67 firstMD = originalSer.getMetadata()
67 self.assert_(firstTD == 'pickle')
68 self.assert_(firstTD == 'pickle')
68 self.assert_(firstMD == {})
69 self.assert_(firstMD == {})
69 unSerialized = IUnSerialized(originalSer)
70 unSerialized = IUnSerialized(originalSer)
70 secondObj = unSerialized.getObject()
71 secondObj = unSerialized.getObject()
71 for k, v in secondObj.iteritems():
72 for k, v in secondObj.iteritems():
72 self.assert_(obj[k] == v)
73 self.assert_(obj[k] == v)
73 secondSer = ISerialized(UnSerialized(secondObj))
74 secondSer = ISerialized(UnSerialized(secondObj))
74 self.assert_(firstData == secondSer.getData())
75 self.assert_(firstData == secondSer.getData())
75 self.assert_(firstTD == secondSer.getTypeDescriptor() )
76 self.assert_(firstTD == secondSer.getTypeDescriptor() )
76 self.assert_(firstMD == secondSer.getMetadata())
77 self.assert_(firstMD == secondSer.getMetadata())
78
79 def testNDArraySerialized(self):
80 try:
81 import numpy
82 except ImportError:
83 pass
84 else:
85 a = numpy.linspace(0.0, 1.0, 1000)
86 unSer1 = UnSerialized(a)
87 ser1 = ISerialized(unSer1)
88 td = ser1.getTypeDescriptor()
89 self.assert_(td == 'ndarray')
90 md = ser1.getMetadata()
91 self.assert_(md['shape'] == a.shape)
92 self.assert_(md['dtype'] == a.dtype.str)
93 buff = ser1.getData()
94 self.assert_(buff == numpy.getbuffer(a))
95 s = Serialized(buff, td, md)
96 us = IUnSerialized(s)
97 final = us.getObject()
98 self.assert_(numpy.getbuffer(a) == numpy.getbuffer(final))
99 self.assert_(a.dtype.str == final.dtype.str)
100 self.assert_(a.shape == final.shape)
101
77
102
78 def testNDArraySerialized(self):
79 try:
80 import numpy
81 except ImportError:
82 pass
83 else:
84 a = numpy.linspace(0.0, 1.0, 1000)
85 unSer1 = UnSerialized(a)
86 ser1 = ISerialized(unSer1)
87 td = ser1.getTypeDescriptor()
88 self.assert_(td == 'ndarray')
89 md = ser1.getMetadata()
90 self.assert_(md['shape'] == a.shape)
91 self.assert_(md['dtype'] == a.dtype.str)
92 buff = ser1.getData()
93 self.assert_(buff == numpy.getbuffer(a))
94 s = Serialized(buff, td, md)
95 us = IUnSerialized(s)
96 final = us.getObject()
97 self.assert_(numpy.getbuffer(a) == numpy.getbuffer(final))
98 self.assert_(a.dtype.str == final.dtype.str)
99 self.assert_(a.shape == final.shape)
100
101
102 No newline at end of file
@@ -1,186 +1,186
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 """Tests for pendingdeferred.py"""
4 """Tests for pendingdeferred.py"""
5
5
6 __docformat__ = "restructuredtext en"
6 __docformat__ = "restructuredtext en"
7
7
8 #-------------------------------------------------------------------------------
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
9 # Copyright (C) 2008 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 try:
19 try:
20 from twisted.internet import defer
20 from twisted.internet import defer
21 from twisted.python import failure
21 from twisted.python import failure
22
22
23 from IPython.testing.util import DeferredTestCase
23 from IPython.testing.util import DeferredTestCase
24 import IPython.kernel.pendingdeferred as pd
24 import IPython.kernel.pendingdeferred as pd
25 from IPython.kernel import error
25 from IPython.kernel import error
26 from IPython.kernel.util import printer
26 from IPython.kernel.util import printer
27 except ImportError:
27 except ImportError:
28 pass
28 import nose
29 else:
29 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
30
30
31 class Foo(object):
31 class Foo(object):
32
33 def bar(self, bahz):
34 return defer.succeed('blahblah: %s' % bahz)
35
32
36 class TwoPhaseFoo(pd.PendingDeferredManager):
33 def bar(self, bahz):
37
34 return defer.succeed('blahblah: %s' % bahz)
38 def __init__(self, foo):
39 self.foo = foo
40 pd.PendingDeferredManager.__init__(self)
41
35
42 @pd.two_phase
36 class TwoPhaseFoo(pd.PendingDeferredManager):
43 def bar(self, bahz):
44 return self.foo.bar(bahz)
45
37
46 class PendingDeferredManagerTest(DeferredTestCase):
38 def __init__(self, foo):
47
39 self.foo = foo
48 def setUp(self):
40 pd.PendingDeferredManager.__init__(self)
49 self.pdm = pd.PendingDeferredManager()
50
51 def tearDown(self):
52 pass
53
54 def testBasic(self):
55 dDict = {}
56 # Create 10 deferreds and save them
57 for i in range(10):
58 d = defer.Deferred()
59 did = self.pdm.save_pending_deferred(d)
60 dDict[did] = d
61 # Make sure they are begin saved
62 for k in dDict.keys():
63 self.assert_(self.pdm.quick_has_id(k))
64 # Get the pending deferred (block=True), then callback with 'foo' and compare
65 for did in dDict.keys()[0:5]:
66 d = self.pdm.get_pending_deferred(did,block=True)
67 dDict[did].callback('foo')
68 d.addCallback(lambda r: self.assert_(r=='foo'))
69 # Get the pending deferreds with (block=False) and make sure ResultNotCompleted is raised
70 for did in dDict.keys()[5:10]:
71 d = self.pdm.get_pending_deferred(did,block=False)
72 d.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
73 # Now callback the last 5, get them and compare.
74 for did in dDict.keys()[5:10]:
75 dDict[did].callback('foo')
76 d = self.pdm.get_pending_deferred(did,block=False)
77 d.addCallback(lambda r: self.assert_(r=='foo'))
78
79 def test_save_then_delete(self):
80 d = defer.Deferred()
81 did = self.pdm.save_pending_deferred(d)
82 self.assert_(self.pdm.quick_has_id(did))
83 self.pdm.delete_pending_deferred(did)
84 self.assert_(not self.pdm.quick_has_id(did))
85
86 def test_save_get_delete(self):
87 d = defer.Deferred()
88 did = self.pdm.save_pending_deferred(d)
89 d2 = self.pdm.get_pending_deferred(did,True)
90 d2.addErrback(lambda f: self.assertRaises(error.AbortedPendingDeferredError, f.raiseException))
91 self.pdm.delete_pending_deferred(did)
92 return d2
93
94 def test_double_get(self):
95 d = defer.Deferred()
96 did = self.pdm.save_pending_deferred(d)
97 d2 = self.pdm.get_pending_deferred(did,True)
98 d3 = self.pdm.get_pending_deferred(did,True)
99 d3.addErrback(lambda f: self.assertRaises(error.InvalidDeferredID, f.raiseException))
100
101 def test_get_after_callback(self):
102 d = defer.Deferred()
103 did = self.pdm.save_pending_deferred(d)
104 d.callback('foo')
105 d2 = self.pdm.get_pending_deferred(did,True)
106 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
107 self.assert_(not self.pdm.quick_has_id(did))
108
41
109 def test_get_before_callback(self):
42 @pd.two_phase
110 d = defer.Deferred()
43 def bar(self, bahz):
111 did = self.pdm.save_pending_deferred(d)
44 return self.foo.bar(bahz)
112 d2 = self.pdm.get_pending_deferred(did,True)
113 d.callback('foo')
114 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
115 self.assert_(not self.pdm.quick_has_id(did))
116 d = defer.Deferred()
117 did = self.pdm.save_pending_deferred(d)
118 d2 = self.pdm.get_pending_deferred(did,True)
119 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
120 d.callback('foo')
121 self.assert_(not self.pdm.quick_has_id(did))
122
123 def test_get_after_errback(self):
124 class MyError(Exception):
125 pass
126 d = defer.Deferred()
127 did = self.pdm.save_pending_deferred(d)
128 d.errback(failure.Failure(MyError('foo')))
129 d2 = self.pdm.get_pending_deferred(did,True)
130 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
131 self.assert_(not self.pdm.quick_has_id(did))
132
133 def test_get_before_errback(self):
134 class MyError(Exception):
135 pass
136 d = defer.Deferred()
137 did = self.pdm.save_pending_deferred(d)
138 d2 = self.pdm.get_pending_deferred(did,True)
139 d.errback(failure.Failure(MyError('foo')))
140 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
141 self.assert_(not self.pdm.quick_has_id(did))
142 d = defer.Deferred()
143 did = self.pdm.save_pending_deferred(d)
144 d2 = self.pdm.get_pending_deferred(did,True)
145 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
146 d.errback(failure.Failure(MyError('foo')))
147 self.assert_(not self.pdm.quick_has_id(did))
148
149 def test_noresult_noblock(self):
150 d = defer.Deferred()
151 did = self.pdm.save_pending_deferred(d)
152 d2 = self.pdm.get_pending_deferred(did,False)
153 d2.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
154
45
155 def test_with_callbacks(self):
46 class PendingDeferredManagerTest(DeferredTestCase):
156 d = defer.Deferred()
47
157 d.addCallback(lambda r: r+' foo')
48 def setUp(self):
158 d.addCallback(lambda r: r+' bar')
49 self.pdm = pd.PendingDeferredManager()
159 did = self.pdm.save_pending_deferred(d)
160 d2 = self.pdm.get_pending_deferred(did,True)
161 d.callback('bam')
162 d2.addCallback(lambda r: self.assertEquals(r,'bam foo bar'))
163
50
164 def test_with_errbacks(self):
51 def tearDown(self):
165 class MyError(Exception):
52 pass
166 pass
53
54 def testBasic(self):
55 dDict = {}
56 # Create 10 deferreds and save them
57 for i in range(10):
167 d = defer.Deferred()
58 d = defer.Deferred()
168 d.addCallback(lambda r: 'foo')
169 d.addErrback(lambda f: 'caught error')
170 did = self.pdm.save_pending_deferred(d)
59 did = self.pdm.save_pending_deferred(d)
171 d2 = self.pdm.get_pending_deferred(did,True)
60 dDict[did] = d
172 d.errback(failure.Failure(MyError('bam')))
61 # Make sure they are begin saved
173 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
62 for k in dDict.keys():
63 self.assert_(self.pdm.quick_has_id(k))
64 # Get the pending deferred (block=True), then callback with 'foo' and compare
65 for did in dDict.keys()[0:5]:
66 d = self.pdm.get_pending_deferred(did,block=True)
67 dDict[did].callback('foo')
68 d.addCallback(lambda r: self.assert_(r=='foo'))
69 # Get the pending deferreds with (block=False) and make sure ResultNotCompleted is raised
70 for did in dDict.keys()[5:10]:
71 d = self.pdm.get_pending_deferred(did,block=False)
72 d.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
73 # Now callback the last 5, get them and compare.
74 for did in dDict.keys()[5:10]:
75 dDict[did].callback('foo')
76 d = self.pdm.get_pending_deferred(did,block=False)
77 d.addCallback(lambda r: self.assert_(r=='foo'))
78
79 def test_save_then_delete(self):
80 d = defer.Deferred()
81 did = self.pdm.save_pending_deferred(d)
82 self.assert_(self.pdm.quick_has_id(did))
83 self.pdm.delete_pending_deferred(did)
84 self.assert_(not self.pdm.quick_has_id(did))
85
86 def test_save_get_delete(self):
87 d = defer.Deferred()
88 did = self.pdm.save_pending_deferred(d)
89 d2 = self.pdm.get_pending_deferred(did,True)
90 d2.addErrback(lambda f: self.assertRaises(error.AbortedPendingDeferredError, f.raiseException))
91 self.pdm.delete_pending_deferred(did)
92 return d2
93
94 def test_double_get(self):
95 d = defer.Deferred()
96 did = self.pdm.save_pending_deferred(d)
97 d2 = self.pdm.get_pending_deferred(did,True)
98 d3 = self.pdm.get_pending_deferred(did,True)
99 d3.addErrback(lambda f: self.assertRaises(error.InvalidDeferredID, f.raiseException))
100
101 def test_get_after_callback(self):
102 d = defer.Deferred()
103 did = self.pdm.save_pending_deferred(d)
104 d.callback('foo')
105 d2 = self.pdm.get_pending_deferred(did,True)
106 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
107 self.assert_(not self.pdm.quick_has_id(did))
108
109 def test_get_before_callback(self):
110 d = defer.Deferred()
111 did = self.pdm.save_pending_deferred(d)
112 d2 = self.pdm.get_pending_deferred(did,True)
113 d.callback('foo')
114 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
115 self.assert_(not self.pdm.quick_has_id(did))
116 d = defer.Deferred()
117 did = self.pdm.save_pending_deferred(d)
118 d2 = self.pdm.get_pending_deferred(did,True)
119 d2.addCallback(lambda r: self.assertEquals(r,'foo'))
120 d.callback('foo')
121 self.assert_(not self.pdm.quick_has_id(did))
122
123 def test_get_after_errback(self):
124 class MyError(Exception):
125 pass
126 d = defer.Deferred()
127 did = self.pdm.save_pending_deferred(d)
128 d.errback(failure.Failure(MyError('foo')))
129 d2 = self.pdm.get_pending_deferred(did,True)
130 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
131 self.assert_(not self.pdm.quick_has_id(did))
132
133 def test_get_before_errback(self):
134 class MyError(Exception):
135 pass
136 d = defer.Deferred()
137 did = self.pdm.save_pending_deferred(d)
138 d2 = self.pdm.get_pending_deferred(did,True)
139 d.errback(failure.Failure(MyError('foo')))
140 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
141 self.assert_(not self.pdm.quick_has_id(did))
142 d = defer.Deferred()
143 did = self.pdm.save_pending_deferred(d)
144 d2 = self.pdm.get_pending_deferred(did,True)
145 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
146 d.errback(failure.Failure(MyError('foo')))
147 self.assert_(not self.pdm.quick_has_id(did))
174
148
175 def test_nested_deferreds(self):
149 def test_noresult_noblock(self):
176 d = defer.Deferred()
150 d = defer.Deferred()
177 d2 = defer.Deferred()
151 did = self.pdm.save_pending_deferred(d)
178 d.addCallback(lambda r: d2)
152 d2 = self.pdm.get_pending_deferred(did,False)
179 did = self.pdm.save_pending_deferred(d)
153 d2.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
180 d.callback('foo')
154
181 d3 = self.pdm.get_pending_deferred(did,False)
155 def test_with_callbacks(self):
182 d3.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
156 d = defer.Deferred()
183 d2.callback('bar')
157 d.addCallback(lambda r: r+' foo')
184 d3 = self.pdm.get_pending_deferred(did,False)
158 d.addCallback(lambda r: r+' bar')
185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
159 did = self.pdm.save_pending_deferred(d)
160 d2 = self.pdm.get_pending_deferred(did,True)
161 d.callback('bam')
162 d2.addCallback(lambda r: self.assertEquals(r,'bam foo bar'))
163
164 def test_with_errbacks(self):
165 class MyError(Exception):
166 pass
167 d = defer.Deferred()
168 d.addCallback(lambda r: 'foo')
169 d.addErrback(lambda f: 'caught error')
170 did = self.pdm.save_pending_deferred(d)
171 d2 = self.pdm.get_pending_deferred(did,True)
172 d.errback(failure.Failure(MyError('bam')))
173 d2.addErrback(lambda f: self.assertRaises(MyError, f.raiseException))
174
175 def test_nested_deferreds(self):
176 d = defer.Deferred()
177 d2 = defer.Deferred()
178 d.addCallback(lambda r: d2)
179 did = self.pdm.save_pending_deferred(d)
180 d.callback('foo')
181 d3 = self.pdm.get_pending_deferred(did,False)
182 d3.addErrback(lambda f: self.assertRaises(error.ResultNotCompleted, f.raiseException))
183 d2.callback('bar')
184 d3 = self.pdm.get_pending_deferred(did,False)
185 d3.addCallback(lambda r: self.assertEquals(r,'bar'))
186
186
@@ -1,50 +1,51
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This file contains unittests for the kernel.task.py module."""
3 """This file contains unittests for the kernel.task.py module."""
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 try:
18 try:
19 import time
19 import time
20
20
21 from twisted.internet import defer
21 from twisted.internet import defer
22 from twisted.trial import unittest
22 from twisted.trial import unittest
23
23
24 from IPython.kernel import task, controllerservice as cs, engineservice as es
24 from IPython.kernel import task, controllerservice as cs, engineservice as es
25 from IPython.kernel.multiengine import IMultiEngine
25 from IPython.kernel.multiengine import IMultiEngine
26 from IPython.testing.util import DeferredTestCase
26 from IPython.testing.util import DeferredTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
28 except ImportError:
28 except ImportError:
29 pass
29 import nose
30 else:
30 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
31 #-------------------------------------------------------------------------------
32 # Tests
33 #-------------------------------------------------------------------------------
34
31
35 class BasicTaskControllerTestCase(DeferredTestCase, ITaskControllerTestCase):
32 #-------------------------------------------------------------------------------
33 # Tests
34 #-------------------------------------------------------------------------------
35
36 class BasicTaskControllerTestCase(DeferredTestCase, ITaskControllerTestCase):
37
38 def setUp(self):
39 self.controller = cs.ControllerService()
40 self.controller.startService()
41 self.multiengine = IMultiEngine(self.controller)
42 self.tc = task.ITaskController(self.controller)
43 self.tc.failurePenalty = 0
44 self.engines=[]
36
45
37 def setUp(self):
46 def tearDown(self):
38 self.controller = cs.ControllerService()
47 self.controller.stopService()
39 self.controller.startService()
48 for e in self.engines:
40 self.multiengine = IMultiEngine(self.controller)
49 e.stopService()
41 self.tc = task.ITaskController(self.controller)
42 self.tc.failurePenalty = 0
43 self.engines=[]
44
45 def tearDown(self):
46 self.controller.stopService()
47 for e in self.engines:
48 e.stopService()
49
50
50
51
@@ -1,161 +1,162
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 __docformat__ = "restructuredtext en"
4 __docformat__ = "restructuredtext en"
5
5
6 #-------------------------------------------------------------------------------
6 #-------------------------------------------------------------------------------
7 # Copyright (C) 2008 The IPython Development Team
7 # Copyright (C) 2008 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-------------------------------------------------------------------------------
11 #-------------------------------------------------------------------------------
12
12
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 try:
17 try:
18 import time
18 import time
19
19
20 from twisted.internet import defer, reactor
20 from twisted.internet import defer, reactor
21
21
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
23
23
24 from IPython.kernel import task as taskmodule
24 from IPython.kernel import task as taskmodule
25 from IPython.kernel import controllerservice as cs
25 from IPython.kernel import controllerservice as cs
26 import IPython.kernel.multiengine as me
26 import IPython.kernel.multiengine as me
27 from IPython.testing.util import DeferredTestCase
27 from IPython.testing.util import DeferredTestCase
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 from IPython.kernel.taskfc import IFCTaskController
29 from IPython.kernel.taskfc import IFCTaskController
30 from IPython.kernel.util import printer
30 from IPython.kernel.util import printer
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
32 from IPython.kernel.clientconnector import ClientConnector
32 from IPython.kernel.clientconnector import ClientConnector
33 from IPython.kernel.error import CompositeError
33 from IPython.kernel.error import CompositeError
34 from IPython.kernel.parallelfunction import ParallelFunction
34 from IPython.kernel.parallelfunction import ParallelFunction
35 except ImportError:
35 except ImportError:
36 pass
36 import nose
37 else:
37 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
38
38
39 #-------------------------------------------------------------------------------
40 # Tests
41 #-------------------------------------------------------------------------------
42
39
43 def _raise_it(f):
40 #-------------------------------------------------------------------------------
44 try:
41 # Tests
45 f.raiseException()
42 #-------------------------------------------------------------------------------
46 except CompositeError, e:
47 e.raise_exception()
48
43
49 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
44 def _raise_it(f):
45 try:
46 f.raiseException()
47 except CompositeError, e:
48 e.raise_exception()
50
49
51 def setUp(self):
50 class TaskTest(DeferredTestCase, ITaskControllerTestCase):
52
51
53 self.engines = []
52 def setUp(self):
54
53
55 self.controller = cs.ControllerService()
54 self.engines = []
56 self.controller.startService()
55
57 self.imultiengine = me.IMultiEngine(self.controller)
56 self.controller = cs.ControllerService()
58 self.itc = taskmodule.ITaskController(self.controller)
57 self.controller.startService()
59 self.itc.failurePenalty = 0
58 self.imultiengine = me.IMultiEngine(self.controller)
60
59 self.itc = taskmodule.ITaskController(self.controller)
61 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
60 self.itc.failurePenalty = 0
62 self.tc_referenceable = IFCTaskController(self.itc)
61
63
62 self.mec_referenceable = IFCSynchronousMultiEngine(self.imultiengine)
64 self.controller_tub = Tub()
63 self.tc_referenceable = IFCTaskController(self.itc)
65 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
64
66 self.controller_tub.setLocation('127.0.0.1:10105')
65 self.controller_tub = Tub()
67
66 self.controller_tub.listenOn('tcp:10105:interface=127.0.0.1')
68 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
67 self.controller_tub.setLocation('127.0.0.1:10105')
69 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
68
70 self.controller_tub.startService()
69 mec_furl = self.controller_tub.registerReference(self.mec_referenceable)
71
70 tc_furl = self.controller_tub.registerReference(self.tc_referenceable)
72 self.client_tub = ClientConnector()
71 self.controller_tub.startService()
73 d = self.client_tub.get_multiengine_client(mec_furl)
72
74 d.addCallback(self.handle_mec_client)
73 self.client_tub = ClientConnector()
75 d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl))
74 d = self.client_tub.get_multiengine_client(mec_furl)
76 d.addCallback(self.handle_tc_client)
75 d.addCallback(self.handle_mec_client)
77 return d
76 d.addCallback(lambda _: self.client_tub.get_task_client(tc_furl))
78
77 d.addCallback(self.handle_tc_client)
79 def handle_mec_client(self, client):
78 return d
80 self.multiengine = client
79
80 def handle_mec_client(self, client):
81 self.multiengine = client
82
83 def handle_tc_client(self, client):
84 self.tc = client
85
86 def tearDown(self):
87 dlist = []
88 # Shut down the multiengine client
89 d = self.client_tub.tub.stopService()
90 dlist.append(d)
91 # Shut down the engines
92 for e in self.engines:
93 e.stopService()
94 # Shut down the controller
95 d = self.controller_tub.stopService()
96 d.addBoth(lambda _: self.controller.stopService())
97 dlist.append(d)
98 return defer.DeferredList(dlist)
99
100 def test_mapper(self):
101 self.addEngine(1)
102 m = self.tc.mapper()
103 self.assertEquals(m.task_controller,self.tc)
104 self.assertEquals(m.clear_before,False)
105 self.assertEquals(m.clear_after,False)
106 self.assertEquals(m.retries,0)
107 self.assertEquals(m.recovery_task,None)
108 self.assertEquals(m.depend,None)
109 self.assertEquals(m.block,True)
110
111 def test_map_default(self):
112 self.addEngine(1)
113 m = self.tc.mapper()
114 d = m.map(lambda x: 2*x, range(10))
115 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
116 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
117 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
118 return d
119
120 def test_map_noblock(self):
121 self.addEngine(1)
122 m = self.tc.mapper(block=False)
123 d = m.map(lambda x: 2*x, range(10))
124 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
125 return d
126
127 def test_mapper_fail(self):
128 self.addEngine(1)
129 m = self.tc.mapper()
130 d = m.map(lambda x: 1/0, range(10))
131 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
132 return d
133
134 def test_parallel(self):
135 self.addEngine(1)
136 p = self.tc.parallel()
137 self.assert_(isinstance(p, ParallelFunction))
138 @p
139 def f(x): return 2*x
140 d = f(range(10))
141 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
142 return d
81
143
82 def handle_tc_client(self, client):
144 def test_parallel_noblock(self):
83 self.tc = client
145 self.addEngine(1)
146 p = self.tc.parallel(block=False)
147 self.assert_(isinstance(p, ParallelFunction))
148 @p
149 def f(x): return 2*x
150 d = f(range(10))
151 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
152 return d
84
153
85 def tearDown(self):
154 def test_parallel_fail(self):
86 dlist = []
155 self.addEngine(1)
87 # Shut down the multiengine client
156 p = self.tc.parallel()
88 d = self.client_tub.tub.stopService()
157 self.assert_(isinstance(p, ParallelFunction))
89 dlist.append(d)
158 @p
90 # Shut down the engines
159 def f(x): return 1/0
91 for e in self.engines:
160 d = f(range(10))
92 e.stopService()
161 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
93 # Shut down the controller
162 return d No newline at end of file
94 d = self.controller_tub.stopService()
95 d.addBoth(lambda _: self.controller.stopService())
96 dlist.append(d)
97 return defer.DeferredList(dlist)
98
99 def test_mapper(self):
100 self.addEngine(1)
101 m = self.tc.mapper()
102 self.assertEquals(m.task_controller,self.tc)
103 self.assertEquals(m.clear_before,False)
104 self.assertEquals(m.clear_after,False)
105 self.assertEquals(m.retries,0)
106 self.assertEquals(m.recovery_task,None)
107 self.assertEquals(m.depend,None)
108 self.assertEquals(m.block,True)
109
110 def test_map_default(self):
111 self.addEngine(1)
112 m = self.tc.mapper()
113 d = m.map(lambda x: 2*x, range(10))
114 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
115 d.addCallback(lambda _: self.tc.map(lambda x: 2*x, range(10)))
116 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
117 return d
118
119 def test_map_noblock(self):
120 self.addEngine(1)
121 m = self.tc.mapper(block=False)
122 d = m.map(lambda x: 2*x, range(10))
123 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
124 return d
125
126 def test_mapper_fail(self):
127 self.addEngine(1)
128 m = self.tc.mapper()
129 d = m.map(lambda x: 1/0, range(10))
130 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
131 return d
132
133 def test_parallel(self):
134 self.addEngine(1)
135 p = self.tc.parallel()
136 self.assert_(isinstance(p, ParallelFunction))
137 @p
138 def f(x): return 2*x
139 d = f(range(10))
140 d.addCallback(lambda r: self.assertEquals(r,[2*x for x in range(10)]))
141 return d
142
143 def test_parallel_noblock(self):
144 self.addEngine(1)
145 p = self.tc.parallel(block=False)
146 self.assert_(isinstance(p, ParallelFunction))
147 @p
148 def f(x): return 2*x
149 d = f(range(10))
150 d.addCallback(lambda r: self.assertEquals(r,[x for x in range(10)]))
151 return d
152
153 def test_parallel_fail(self):
154 self.addEngine(1)
155 p = self.tc.parallel()
156 self.assert_(isinstance(p, ParallelFunction))
157 @p
158 def f(x): return 1/0
159 d = f(range(10))
160 d.addBoth(lambda f: self.assertRaises(ZeroDivisionError, _raise_it, f))
161 return d No newline at end of file
@@ -1,393 +1,401
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
352 Installation and testing scenarios
353 ==================================
353 ==================================
354
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.
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
356
357 Installation scenarios
357 Installation scenarios
358 ----------------------
358 ----------------------
359
359
360 1. Install from tarball using `python setup.py install`.
360 1. Install from tarball using `python setup.py install`.
361 a. With only readline+nose dependencies installed (test1)
361 a. With only readline+nose dependencies installed (test1)
362 b. With all dependencies installed (readline, zope.interface,
362
363 Twisted, foolscap, Sphinx, nose, pyOpenSSL) (test2)
363 virtualenv --no-site-packages test1
364 2. Install using easy_install.
364 # Activate it
365 a. With only readline+nose dependencies installed (test3)
365 easy_install nose
366 i. Default dependencies.
366 easy_install readline # On OS X or pyreadline on win32
367 ii. Optional dependency sets (kernel, doc, test, security)
367 cd ipython-0.9.beta3
368 easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]
368 python setup.py install
369
369
370 b. With all dependencies already installed (test2)
370 b. With all dependencies installed (readline, zope.interface,
371 Twisted, foolscap, Sphinx, nose, pyOpenSSL) (test2)
372 2. Install using easy_install.
373 a. With only readline+nose dependencies installed (test3)
374 i. Default dependencies.
375 ii. Optional dependency sets (kernel, doc, test, security)
376 easy_install -f ipython-0.9.beta3-py2.5.egg IPython[kernel,doc,test,security]
377
378 b. With all dependencies already installed (test2)
371
379
372
380
373 Tests to run for these scenarios
381 Tests to run for these scenarios
374 --------------------------------
382 --------------------------------
375
383
376 1. Run the full test suite.
384 1. Run the full test suite.
377 2. Start a controller and engines and try a few things by hand.
385 2. Start a controller and engines and try a few things by hand.
378 a. Using ipcluster.
386 a. Using ipcluster.
379 b. Using ipcontroller/ipengine by hand.
387 b. Using ipcontroller/ipengine by hand.
380 3. Run a few of the parallel examples.
388 3. Run a few of the parallel examples.
381 4. Try the kernel with and without security with and without PyOpenSSL
389 4. Try the kernel with and without security with and without PyOpenSSL
382 installed.
390 installed.
383 5. Beat on the IPython terminal a bunch.
391 5. Beat on the IPython terminal a bunch.
384 6. Make sure that furl files are being put in proper locations.
392 6. Make sure that furl files are being put in proper locations.
385
393
386
394
387
395
388
396
389
397
390
398
391
399
392
400
393
401
General Comments 0
You need to be logged in to leave comments. Login now