##// END OF EJS Templates
Merging with upstream
Fernando Perez -
r1996:7a6a76a5 merge
parent child Browse files
Show More
@@ -0,0 +1,37 b''
1 # encoding: utf-8
2 """
3 Test the LineFrontEnd
4 """
5
6 __docformat__ = "restructuredtext en"
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
14
15 from IPython.frontend.linefrontendbase import LineFrontEndBase
16 from copy import deepcopy
17 import nose.tools as nt
18
19 class ConcreteLineFrontEnd(LineFrontEndBase):
20 """ A concrete class to test the LineFrontEndBase.
21 """
22 def capture_output(self):
23 pass
24
25 def release_output(self):
26 pass
27
28
29 def test_is_complete():
30 """ Tests line completion heuristic.
31 """
32 frontend = ConcreteLineFrontEnd()
33 yield nt.assert_true, not frontend.is_complete('for x in \\')
34 yield nt.assert_true, not frontend.is_complete('for x in (1, ):')
35 yield nt.assert_true, frontend.is_complete('for x in (1, ):\n pass')
36
37
@@ -0,0 +1,51 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 # Tell nose to skip this module
16 __test__ = {}
17
18 import tempfile
19 import os, sys
20
21 from twisted.internet import reactor
22 from twisted.trial import unittest
23
24 from IPython.kernel.error import FileTimeoutError
25 from IPython.kernel.twistedutil import wait_for_file
26
27 #-----------------------------------------------------------------------------
28 # Tests
29 #-----------------------------------------------------------------------------
30
31 class TestWaitForFile(unittest.TestCase):
32
33 def test_delay(self):
34 filename = tempfile.mktemp()
35 def _create_file():
36 open(filename,'w').write('####')
37 dcall = reactor.callLater(0.5, _create_file)
38 d = wait_for_file(filename,delay=0.1)
39 d.addCallback(lambda r: self.assert_(r))
40 def _cancel_dcall(r):
41 if dcall.active():
42 dcall.cancel()
43 d.addCallback(_cancel_dcall)
44 return d
45
46 def test_timeout(self):
47 filename = tempfile.mktemp()
48 d = wait_for_file(filename,delay=0.1,max_tries=1)
49 d.addErrback(lambda f: self.assertRaises(FileTimeoutError,f.raiseException))
50 return d
51 No newline at end of file
@@ -0,0 +1,132 b''
1 # encoding: utf-8
2 """
3 Testing related decorators for use with twisted.trial.
4
5 The decorators in this files are designed to follow the same API as those
6 in the decorators module (in this same directory). But they can be used
7 with twisted.trial
8 """
9
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2009 The IPython Development Team
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
16
17 #-----------------------------------------------------------------------------
18 # Imports
19 #-----------------------------------------------------------------------------
20
21 import os
22 import sys
23
24 from IPython.testing.decorators import make_label_dec
25
26 #-----------------------------------------------------------------------------
27 # Testing decorators
28 #-----------------------------------------------------------------------------
29
30
31 def skipif(skip_condition, msg=None):
32 """Create a decorator that marks a test function for skipping.
33
34 The is a decorator factory that returns a decorator that will
35 conditionally skip a test based on the value of skip_condition. The
36 skip_condition argument can either be a boolean or a callable that returns
37 a boolean.
38
39 Parameters
40 ----------
41 skip_condition : boolean or callable
42 If this evaluates to True, the test is skipped.
43 msg : str
44 The message to print if the test is skipped.
45
46 Returns
47 -------
48 decorator : function
49 The decorator function that can be applied to the test function.
50 """
51
52 def skip_decorator(f):
53
54 # Allow for both boolean or callable skip conditions.
55 if callable(skip_condition):
56 skip_val = lambda : skip_condition()
57 else:
58 skip_val = lambda : skip_condition
59
60 if msg is None:
61 out = 'Test skipped due to test condition.'
62 else:
63 out = msg
64 final_msg = "Skipping test: %s. %s" % (f.__name__,out)
65
66 if skip_val():
67 f.skip = final_msg
68
69 return f
70 return skip_decorator
71
72
73 def skip(msg=None):
74 """Create a decorator that marks a test function for skipping.
75
76 This is a decorator factory that returns a decorator that will cause
77 tests to be skipped.
78
79 Parameters
80 ----------
81 msg : str
82 Optional message to be added.
83
84 Returns
85 -------
86 decorator : function
87 Decorator, which, when applied to a function, sets the skip
88 attribute of the function causing `twisted.trial` to skip it.
89 """
90
91 return skipif(True,msg)
92
93
94 def numpy_not_available():
95 """Can numpy be imported? Returns true if numpy does NOT import.
96
97 This is used to make a decorator to skip tests that require numpy to be
98 available, but delay the 'import numpy' to test execution time.
99 """
100 try:
101 import numpy
102 np_not_avail = False
103 except ImportError:
104 np_not_avail = True
105
106 return np_not_avail
107
108 #-----------------------------------------------------------------------------
109 # Decorators for public use
110 #-----------------------------------------------------------------------------
111
112 # Decorators to skip certain tests on specific platforms.
113 skip_win32 = skipif(sys.platform == 'win32',
114 "This test does not run under Windows")
115 skip_linux = skipif(sys.platform == 'linux2',
116 "This test does not run under Linux")
117 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
118
119 # Decorators to skip tests if not on specific platforms.
120 skip_if_not_win32 = skipif(sys.platform != 'win32',
121 "This test only runs under Windows")
122 skip_if_not_linux = skipif(sys.platform != 'linux2',
123 "This test only runs under Linux")
124 skip_if_not_osx = skipif(sys.platform != 'darwin',
125 "This test only runs under OSX")
126
127 # Other skip decorators
128 skipif_not_numpy = skipif(numpy_not_available,"This test requires numpy")
129
130 skipknownfailure = skip('This test is known to fail')
131
132
@@ -0,0 +1,52 b''
1 # encoding: utf-8
2 """
3 Tests for decorators_trial.py
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 # Tell nose to skip this module
18 __test__ = {}
19
20 import os
21 import sys
22
23 from twisted.trial import unittest
24 import IPython.testing.decorators_trial as dec
25
26 #-----------------------------------------------------------------------------
27 # Tests
28 #-----------------------------------------------------------------------------
29
30 class TestDecoratorsTrial(unittest.TestCase):
31
32 @dec.skip()
33 def test_deliberately_broken(self):
34 """A deliberately broken test - we want to skip this one."""
35 1/0
36
37 @dec.skip('Testing the skip decorator')
38 def test_deliberately_broken2(self):
39 """Another deliberately broken test - we want to skip this one."""
40 1/0
41
42 @dec.skip_linux
43 def test_linux(self):
44 self.assertNotEquals(sys.platform,'linux2',"This test can't run under linux")
45
46 @dec.skip_win32
47 def test_win32(self):
48 self.assertNotEquals(sys.platform,'win32',"This test can't run under windows")
49
50 @dec.skip_osx
51 def test_osx(self):
52 self.assertNotEquals(sys.platform,'darwin',"This test can't run under osx") No newline at end of file
@@ -0,0 +1,52 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 Tests for testing.tools
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 import os
19 import sys
20
21 import nose.tools as nt
22
23 from IPython.testing import decorators as dec
24 from IPython.testing.tools import full_path
25
26 #-----------------------------------------------------------------------------
27 # Tests
28 #-----------------------------------------------------------------------------
29
30
31 @dec.skip_win32
32 def test_full_path_posix():
33 spath = '/foo/bar.py'
34 result = full_path(spath,['a.txt','b.txt'])
35 nt.assert_equal(result, ['/foo/a.txt', '/foo/b.txt'])
36 spath = '/foo'
37 result = full_path(spath,['a.txt','b.txt'])
38 nt.assert_equal(result, ['/a.txt', '/b.txt'])
39 result = full_path(spath,'a.txt')
40 nt.assert_equal(result, ['/a.txt'])
41
42
43 @dec.skip_if_not_win32
44 def test_full_path_win32():
45 spath = 'c:\\foo\\bar.py'
46 result = full_path(spath,['a.txt','b.txt'])
47 nt.assert_equal(result, ['c:\\foo\\a.txt', 'c:\\foo\\b.txt'])
48 spath = 'c:\\foo'
49 result = full_path(spath,['a.txt','b.txt'])
50 nt.assert_equal(result, ['c:\\a.txt', 'c:\\b.txt'])
51 result = full_path(spath,'a.txt')
52 nt.assert_equal(result, ['c:\\a.txt']) No newline at end of file
@@ -0,0 +1,59 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 Tests for platutils.py
5 """
6
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
13
14 #-----------------------------------------------------------------------------
15 # Imports
16 #-----------------------------------------------------------------------------
17
18 import os
19 import sys
20
21 import nose.tools as nt
22
23 from IPython.platutils import find_cmd, FindCmdError, get_long_path_name
24 from IPython.testing import decorators as dec
25
26 #-----------------------------------------------------------------------------
27 # Tests
28 #-----------------------------------------------------------------------------
29
30 def test_find_cmd_python():
31 """Make sure we find sys.exectable for python."""
32 nt.assert_equals(find_cmd('python'), sys.executable)
33
34 @dec.skip_win32
35 def test_find_cmd():
36 """Make sure we can find the full path to ls."""
37 path = find_cmd('ls')
38 nt.assert_true(path.endswith('ls'))
39
40 @dec.skip_if_not_win32
41 def test_find_cmd():
42 """Try to find pythonw on Windows."""
43 path = find_cmd('pythonw')
44 nt.assert_true(path.endswith('pythonw.exe'))
45
46 def test_find_cmd_fail():
47 """Make sure that FindCmdError is raised if we can't find the cmd."""
48 nt.assert_raises(FindCmdError,find_cmd,'asdfasdf')
49
50 @dec.skip_if_not_win32
51 def test_get_long_path_name_win32():
52 p = get_long_path_name('c:\\docume~1')
53 nt.assert_equals(p,u'c:\\Documents and Settings')
54
55 @dec.skip_win32
56 def test_get_long_path_name():
57 p = get_long_path_name('/usr/local')
58 nt.assert_equals(p,'/usr/local')
59
@@ -0,0 +1,53 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3
4 """
5 A new example showing how to use `TaskRejectError` to handle dependencies
6 in the IPython task system.
7
8 To run this example, do::
9
10 $ ipcluster local -n 4
11
12 Then, in another terminal start up IPython and do::
13
14 In [0]: %run taskreject.py
15
16 In [1]: mec.execute('run=True', targets=[0,1])
17
18 After the first command, the scheduler will keep rescheduling the tasks, as
19 they will fail with `TaskRejectError`. But after the second command, there
20 are two engines that the tasks can run on. The tasks are quickly funneled
21 to these engines.
22
23 If you want to see how the controller is scheduling and retrying the tasks
24 do a `tail -f` on the controller's log file in ~/.ipython/log.
25 """
26
27 #-----------------------------------------------------------------------------
28 # Copyright (C) 2008-2009 The IPython Development Team
29 #
30 # Distributed under the terms of the BSD License. The full license is in
31 # the file COPYING, distributed as part of this software.
32 #-----------------------------------------------------------------------------
33
34 from IPython.kernel import client
35 from IPython.kernel import TaskRejectError
36
37 mec = client.MultiEngineClient()
38 tc = client.TaskClient()
39
40 mec.execute('from IPython.kernel import TaskRejectError')
41 mec.execute('run = False')
42
43 def map_task():
44 if not run:
45 raise TaskRejectError('task dependency not met')
46 return 3.0e8
47
48 task_ids = []
49
50 for i in range(10):
51 task = client.MapTask(map_task, retries=20)
52 task_ids.append(tc.run(task, block=False))
53
@@ -66,7 +66,7 b' def attr_matches(self, text):'
66 return res
66 return res
67
67
68 def main():
68 def main():
69 import readline
69 import IPython.rlineimpl as readline
70 readline.set_completer_delims(" \n\t")
70 readline.set_completer_delims(" \n\t")
71 # monkeypatch - the code will be folded to normal completer later on
71 # monkeypatch - the code will be folded to normal completer later on
72 import IPython.completer
72 import IPython.completer
@@ -99,16 +99,14 b' def main():'
99 #import readline
99 #import readline
100 #readline.parse_and_bind('set completion-query-items 1000')
100 #readline.parse_and_bind('set completion-query-items 1000')
101 #readline.parse_and_bind('set page-completions no')
101 #readline.parse_and_bind('set page-completions no')
102
102
103
103
104
105
106 # some config helper functions you can use
104 # some config helper functions you can use
107 def import_all(modules):
105 def import_all(modules):
108 """ Usage: import_all("os sys") """
106 """ Usage: import_all("os sys") """
109 for m in modules.split():
107 for m in modules.split():
110 ip.ex("from %s import *" % m)
108 ip.ex("from %s import *" % m)
111
109
112 def execf(fname):
110 def execf(fname):
113 """ Execute a file in user namespace """
111 """ Execute a file in user namespace """
114 ip.ex('execfile("%s")' % os.path.expanduser(fname))
112 ip.ex('execfile("%s")' % os.path.expanduser(fname))
@@ -14,12 +14,13 b' __docformat__ = "restructuredtext en"'
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17 from IPython.external import guid
18
17
18 from IPython.external import guid
19
19
20 from zope.interface import Interface, Attribute, implements, classProvides
20 from zope.interface import Interface, Attribute, implements, classProvides
21 from twisted.python.failure import Failure
21 from twisted.python.failure import Failure
22 from IPython.frontend.frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
22 from IPython.frontend.frontendbase import (
23 FrontEndBase, IFrontEnd, IFrontEndFactory)
23 from IPython.kernel.core.history import FrontEndHistory
24 from IPython.kernel.core.history import FrontEndHistory
24 from IPython.kernel.engineservice import IEngineCore
25 from IPython.kernel.engineservice import IEngineCore
25
26
@@ -15,30 +15,38 b' __docformat__ = "restructuredtext en"'
15 # Imports
15 # Imports
16 #---------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
17
17
18 # Tell nose to skip this module
19 __test__ = {}
20
21 from twisted.trial import unittest
22 from twisted.internet.defer import succeed
23
24 from IPython.kernel.core.interpreter import Interpreter
25 import IPython.kernel.engineservice as es
26
18 try:
27 try:
19 from IPython.kernel.core.interpreter import Interpreter
28 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
20 import IPython.kernel.engineservice as es
21 from IPython.testing.util import DeferredTestCase
22 from twisted.internet.defer import succeed
23 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
24 from Foundation import NSMakeRect
29 from Foundation import NSMakeRect
25 from AppKit import NSTextView, NSScrollView
30 from AppKit import NSTextView, NSScrollView
26 except ImportError:
31 except ImportError:
27 import nose
32 # This tells twisted.trial to skip this module if PyObjC is not found
28 raise nose.SkipTest("This test requires zope.interface, Twisted, Foolscap and PyObjC")
33 skip = True
29
34
30 class TestIPythonCocoaControler(DeferredTestCase):
35 #---------------------------------------------------------------------------
36 # Tests
37 #---------------------------------------------------------------------------
38 class TestIPythonCocoaControler(unittest.TestCase):
31 """Tests for IPythonCocoaController"""
39 """Tests for IPythonCocoaController"""
32
40
33 def setUp(self):
41 def setUp(self):
34 self.controller = IPythonCocoaController.alloc().init()
42 self.controller = IPythonCocoaController.alloc().init()
35 self.engine = es.EngineService()
43 self.engine = es.EngineService()
36 self.engine.startService()
44 self.engine.startService()
37
45
38 def tearDown(self):
46 def tearDown(self):
39 self.controller = None
47 self.controller = None
40 self.engine.stopService()
48 self.engine.stopService()
41
49
42 def testControllerExecutesCode(self):
50 def testControllerExecutesCode(self):
43 code ="""5+5"""
51 code ="""5+5"""
44 expected = Interpreter().execute(code)
52 expected = Interpreter().execute(code)
@@ -47,48 +55,46 b' class TestIPythonCocoaControler(DeferredTestCase):'
47 del result['number']
55 del result['number']
48 del result['id']
56 del result['id']
49 return result
57 return result
50 self.assertDeferredEquals(
58 d = self.controller.execute(code)
51 self.controller.execute(code).addCallback(removeNumberAndID),
59 d.addCallback(removeNumberAndID)
52 expected)
60 d.addCallback(lambda r: self.assertEquals(r, expected))
53
61
54 def testControllerMirrorsUserNSWithValuesAsStrings(self):
62 def testControllerMirrorsUserNSWithValuesAsStrings(self):
55 code = """userns1=1;userns2=2"""
63 code = """userns1=1;userns2=2"""
56 def testControllerUserNS(result):
64 def testControllerUserNS(result):
57 self.assertEquals(self.controller.userNS['userns1'], 1)
65 self.assertEquals(self.controller.userNS['userns1'], 1)
58 self.assertEquals(self.controller.userNS['userns2'], 2)
66 self.assertEquals(self.controller.userNS['userns2'], 2)
59
60 self.controller.execute(code).addCallback(testControllerUserNS)
67 self.controller.execute(code).addCallback(testControllerUserNS)
61
68
62
63 def testControllerInstantiatesIEngine(self):
69 def testControllerInstantiatesIEngine(self):
64 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
70 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
65
71
66 def testControllerCompletesToken(self):
72 def testControllerCompletesToken(self):
67 code = """longNameVariable=10"""
73 code = """longNameVariable=10"""
68 def testCompletes(result):
74 def testCompletes(result):
69 self.assert_("longNameVariable" in result)
75 self.assert_("longNameVariable" in result)
70
76
71 def testCompleteToken(result):
77 def testCompleteToken(result):
72 self.controller.complete("longNa").addCallback(testCompletes)
78 self.controller.complete("longNa").addCallback(testCompletes)
73
79
74 self.controller.execute(code).addCallback(testCompletes)
80 self.controller.execute(code).addCallback(testCompletes)
75
81
76
82
77 def testCurrentIndent(self):
83 def testCurrentIndent(self):
78 """test that current_indent_string returns current indent or None.
84 """test that current_indent_string returns current indent or None.
79 Uses _indent_for_block for direct unit testing.
85 Uses _indent_for_block for direct unit testing.
80 """
86 """
81
87
82 self.controller.tabUsesSpaces = True
88 self.controller.tabUsesSpaces = True
83 self.assert_(self.controller._indent_for_block("""a=3""") == None)
89 self.assert_(self.controller._indent_for_block("""a=3""") == None)
84 self.assert_(self.controller._indent_for_block("") == None)
90 self.assert_(self.controller._indent_for_block("") == None)
85 block = """def test():\n a=3"""
91 block = """def test():\n a=3"""
86 self.assert_(self.controller._indent_for_block(block) == \
92 self.assert_(self.controller._indent_for_block(block) == \
87 ' ' * self.controller.tabSpaces)
93 ' ' * self.controller.tabSpaces)
88
94
89 block = """if(True):\n%sif(False):\n%spass""" % \
95 block = """if(True):\n%sif(False):\n%spass""" % \
90 (' '*self.controller.tabSpaces,
96 (' '*self.controller.tabSpaces,
91 2*' '*self.controller.tabSpaces)
97 2*' '*self.controller.tabSpaces)
92 self.assert_(self.controller._indent_for_block(block) == \
98 self.assert_(self.controller._indent_for_block(block) == \
93 2*(' '*self.controller.tabSpaces))
99 2*(' '*self.controller.tabSpaces))
94
100
@@ -18,10 +18,8 b' __docformat__ = "restructuredtext en"'
18 #-------------------------------------------------------------------------------
18 #-------------------------------------------------------------------------------
19 import re
19 import re
20
20
21 import IPython
22 import sys
21 import sys
23 import codeop
22 import codeop
24 import traceback
25
23
26 from frontendbase import FrontEndBase
24 from frontendbase import FrontEndBase
27 from IPython.kernel.core.interpreter import Interpreter
25 from IPython.kernel.core.interpreter import Interpreter
@@ -58,6 +56,9 b' class LineFrontEndBase(FrontEndBase):'
58 # programatic control of the frontend.
56 # programatic control of the frontend.
59 last_result = dict(number=0)
57 last_result = dict(number=0)
60
58
59 # The last prompt displayed. Useful for continuation prompts.
60 last_prompt = ''
61
61 # The input buffer being edited
62 # The input buffer being edited
62 input_buffer = ''
63 input_buffer = ''
63
64
@@ -151,8 +152,12 b' class LineFrontEndBase(FrontEndBase):'
151 self.capture_output()
152 self.capture_output()
152 try:
153 try:
153 # Add line returns here, to make sure that the statement is
154 # Add line returns here, to make sure that the statement is
154 # complete.
155 # complete (except if '\' was used).
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
156 # This should probably be done in a different place (like
157 # maybe 'prefilter_input' method? For now, this works.
158 clean_string = string.rstrip('\n')
159 if not clean_string.endswith('\\'): clean_string +='\n\n'
160 is_complete = codeop.compile_command(clean_string,
156 "<string>", "exec")
161 "<string>", "exec")
157 self.release_output()
162 self.release_output()
158 except Exception, e:
163 except Exception, e:
@@ -183,16 +188,6 b' class LineFrontEndBase(FrontEndBase):'
183 # Create a false result, in case there is an exception
188 # Create a false result, in case there is an exception
184 self.last_result = dict(number=self.prompt_number)
189 self.last_result = dict(number=self.prompt_number)
185
190
186 ## try:
187 ## self.history.input_cache[-1] = raw_string.rstrip()
188 ## result = self.shell.execute(python_string)
189 ## self.last_result = result
190 ## self.render_result(result)
191 ## except:
192 ## self.show_traceback()
193 ## finally:
194 ## self.after_execute()
195
196 try:
191 try:
197 try:
192 try:
198 self.history.input_cache[-1] = raw_string.rstrip()
193 self.history.input_cache[-1] = raw_string.rstrip()
@@ -272,15 +267,15 b' class LineFrontEndBase(FrontEndBase):'
272 symbols_per_line = max(1, chars_per_line/max_len)
267 symbols_per_line = max(1, chars_per_line/max_len)
273
268
274 pos = 1
269 pos = 1
275 buf = []
270 completion_string = []
276 for symbol in possibilities:
271 for symbol in possibilities:
277 if pos < symbols_per_line:
272 if pos < symbols_per_line:
278 buf.append(symbol.ljust(max_len))
273 completion_string.append(symbol.ljust(max_len))
279 pos += 1
274 pos += 1
280 else:
275 else:
281 buf.append(symbol.rstrip() + '\n')
276 completion_string.append(symbol.rstrip() + '\n')
282 pos = 1
277 pos = 1
283 self.write(''.join(buf))
278 self.write(''.join(completion_string))
284 self.new_prompt(self.input_prompt_template.substitute(
279 self.new_prompt(self.input_prompt_template.substitute(
285 number=self.last_result['number'] + 1))
280 number=self.last_result['number'] + 1))
286 self.input_buffer = new_line
281 self.input_buffer = new_line
@@ -297,26 +292,70 b' class LineFrontEndBase(FrontEndBase):'
297 self.write(prompt)
292 self.write(prompt)
298
293
299
294
295 def continuation_prompt(self):
296 """Returns the current continuation prompt.
297 """
298 return ("."*(len(self.last_prompt)-2) + ': ')
299
300
301 def execute_command(self, command, hidden=False):
302 """ Execute a command, not only in the model, but also in the
303 view, if any.
304 """
305 return self.shell.execute(command)
306
300 #--------------------------------------------------------------------------
307 #--------------------------------------------------------------------------
301 # Private API
308 # Private API
302 #--------------------------------------------------------------------------
309 #--------------------------------------------------------------------------
303
310
304 def _on_enter(self):
311 def _on_enter(self, new_line_pos=0):
305 """ Called when the return key is pressed in a line editing
312 """ Called when the return key is pressed in a line editing
306 buffer.
313 buffer.
314
315 Parameters
316 ----------
317 new_line_pos : integer, optional
318 Position of the new line to add, starting from the
319 end (0 adds a new line after the last line, -1 before
320 the last line...)
321
322 Returns
323 -------
324 True if execution is triggered
307 """
325 """
308 current_buffer = self.input_buffer
326 current_buffer = self.input_buffer
309 cleaned_buffer = self.prefilter_input(current_buffer)
327 # XXX: This string replace is ugly, but there should be no way it
328 # fails.
329 prompt_less_buffer = re.sub('^' + self.continuation_prompt(),
330 '', current_buffer).replace('\n' + self.continuation_prompt(),
331 '\n')
332 cleaned_buffer = self.prefilter_input(prompt_less_buffer)
310 if self.is_complete(cleaned_buffer):
333 if self.is_complete(cleaned_buffer):
311 self.execute(cleaned_buffer, raw_string=current_buffer)
334 self.execute(cleaned_buffer, raw_string=current_buffer)
335 return True
312 else:
336 else:
313 self.input_buffer += self._get_indent_string(
337 # Start a new line.
314 current_buffer[:-1])
338 new_line_pos = -new_line_pos
315 if len(current_buffer.split('\n')) == 2:
339 lines = current_buffer.split('\n')[:-1]
316 self.input_buffer += '\t\t'
340 prompt_less_lines = prompt_less_buffer.split('\n')
317 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
341 # Create the new line, with the continuation prompt, and the
318 self.input_buffer += '\t'
342 # same amount of indent than the line above it.
319
343 new_line = self.continuation_prompt() + \
344 self._get_indent_string('\n'.join(
345 prompt_less_lines[:new_line_pos-1]))
346 if len(lines) == 1:
347 # We are starting a first continuation line. Indent it.
348 new_line += '\t'
349 elif current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
350 # The last line ends with ":", autoindent the new line.
351 new_line += '\t'
352
353 if new_line_pos == 0:
354 lines.append(new_line)
355 else:
356 lines.insert(new_line_pos, new_line)
357 self.input_buffer = '\n'.join(lines)
358
320
359
321 def _get_indent_string(self, string):
360 def _get_indent_string(self, string):
322 """ Return the string of whitespace that prefixes a line. Used to
361 """ Return the string of whitespace that prefixes a line. Used to
@@ -22,9 +22,10 b' __docformat__ = "restructuredtext en"'
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24 import sys
24 import sys
25
25 import pydoc
26 from linefrontendbase import LineFrontEndBase, common_prefix
26 import os
27 from frontendbase import FrontEndBase
27 import re
28 import __builtin__
28
29
29 from IPython.ipmaker import make_IPython
30 from IPython.ipmaker import make_IPython
30 from IPython.ipapi import IPApi
31 from IPython.ipapi import IPApi
@@ -33,9 +34,8 b' from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap'
33 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34
35
35 from IPython.genutils import Term
36 from IPython.genutils import Term
36 import pydoc
37
37 import os
38 from linefrontendbase import LineFrontEndBase, common_prefix
38 import sys
39
39
40
40
41 def mk_system_call(system_call_function, command):
41 def mk_system_call(system_call_function, command):
@@ -45,6 +45,8 b' def mk_system_call(system_call_function, command):'
45 """
45 """
46 def my_system_call(args):
46 def my_system_call(args):
47 system_call_function("%s %s" % (command, args))
47 system_call_function("%s %s" % (command, args))
48
49 my_system_call.__doc__ = "Calls %s" % command
48 return my_system_call
50 return my_system_call
49
51
50 #-------------------------------------------------------------------------------
52 #-------------------------------------------------------------------------------
@@ -62,13 +64,25 b' class PrefilterFrontEnd(LineFrontEndBase):'
62
64
63 debug = False
65 debug = False
64
66
65 def __init__(self, ipython0=None, *args, **kwargs):
67 def __init__(self, ipython0=None, argv=None, *args, **kwargs):
66 """ Parameters:
68 """ Parameters:
67 -----------
69 -----------
68
70
69 ipython0: an optional ipython0 instance to use for command
71 ipython0: an optional ipython0 instance to use for command
70 prefiltering and completion.
72 prefiltering and completion.
73
74 argv : list, optional
75 Used as the instance's argv value. If not given, [] is used.
71 """
76 """
77 if argv is None:
78 argv = []
79 # This is a hack to avoid the IPython exception hook to trigger
80 # on exceptions (https://bugs.launchpad.net/bugs/337105)
81 # XXX: This is horrible: module-leve monkey patching -> side
82 # effects.
83 from IPython import iplib
84 iplib.InteractiveShell.isthreaded = True
85
72 LineFrontEndBase.__init__(self, *args, **kwargs)
86 LineFrontEndBase.__init__(self, *args, **kwargs)
73 self.shell.output_trap = RedirectorOutputTrap(
87 self.shell.output_trap = RedirectorOutputTrap(
74 out_callback=self.write,
88 out_callback=self.write,
@@ -83,10 +97,16 b' class PrefilterFrontEnd(LineFrontEndBase):'
83 if ipython0 is None:
97 if ipython0 is None:
84 # Instanciate an IPython0 interpreter to be able to use the
98 # Instanciate an IPython0 interpreter to be able to use the
85 # prefiltering.
99 # prefiltering.
100 # Suppress all key input, to avoid waiting
101 def my_rawinput(x=None):
102 return '\n'
103 old_rawinput = __builtin__.raw_input
104 __builtin__.raw_input = my_rawinput
86 # XXX: argv=[] is a bit bold.
105 # XXX: argv=[] is a bit bold.
87 ipython0 = make_IPython(argv=[],
106 ipython0 = make_IPython(argv=argv,
88 user_ns=self.shell.user_ns,
107 user_ns=self.shell.user_ns,
89 user_global_ns=self.shell.user_global_ns)
108 user_global_ns=self.shell.user_global_ns)
109 __builtin__.raw_input = old_rawinput
90 self.ipython0 = ipython0
110 self.ipython0 = ipython0
91 # Set the pager:
111 # Set the pager:
92 self.ipython0.set_hook('show_in_pager',
112 self.ipython0.set_hook('show_in_pager',
@@ -98,16 +118,17 b' class PrefilterFrontEnd(LineFrontEndBase):'
98 self._ip.system = self.system_call
118 self._ip.system = self.system_call
99 # XXX: Muck around with magics so that they work better
119 # XXX: Muck around with magics so that they work better
100 # in our environment
120 # in our environment
101 self.ipython0.magic_ls = mk_system_call(self.system_call,
121 if not sys.platform.startswith('win'):
102 'ls -CF')
122 self.ipython0.magic_ls = mk_system_call(self.system_call,
123 'ls -CF')
103 # And now clean up the mess created by ipython0
124 # And now clean up the mess created by ipython0
104 self.release_output()
125 self.release_output()
105
126
106
127
107 if not 'banner' in kwargs and self.banner is None:
128 if not 'banner' in kwargs and self.banner is None:
108 self.banner = self.ipython0.BANNER + """
129 self.banner = self.ipython0.BANNER
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
110
130
131 # FIXME: __init__ and start should be two different steps
111 self.start()
132 self.start()
112
133
113 #--------------------------------------------------------------------------
134 #--------------------------------------------------------------------------
@@ -117,7 +138,10 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
117 def show_traceback(self):
138 def show_traceback(self):
118 """ Use ipython0 to capture the last traceback and display it.
139 """ Use ipython0 to capture the last traceback and display it.
119 """
140 """
120 self.capture_output()
141 # Don't do the capture; the except_hook has already done some
142 # modifications to the IO streams, if we store them, we'll be
143 # storing the wrong ones.
144 #self.capture_output()
121 self.ipython0.showtraceback(tb_offset=-1)
145 self.ipython0.showtraceback(tb_offset=-1)
122 self.release_output()
146 self.release_output()
123
147
@@ -171,7 +195,7 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
171 def complete(self, line):
195 def complete(self, line):
172 # FIXME: This should be factored out in the linefrontendbase
196 # FIXME: This should be factored out in the linefrontendbase
173 # method.
197 # method.
174 word = line.split('\n')[-1].split(' ')[-1]
198 word = self._get_completion_text(line)
175 completions = self.ipython0.complete(word)
199 completions = self.ipython0.complete(word)
176 # FIXME: The proper sort should be done in the complete method.
200 # FIXME: The proper sort should be done in the complete method.
177 key = lambda x: x.replace('_', '')
201 key = lambda x: x.replace('_', '')
@@ -244,3 +268,18 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
244 """
268 """
245 self.ipython0.atexit_operations()
269 self.ipython0.atexit_operations()
246
270
271
272 def _get_completion_text(self, line):
273 """ Returns the text to be completed by breaking the line at specified
274 delimiters.
275 """
276 # Break at: spaces, '=', all parentheses (except if balanced).
277 # FIXME2: In the future, we need to make the implementation similar to
278 # that in the 'pyreadline' module (modes/basemode.py) where we break at
279 # each delimiter and try to complete the residual line, until we get a
280 # successful list of completions.
281 expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})'
282 complete_sep = re.compile(expression)
283 text = complete_sep.split(line)[-1]
284 return text
285
1 NO CONTENT: file renamed from IPython/frontend/_process/__init__.py to IPython/frontend/process/__init__.py
NO CONTENT: file renamed from IPython/frontend/_process/__init__.py to IPython/frontend/process/__init__.py
@@ -151,7 +151,12 b' else:'
151 self._thread = ht
151 self._thread = ht
152 self.pid = pid
152 self.pid = pid
153
153
154 winprocess.AssignProcessToJobObject(self._job, hp)
154 # XXX: A try/except to fix UAC-related problems under
155 # Windows Vista, when reparenting jobs.
156 try:
157 winprocess.AssignProcessToJobObject(self._job, hp)
158 except WindowsError:
159 pass
155 winprocess.ResumeThread(ht)
160 winprocess.ResumeThread(ht)
156
161
157 if p2cread is not None:
162 if p2cread is not None:
1 NO CONTENT: file renamed from IPython/frontend/_process/pipedprocess.py to IPython/frontend/process/pipedprocess.py
NO CONTENT: file renamed from IPython/frontend/_process/pipedprocess.py to IPython/frontend/process/pipedprocess.py
1 NO CONTENT: file renamed from IPython/frontend/_process/winprocess.py to IPython/frontend/process/winprocess.py
NO CONTENT: file renamed from IPython/frontend/_process/winprocess.py to IPython/frontend/process/winprocess.py
@@ -1,6 +1,6 b''
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 asyncfrontendbase module."""
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
@@ -15,17 +15,15 b' __docformat__ = "restructuredtext en"'
15 # Imports
15 # Imports
16 #---------------------------------------------------------------------------
16 #---------------------------------------------------------------------------
17
17
18 import unittest
18 # Tell nose to skip this module
19 __test__ = {}
19
20
20 try:
21 from twisted.trial import unittest
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
23 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
24 from IPython.kernel.engineservice import EngineService
24 except ImportError:
25 from IPython.testing.parametric import Parametric, parametric
25 import nose
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27
26
28 from IPython.testing.decorators import skip
29
27
30 class FrontEndCallbackChecker(AsyncFrontEndBase):
28 class FrontEndCallbackChecker(AsyncFrontEndBase):
31 """FrontEndBase subclass for checking callbacks"""
29 """FrontEndBase subclass for checking callbacks"""
@@ -44,14 +42,11 b' class FrontEndCallbackChecker(AsyncFrontEndBase):'
44 self.renderResultCalled = True
42 self.renderResultCalled = True
45 return result
43 return result
46
44
47
48 def render_error(self, failure):
45 def render_error(self, failure):
49 self.renderErrorCalled = True
46 self.renderErrorCalled = True
50 return failure
47 return failure
51
52
48
53
49
54
55 class TestAsyncFrontendBase(unittest.TestCase):
50 class TestAsyncFrontendBase(unittest.TestCase):
56 def setUp(self):
51 def setUp(self):
57 """Setup the EngineService and FrontEndBase"""
52 """Setup the EngineService and FrontEndBase"""
@@ -59,97 +54,56 b' class TestAsyncFrontendBase(unittest.TestCase):'
59 self.fb = FrontEndCallbackChecker(engine=EngineService())
54 self.fb = FrontEndCallbackChecker(engine=EngineService())
60
55
61 def test_implements_IFrontEnd(self):
56 def test_implements_IFrontEnd(self):
62 assert(frontendbase.IFrontEnd.implementedBy(
57 self.assert_(frontendbase.IFrontEnd.implementedBy(
63 AsyncFrontEndBase))
58 AsyncFrontEndBase))
64
59
65 def test_is_complete_returns_False_for_incomplete_block(self):
60 def test_is_complete_returns_False_for_incomplete_block(self):
66 """"""
67
68 block = """def test(a):"""
61 block = """def test(a):"""
69
62 self.assert_(self.fb.is_complete(block) == False)
70 assert(self.fb.is_complete(block) == False)
71
63
72 def test_is_complete_returns_True_for_complete_block(self):
64 def test_is_complete_returns_True_for_complete_block(self):
73 """"""
74
75 block = """def test(a): pass"""
65 block = """def test(a): pass"""
76
66 self.assert_(self.fb.is_complete(block))
77 assert(self.fb.is_complete(block))
78
79 block = """a=3"""
67 block = """a=3"""
80
68 self.assert_(self.fb.is_complete(block))
81 assert(self.fb.is_complete(block))
82
69
83 def test_blockID_added_to_result(self):
70 def test_blockID_added_to_result(self):
84 block = """3+3"""
71 block = """3+3"""
85
86 d = self.fb.execute(block, blockID='TEST_ID')
72 d = self.fb.execute(block, blockID='TEST_ID')
87
73 d.addCallback(lambda r: self.assert_(r['blockID']=='TEST_ID'))
88 d.addCallback(self.checkBlockID, expected='TEST_ID')
74 return d
89
75
90 def test_blockID_added_to_failure(self):
76 def test_blockID_added_to_failure(self):
91 block = "raise Exception()"
77 block = "raise Exception()"
92
93 d = self.fb.execute(block,blockID='TEST_ID')
78 d = self.fb.execute(block,blockID='TEST_ID')
94 d.addErrback(self.checkFailureID, expected='TEST_ID')
79 d.addErrback(lambda f: self.assert_(f.blockID=='TEST_ID'))
95
80 return d
96 def checkBlockID(self, result, expected=""):
97 assert(result['blockID'] == expected)
98
99
100 def checkFailureID(self, failure, expected=""):
101 assert(failure.blockID == expected)
102
103
81
104 def test_callbacks_added_to_execute(self):
82 def test_callbacks_added_to_execute(self):
105 """test that
106 update_cell_prompt
107 render_result
108
109 are added to execute request
110 """
111
112 d = self.fb.execute("10+10")
83 d = self.fb.execute("10+10")
113 d.addCallback(self.checkCallbacks)
84 d.addCallback(lambda r: self.assert_(self.fb.updateCalled and self.fb.renderResultCalled))
85 return d
114
86
115 def checkCallbacks(self, result):
116 assert(self.fb.updateCalled)
117 assert(self.fb.renderResultCalled)
118
119 @skip("This test fails and lead to an unhandled error in a Deferred.")
120 def test_error_callback_added_to_execute(self):
87 def test_error_callback_added_to_execute(self):
121 """test that render_error called on execution error"""
88 """Test that render_error called on execution error."""
122
89
123 d = self.fb.execute("raise Exception()")
90 d = self.fb.execute("raise Exception()")
124 d.addCallback(self.checkRenderError)
91 d.addErrback(lambda f: self.assert_(self.fb.renderErrorCalled))
125
92 return d
126 def checkRenderError(self, result):
127 assert(self.fb.renderErrorCalled)
128
93
129 def test_history_returns_expected_block(self):
94 def test_history_returns_expected_block(self):
130 """Make sure history browsing doesn't fail"""
95 """Make sure history browsing doesn't fail."""
131
96
132 blocks = ["a=1","a=2","a=3"]
97 blocks = ["a=1","a=2","a=3"]
133 for b in blocks:
98 d = self.fb.execute(blocks[0])
134 d = self.fb.execute(b)
99 d.addCallback(lambda _: self.fb.execute(blocks[1]))
135
100 d.addCallback(lambda _: self.fb.execute(blocks[2]))
136 # d is now the deferred for the last executed block
101 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-2]))
137 d.addCallback(self.historyTests, blocks)
102 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-3]))
138
103 d.addCallback(lambda _: self.assert_(self.fb.get_history_next()==blocks[-2]))
139
104 return d
140 def historyTests(self, result, blocks):
105
141 """historyTests"""
106 def test_history_returns_none_at_startup(self):
142
107 self.assert_(self.fb.get_history_previous("")==None)
143 assert(len(blocks) >= 3)
108 self.assert_(self.fb.get_history_next()==None)
144 assert(self.fb.get_history_previous("") == blocks[-2])
109
145 assert(self.fb.get_history_previous("") == blocks[-3])
146 assert(self.fb.get_history_next() == blocks[-2])
147
148
149 def test_history_returns_none_at_startup(self):
150 """test_history_returns_none_at_startup"""
151
152 assert(self.fb.get_history_previous("")==None)
153 assert(self.fb.get_history_next()==None)
154
155
@@ -12,12 +12,32 b' __docformat__ = "restructuredtext en"'
12 # in the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-------------------------------------------------------------------------------
14
14
15 from copy import copy, deepcopy
15 from cStringIO import StringIO
16 from cStringIO import StringIO
16 import string
17 import string
17
18
18 from IPython.ipapi import get as get_ipython0
19 from nose.tools import assert_equal
20
19 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
21 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
20 from copy import deepcopy
22 from IPython.ipapi import get as get_ipython0
23 from IPython.testing.plugin.ipdoctest import default_argv
24
25
26 def safe_deepcopy(d):
27 """ Deep copy every key of the given dict, when possible. Elsewhere
28 do a copy.
29 """
30 copied_d = dict()
31 for key, value in d.iteritems():
32 try:
33 copied_d[key] = deepcopy(value)
34 except:
35 try:
36 copied_d[key] = copy(value)
37 except:
38 copied_d[key] = value
39 return copied_d
40
21
41
22 class TestPrefilterFrontEnd(PrefilterFrontEnd):
42 class TestPrefilterFrontEnd(PrefilterFrontEnd):
23
43
@@ -26,16 +46,8 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
26 banner = ''
46 banner = ''
27
47
28 def __init__(self):
48 def __init__(self):
29 ipython0 = get_ipython0().IP
30 self.out = StringIO()
49 self.out = StringIO()
31 PrefilterFrontEnd.__init__(self, ipython0=ipython0)
50 PrefilterFrontEnd.__init__(self,argv=default_argv())
32 # Clean up the namespace for isolation between tests
33 user_ns = self.ipython0.user_ns
34 # We need to keep references to things so that they don't
35 # get garbage collected (this stinks).
36 self.shadow_ns = dict()
37 for i in self.ipython0.magic_who_ls():
38 self.shadow_ns[i] = user_ns.pop(i)
39 # Some more code for isolation (yeah, crazy)
51 # Some more code for isolation (yeah, crazy)
40 self._on_enter()
52 self._on_enter()
41 self.out.flush()
53 self.out.flush()
@@ -52,17 +64,31 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
52
64
53 def isolate_ipython0(func):
65 def isolate_ipython0(func):
54 """ Decorator to isolate execution that involves an iptyhon0.
66 """ Decorator to isolate execution that involves an iptyhon0.
67
68 Notes
69 -----
70
71 Apply only to functions with no arguments. Nose skips functions
72 with arguments.
55 """
73 """
56 def my_func(*args, **kwargs):
74 def my_func():
57 ipython0 = get_ipython0().IP
75 iplib = get_ipython0()
58 user_ns = deepcopy(ipython0.user_ns)
76 if iplib is None:
59 global_ns = deepcopy(ipython0.global_ns)
77 return func()
78 ipython0 = iplib.IP
79 global_ns = safe_deepcopy(ipython0.user_global_ns)
80 user_ns = safe_deepcopy(ipython0.user_ns)
60 try:
81 try:
61 func(*args, **kwargs)
82 out = func()
62 finally:
83 finally:
63 ipython0.user_ns = user_ns
84 ipython0.user_ns = user_ns
64 ipython0.global_ns = global_ns
85 ipython0.user_global_ns = global_ns
86 # Undo the hack at creation of PrefilterFrontEnd
87 from IPython import iplib
88 iplib.InteractiveShell.isthreaded = False
89 return out
65
90
91 my_func.__name__ = func.__name__
66 return my_func
92 return my_func
67
93
68
94
@@ -74,7 +100,7 b' def test_execution():'
74 f.input_buffer = 'print 1'
100 f.input_buffer = 'print 1'
75 f._on_enter()
101 f._on_enter()
76 out_value = f.out.getvalue()
102 out_value = f.out.getvalue()
77 assert out_value == '1\n'
103 assert_equal(out_value, '1\n')
78
104
79
105
80 @isolate_ipython0
106 @isolate_ipython0
@@ -87,20 +113,20 b' def test_multiline():'
87 f.input_buffer += 'print 1'
113 f.input_buffer += 'print 1'
88 f._on_enter()
114 f._on_enter()
89 out_value = f.out.getvalue()
115 out_value = f.out.getvalue()
90 assert out_value == ''
116 yield assert_equal, out_value, ''
91 f._on_enter()
117 f._on_enter()
92 out_value = f.out.getvalue()
118 out_value = f.out.getvalue()
93 assert out_value == '1\n'
119 yield assert_equal, out_value, '1\n'
94 f = TestPrefilterFrontEnd()
120 f = TestPrefilterFrontEnd()
95 f.input_buffer='(1 +'
121 f.input_buffer='(1 +'
96 f._on_enter()
122 f._on_enter()
97 f.input_buffer += '0)'
123 f.input_buffer += '0)'
98 f._on_enter()
124 f._on_enter()
99 out_value = f.out.getvalue()
125 out_value = f.out.getvalue()
100 assert out_value == ''
126 yield assert_equal, out_value, ''
101 f._on_enter()
127 f._on_enter()
102 out_value = f.out.getvalue()
128 out_value = f.out.getvalue()
103 assert out_value == '1\n'
129 yield assert_equal, out_value, '1\n'
104
130
105
131
106 @isolate_ipython0
132 @isolate_ipython0
@@ -113,13 +139,13 b' def test_capture():'
113 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()'
139 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()'
114 f._on_enter()
140 f._on_enter()
115 out_value = f.out.getvalue()
141 out_value = f.out.getvalue()
116 assert out_value == '1'
142 yield assert_equal, out_value, '1'
117 f = TestPrefilterFrontEnd()
143 f = TestPrefilterFrontEnd()
118 f.input_buffer = \
144 f.input_buffer = \
119 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()'
145 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()'
120 f._on_enter()
146 f._on_enter()
121 out_value = f.out.getvalue()
147 out_value = f.out.getvalue()
122 assert out_value == '1'
148 yield assert_equal, out_value, '1'
123
149
124
150
125 @isolate_ipython0
151 @isolate_ipython0
@@ -129,10 +155,16 b' def test_magic():'
129 This test is fairly fragile and will break when magics change.
155 This test is fairly fragile and will break when magics change.
130 """
156 """
131 f = TestPrefilterFrontEnd()
157 f = TestPrefilterFrontEnd()
158 # Before checking the interactive namespace, make sure it's clear (it can
159 # otherwise pick up things stored in the user's local db)
160 f.input_buffer += '%reset -f'
161 f._on_enter()
162 f.complete_current_input()
163 # Now, run the %who magic and check output
132 f.input_buffer += '%who'
164 f.input_buffer += '%who'
133 f._on_enter()
165 f._on_enter()
134 out_value = f.out.getvalue()
166 out_value = f.out.getvalue()
135 assert out_value == 'Interactive namespace is empty.\n'
167 assert_equal(out_value, 'Interactive namespace is empty.\n')
136
168
137
169
138 @isolate_ipython0
170 @isolate_ipython0
@@ -156,8 +188,8 b' def test_help():'
156
188
157
189
158 @isolate_ipython0
190 @isolate_ipython0
159 def test_completion():
191 def test_completion_simple():
160 """ Test command-line completion.
192 """ Test command-line completion on trivial examples.
161 """
193 """
162 f = TestPrefilterFrontEnd()
194 f = TestPrefilterFrontEnd()
163 f.input_buffer = 'zzza = 1'
195 f.input_buffer = 'zzza = 1'
@@ -167,8 +199,47 b' def test_completion():'
167 f.input_buffer = 'zz'
199 f.input_buffer = 'zz'
168 f.complete_current_input()
200 f.complete_current_input()
169 out_value = f.out.getvalue()
201 out_value = f.out.getvalue()
170 assert out_value == '\nzzza zzzb '
202 yield assert_equal, out_value, '\nzzza zzzb '
171 assert f.input_buffer == 'zzz'
203 yield assert_equal, f.input_buffer, 'zzz'
204
205
206 @isolate_ipython0
207 def test_completion_parenthesis():
208 """ Test command-line completion when a parenthesis is open.
209 """
210 f = TestPrefilterFrontEnd()
211 f.input_buffer = 'zzza = 1'
212 f._on_enter()
213 f.input_buffer = 'zzzb = 2'
214 f._on_enter()
215 f.input_buffer = 'map(zz'
216 f.complete_current_input()
217 out_value = f.out.getvalue()
218 yield assert_equal, out_value, '\nzzza zzzb '
219 yield assert_equal, f.input_buffer, 'map(zzz'
220
221
222 @isolate_ipython0
223 def test_completion_indexing():
224 """ Test command-line completion when indexing on objects.
225 """
226 f = TestPrefilterFrontEnd()
227 f.input_buffer = 'a = [0]'
228 f._on_enter()
229 f.input_buffer = 'a[0].'
230 f.complete_current_input()
231 assert_equal(f.input_buffer, 'a[0].__')
232
233
234 @isolate_ipython0
235 def test_completion_equal():
236 """ Test command-line completion when the delimiter is "=", not " ".
237 """
238 f = TestPrefilterFrontEnd()
239 f.input_buffer = 'a=1.'
240 f.complete_current_input()
241 assert_equal(f.input_buffer, 'a=1.__')
242
172
243
173
244
174 if __name__ == '__main__':
245 if __name__ == '__main__':
@@ -177,4 +248,5 b" if __name__ == '__main__':"
177 test_execution()
248 test_execution()
178 test_multiline()
249 test_multiline()
179 test_capture()
250 test_capture()
180 test_completion()
251 test_completion_simple()
252 test_completion_complex()
@@ -5,18 +5,18 b' Test process execution and IO redirection.'
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-2009 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 from cStringIO import StringIO
15 from cStringIO import StringIO
16 from time import sleep
16 from time import sleep
17 import sys
17 import sys
18
18
19 from IPython.frontend._process import PipedProcess
19 from IPython.frontend.process import PipedProcess
20 from IPython.testing import decorators as testdec
20 from IPython.testing import decorators as testdec
21
21
22
22
@@ -25,6 +25,8 b' import wx.stc as stc'
25 from wx.py import editwindow
25 from wx.py import editwindow
26 import time
26 import time
27 import sys
27 import sys
28 import string
29
28 LINESEP = '\n'
30 LINESEP = '\n'
29 if sys.platform == 'win32':
31 if sys.platform == 'win32':
30 LINESEP = '\n\r'
32 LINESEP = '\n\r'
@@ -33,20 +35,26 b' import re'
33
35
34 # FIXME: Need to provide an API for non user-generated display on the
36 # FIXME: Need to provide an API for non user-generated display on the
35 # screen: this should not be editable by the user.
37 # screen: this should not be editable by the user.
38 #-------------------------------------------------------------------------------
39 # Constants
40 #-------------------------------------------------------------------------------
41 _COMPLETE_BUFFER_MARKER = 31
42 _ERROR_MARKER = 30
43 _INPUT_MARKER = 29
36
44
37 _DEFAULT_SIZE = 10
45 _DEFAULT_SIZE = 10
38 if sys.platform == 'darwin':
46 if sys.platform == 'darwin':
39 _DEFAULT_SIZE = 12
47 _DEFAULT_SIZE = 12
40
48
41 _DEFAULT_STYLE = {
49 _DEFAULT_STYLE = {
42 'stdout' : 'fore:#0000FF',
50 #background definition
43 'stderr' : 'fore:#007f00',
44 'trace' : 'fore:#FF0000',
45
46 'default' : 'size:%d' % _DEFAULT_SIZE,
51 'default' : 'size:%d' % _DEFAULT_SIZE,
47 'bracegood' : 'fore:#00AA00,back:#000000,bold',
52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
48 'bracebad' : 'fore:#FF0000,back:#000000,bold',
53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
49
54
55 # Edge column: a number of None
56 'edge_column' : -1,
57
50 # properties for the various Python lexer styles
58 # properties for the various Python lexer styles
51 'comment' : 'fore:#007F00',
59 'comment' : 'fore:#007F00',
52 'number' : 'fore:#007F7F',
60 'number' : 'fore:#007F7F',
@@ -57,7 +65,24 b' _DEFAULT_STYLE = {'
57 'tripledouble' : 'fore:#7F0000',
65 'tripledouble' : 'fore:#7F0000',
58 'class' : 'fore:#0000FF,bold,underline',
66 'class' : 'fore:#0000FF,bold,underline',
59 'def' : 'fore:#007F7F,bold',
67 'def' : 'fore:#007F7F,bold',
60 'operator' : 'bold'
68 'operator' : 'bold',
69
70 # Default colors
71 'trace' : '#FAFAF1', # Nice green
72 'stdout' : '#FDFFD3', # Nice yellow
73 'stderr' : '#FFF1F1', # Nice red
74
75 # Default scintilla settings
76 'antialiasing' : True,
77 'carret_color' : 'BLACK',
78 'background_color' :'WHITE',
79
80 #prompt definition
81 'prompt_in1' : \
82 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02',
83
84 'prompt_out': \
85 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02',
61 }
86 }
62
87
63 # new style numbers
88 # new style numbers
@@ -69,6 +94,47 b' _TRACE_STYLE = 17'
69 # system colors
94 # system colors
70 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
95 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
71
96
97 # Translation table from ANSI escape sequences to color.
98 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
99 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
100 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
101 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
102 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
103 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
104 '1;34': [12, 'LIGHT BLUE'], '1;35':
105 [13, 'MEDIUM VIOLET RED'],
106 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
107
108 # XXX: Maybe one day we should factor this code with ColorANSI. Right now
109 # ColorANSI is hard to reuse and makes our code more complex.
110
111 #we define platform specific fonts
112 if wx.Platform == '__WXMSW__':
113 FACES = { 'times': 'Times New Roman',
114 'mono' : 'Courier New',
115 'helv' : 'Arial',
116 'other': 'Comic Sans MS',
117 'size' : 10,
118 'size2': 8,
119 }
120 elif wx.Platform == '__WXMAC__':
121 FACES = { 'times': 'Times New Roman',
122 'mono' : 'Monaco',
123 'helv' : 'Arial',
124 'other': 'Comic Sans MS',
125 'size' : 10,
126 'size2': 8,
127 }
128 else:
129 FACES = { 'times': 'Times',
130 'mono' : 'Courier',
131 'helv' : 'Helvetica',
132 'other': 'new century schoolbook',
133 'size' : 10,
134 'size2': 8,
135 }
136
137
72 #-------------------------------------------------------------------------------
138 #-------------------------------------------------------------------------------
73 # The console widget class
139 # The console widget class
74 #-------------------------------------------------------------------------------
140 #-------------------------------------------------------------------------------
@@ -83,6 +149,9 b' class ConsoleWidget(editwindow.EditWindow):'
83 # stored.
149 # stored.
84 title = 'Console'
150 title = 'Console'
85
151
152 # Last prompt printed
153 last_prompt = ''
154
86 # The buffer being edited.
155 # The buffer being edited.
87 def _set_input_buffer(self, string):
156 def _set_input_buffer(self, string):
88 self.SetSelection(self.current_prompt_pos, self.GetLength())
157 self.SetSelection(self.current_prompt_pos, self.GetLength())
@@ -103,19 +172,11 b' class ConsoleWidget(editwindow.EditWindow):'
103
172
104 # Translation table from ANSI escape sequences to color. Override
173 # Translation table from ANSI escape sequences to color. Override
105 # this to specify your colors.
174 # this to specify your colors.
106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
175 ANSI_STYLES = ANSI_STYLES.copy()
107 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
108 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
112 '1;34': [12, 'LIGHT BLUE'], '1;35':
113 [13, 'MEDIUM VIOLET RED'],
114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
115
116 # The color of the carret (call _apply_style() after setting)
117 carret_color = 'BLACK'
118
176
177 # Font faces
178 faces = FACES.copy()
179
119 # Store the last time a refresh was done
180 # Store the last time a refresh was done
120 _last_refresh_time = 0
181 _last_refresh_time = 0
121
182
@@ -126,7 +187,11 b' class ConsoleWidget(editwindow.EditWindow):'
126 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
187 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
127 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
188 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
128 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
189 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
129 self._configure_scintilla()
190 self.configure_scintilla()
191 # Track if 'enter' key as ever been processed
192 # This variable will only be reallowed until key goes up
193 self.enter_catched = False
194 self.current_prompt_pos = 0
130
195
131 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
196 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
132 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
197 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
@@ -193,8 +258,19 b' class ConsoleWidget(editwindow.EditWindow):'
193 self.current_prompt_pos = self.GetLength()
258 self.current_prompt_pos = self.GetLength()
194 self.current_prompt_line = self.GetCurrentLine()
259 self.current_prompt_line = self.GetCurrentLine()
195 self.EnsureCaretVisible()
260 self.EnsureCaretVisible()
261 self.last_prompt = prompt
196
262
197
263
264 def continuation_prompt(self):
265 """ Returns the current continuation prompt.
266 We need to implement this method here to deal with the
267 ascii escape sequences cleaning up.
268 """
269 # ASCII-less prompt
270 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
271 return "."*(len(ascii_less)-2) + ': '
272
273
198 def scroll_to_bottom(self):
274 def scroll_to_bottom(self):
199 maxrange = self.GetScrollRange(wx.VERTICAL)
275 maxrange = self.GetScrollRange(wx.VERTICAL)
200 self.ScrollLines(maxrange)
276 self.ScrollLines(maxrange)
@@ -216,37 +292,24 b' class ConsoleWidget(editwindow.EditWindow):'
216 """
292 """
217 return self.GetSize()[0]/self.GetCharWidth()
293 return self.GetSize()[0]/self.GetCharWidth()
218
294
219 #--------------------------------------------------------------------------
220 # EditWindow API
221 #--------------------------------------------------------------------------
222
295
223 def OnUpdateUI(self, event):
296 def configure_scintilla(self):
224 """ Override the OnUpdateUI of the EditWindow class, to prevent
297 """ Set up all the styling option of the embedded scintilla
225 syntax highlighting both for faster redraw, and for more
298 widget.
226 consistent look and feel.
227 """
299 """
300 p = self.style.copy()
301
302 # Marker for complete buffer.
303 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
304 background=p['trace'])
228
305
229 #--------------------------------------------------------------------------
306 # Marker for current input buffer.
230 # Private API
307 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
231 #--------------------------------------------------------------------------
308 background=p['stdout'])
232
309 # Marker for tracebacks.
233 def _apply_style(self):
310 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
234 """ Applies the colors for the different text elements and the
311 background=p['stderr'])
235 carret.
236 """
237 self.SetCaretForeground(self.carret_color)
238
239 #self.StyleClearAll()
240 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
241 "fore:#FF0000,back:#0000FF,bold")
242 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
243 "fore:#000000,back:#FF0000,bold")
244
245 for style in self.ANSI_STYLES.values():
246 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
247
312
248
249 def _configure_scintilla(self):
250 self.SetEOLMode(stc.STC_EOL_LF)
313 self.SetEOLMode(stc.STC_EOL_LF)
251
314
252 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
315 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
@@ -268,7 +331,9 b' class ConsoleWidget(editwindow.EditWindow):'
268 self.SetWrapMode(stc.STC_WRAP_CHAR)
331 self.SetWrapMode(stc.STC_WRAP_CHAR)
269 self.SetWrapMode(stc.STC_WRAP_WORD)
332 self.SetWrapMode(stc.STC_WRAP_WORD)
270 self.SetBufferedDraw(True)
333 self.SetBufferedDraw(True)
271 self.SetUseAntiAliasing(True)
334
335 self.SetUseAntiAliasing(p['antialiasing'])
336
272 self.SetLayoutCache(stc.STC_CACHE_PAGE)
337 self.SetLayoutCache(stc.STC_CACHE_PAGE)
273 self.SetUndoCollection(False)
338 self.SetUndoCollection(False)
274 self.SetUseTabs(True)
339 self.SetUseTabs(True)
@@ -289,23 +354,48 b' class ConsoleWidget(editwindow.EditWindow):'
289 self.SetMarginWidth(1, 0)
354 self.SetMarginWidth(1, 0)
290 self.SetMarginWidth(2, 0)
355 self.SetMarginWidth(2, 0)
291
356
292 self._apply_style()
293
294 # Xterm escape sequences
357 # Xterm escape sequences
295 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
358 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
296 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
359 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
297
360
298 #self.SetEdgeMode(stc.STC_EDGE_LINE)
299 #self.SetEdgeColumn(80)
300
301 # styles
361 # styles
302 p = self.style
362
303 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
363 self.SetCaretForeground(p['carret_color'])
364
365 background_color = p['background_color']
366
367 if 'default' in p:
368 if 'back' not in p['default']:
369 p['default'] += ',back:%s' % background_color
370 if 'size' not in p['default']:
371 p['default'] += ',size:%s' % self.faces['size']
372 if 'face' not in p['default']:
373 p['default'] += ',face:%s' % self.faces['mono']
374
375 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
376 else:
377 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
378 "fore:%s,back:%s,size:%d,face:%s"
379 % (self.ANSI_STYLES['0;30'][1],
380 background_color,
381 self.faces['size'], self.faces['mono']))
382
304 self.StyleClearAll()
383 self.StyleClearAll()
384
385 # XXX: two lines below are usefull if not using the lexer
386 #for style in self.ANSI_STYLES.values():
387 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
388
389 # prompt definition
390 self.prompt_in1 = p['prompt_in1']
391 self.prompt_out = p['prompt_out']
392
393 self.output_prompt_template = string.Template(self.prompt_out)
394 self.input_prompt_template = string.Template(self.prompt_in1)
395
305 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
396 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
306 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
397 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
307 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
398 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
308
309 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
399 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
310 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
400 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
311 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
401 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
@@ -321,6 +411,28 b' class ConsoleWidget(editwindow.EditWindow):'
321 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
411 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
322 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
412 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
323
413
414 edge_column = p['edge_column']
415 if edge_column is not None and edge_column > 0:
416 #we add a vertical line to console widget
417 self.SetEdgeMode(stc.STC_EDGE_LINE)
418 self.SetEdgeColumn(edge_column)
419
420
421 #--------------------------------------------------------------------------
422 # EditWindow API
423 #--------------------------------------------------------------------------
424
425 def OnUpdateUI(self, event):
426 """ Override the OnUpdateUI of the EditWindow class, to prevent
427 syntax highlighting both for faster redraw, and for more
428 consistent look and feel.
429 """
430
431
432 #--------------------------------------------------------------------------
433 # Private API
434 #--------------------------------------------------------------------------
435
324 def _on_key_down(self, event, skip=True):
436 def _on_key_down(self, event, skip=True):
325 """ Key press callback used for correcting behavior for
437 """ Key press callback used for correcting behavior for
326 console-like interfaces: the cursor is constraint to be after
438 console-like interfaces: the cursor is constraint to be after
@@ -329,6 +441,11 b' class ConsoleWidget(editwindow.EditWindow):'
329 Return True if event as been catched.
441 Return True if event as been catched.
330 """
442 """
331 catched = True
443 catched = True
444 # XXX: Would the right way to do this be to have a
445 # dictionary at the instance level associating keys with
446 # callbacks? How would we deal with inheritance? And Do the
447 # different callbacks share local variables?
448
332 # Intercept some specific keys.
449 # Intercept some specific keys.
333 if event.KeyCode == ord('L') and event.ControlDown() :
450 if event.KeyCode == ord('L') and event.ControlDown() :
334 self.scroll_to_bottom()
451 self.scroll_to_bottom()
@@ -346,6 +463,10 b' class ConsoleWidget(editwindow.EditWindow):'
346 self.ScrollPages(-1)
463 self.ScrollPages(-1)
347 elif event.KeyCode == wx.WXK_PAGEDOWN:
464 elif event.KeyCode == wx.WXK_PAGEDOWN:
348 self.ScrollPages(1)
465 self.ScrollPages(1)
466 elif event.KeyCode == wx.WXK_HOME:
467 self.GotoPos(self.GetLength())
468 elif event.KeyCode == wx.WXK_END:
469 self.GotoPos(self.GetLength())
349 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
470 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
350 self.ScrollLines(-1)
471 self.ScrollLines(-1)
351 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
472 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
@@ -357,16 +478,20 b' class ConsoleWidget(editwindow.EditWindow):'
357 event.Skip()
478 event.Skip()
358 else:
479 else:
359 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
480 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
360 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
481 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
482 wx.MOD_SHIFT):
361 catched = True
483 catched = True
362 self.CallTipCancel()
484 if not self.enter_catched:
363 self.write('\n', refresh=False)
485 self.CallTipCancel()
364 # Under windows scintilla seems to be doing funny stuff to the
486 if event.Modifiers == wx.MOD_SHIFT:
365 # line returns here, but the getter for input_buffer filters
487 # Try to force execution
366 # this out.
488 self.GotoPos(self.GetLength())
367 if sys.platform == 'win32':
489 self.write('\n' + self.continuation_prompt(),
368 self.input_buffer = self.input_buffer
490 refresh=False)
369 self._on_enter()
491 self._on_enter()
492 else:
493 self._on_enter()
494 self.enter_catched = True
370
495
371 elif event.KeyCode == wx.WXK_HOME:
496 elif event.KeyCode == wx.WXK_HOME:
372 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
497 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
@@ -391,16 +516,28 b' class ConsoleWidget(editwindow.EditWindow):'
391 catched = True
516 catched = True
392
517
393 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
518 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
394 if self.GetCurrentPos() > self.current_prompt_pos:
519 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
520 event.Skip()
521 catched = True
522
523 elif event.KeyCode == wx.WXK_RIGHT:
524 if not self._keep_cursor_in_buffer(self.GetCurrentPos() + 1):
525 event.Skip()
526 catched = True
527
528
529 elif event.KeyCode == wx.WXK_DELETE:
530 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
395 event.Skip()
531 event.Skip()
396 catched = True
532 catched = True
397
533
398 if skip and not catched:
534 if skip and not catched:
399 # Put the cursor back in the edit region
535 # Put the cursor back in the edit region
400 if self.GetCurrentPos() < self.current_prompt_pos:
536 if not self._keep_cursor_in_buffer():
401 self.GotoPos(self.current_prompt_pos)
537 if not (self.GetCurrentPos() == self.GetLength()
402 else:
538 and event.KeyCode == wx.WXK_DELETE):
403 event.Skip()
539 event.Skip()
540 catched = True
404
541
405 return catched
542 return catched
406
543
@@ -408,17 +545,69 b' class ConsoleWidget(editwindow.EditWindow):'
408 def _on_key_up(self, event, skip=True):
545 def _on_key_up(self, event, skip=True):
409 """ If cursor is outside the editing region, put it back.
546 """ If cursor is outside the editing region, put it back.
410 """
547 """
411 event.Skip()
548 if skip:
412 if self.GetCurrentPos() < self.current_prompt_pos:
549 event.Skip()
413 self.GotoPos(self.current_prompt_pos)
550 self._keep_cursor_in_buffer()
551
552
553 # XXX: I need to avoid the problem of having an empty glass;
554 def _keep_cursor_in_buffer(self, pos=None):
555 """ Checks if the cursor is where it is allowed to be. If not,
556 put it back.
414
557
558 Returns
559 -------
560 cursor_moved: Boolean
561 whether or not the cursor was moved by this routine.
562
563 Notes
564 ------
565 WARNING: This does proper checks only for horizontal
566 movements.
567 """
568 if pos is None:
569 current_pos = self.GetCurrentPos()
570 else:
571 current_pos = pos
572 if current_pos < self.current_prompt_pos:
573 self.GotoPos(self.current_prompt_pos)
574 return True
575 line_num = self.LineFromPosition(current_pos)
576 if not current_pos > self.GetLength():
577 line_pos = self.GetColumn(current_pos)
578 else:
579 line_pos = self.GetColumn(self.GetLength())
580 line = self.GetLine(line_num)
581 # Jump the continuation prompt
582 continuation_prompt = self.continuation_prompt()
583 if ( line.startswith(continuation_prompt)
584 and line_pos < len(continuation_prompt)):
585 if line_pos < 2:
586 # We are at the beginning of the line, trying to move
587 # forward: jump forward.
588 self.GotoPos(current_pos + 1 +
589 len(continuation_prompt) - line_pos)
590 else:
591 # Jump back up
592 self.GotoPos(self.GetLineEndPosition(line_num-1))
593 return True
594 elif ( current_pos > self.GetLineEndPosition(line_num)
595 and not current_pos == self.GetLength()):
596 # Jump to next line
597 self.GotoPos(current_pos + 1 +
598 len(continuation_prompt))
599 return True
600
601 # We re-allow enter event processing
602 self.enter_catched = False
603 return False
415
604
416
605
417 if __name__ == '__main__':
606 if __name__ == '__main__':
418 # Some simple code to test the console widget.
607 # Some simple code to test the console widget.
419 class MainWindow(wx.Frame):
608 class MainWindow(wx.Frame):
420 def __init__(self, parent, id, title):
609 def __init__(self, parent, id, title):
421 wx.Frame.__init__(self, parent, id, title, size=(300,250))
610 wx.Frame.__init__(self, parent, id, title, size=(300, 250))
422 self._sizer = wx.BoxSizer(wx.VERTICAL)
611 self._sizer = wx.BoxSizer(wx.VERTICAL)
423 self.console_widget = ConsoleWidget(self)
612 self.console_widget = ConsoleWidget(self)
424 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
613 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
@@ -80,6 +80,15 b' class IPythonX(wx.Frame):'
80 self.SetSizer(self._sizer)
80 self.SetSizer(self._sizer)
81 self.SetAutoLayout(1)
81 self.SetAutoLayout(1)
82 self.Show(True)
82 self.Show(True)
83 wx.EVT_CLOSE(self, self.on_close)
84
85
86 def on_close(self, event):
87 """ Called on closing the windows.
88
89 Stops the event loop, to close all the child windows.
90 """
91 wx.CallAfter(wx.Exit)
83
92
84
93
85 def main():
94 def main():
@@ -25,38 +25,19 b' __docformat__ = "restructuredtext en"'
25 # Major library imports
25 # Major library imports
26 import re
26 import re
27 import __builtin__
27 import __builtin__
28 from time import sleep
29 import sys
28 import sys
30 from threading import Lock
29 from threading import Lock
31 import string
32
30
33 import wx
31 import wx
34 from wx import stc
32 from wx import stc
35
33
36 # Ipython-specific imports.
34 # Ipython-specific imports.
37 from IPython.frontend._process import PipedProcess
35 from IPython.frontend.process import PipedProcess
38 from console_widget import ConsoleWidget
36 from console_widget import ConsoleWidget, _COMPLETE_BUFFER_MARKER, \
37 _ERROR_MARKER, _INPUT_MARKER
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
38 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40
39
41 #-------------------------------------------------------------------------------
40 #-------------------------------------------------------------------------------
42 # Constants
43 #-------------------------------------------------------------------------------
44
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 _ERROR_BG = '#FFF1F1' # Nice red
48
49 _COMPLETE_BUFFER_MARKER = 31
50 _ERROR_MARKER = 30
51 _INPUT_MARKER = 29
52
53 prompt_in1 = \
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55
56 prompt_out = \
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58
59 #-------------------------------------------------------------------------------
60 # Classes to implement the Wx frontend
41 # Classes to implement the Wx frontend
61 #-------------------------------------------------------------------------------
42 #-------------------------------------------------------------------------------
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
43 class WxController(ConsoleWidget, PrefilterFrontEnd):
@@ -66,11 +47,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
66 This class inherits from ConsoleWidget, that provides a console-like
47 This class inherits from ConsoleWidget, that provides a console-like
67 widget to provide a text-rendering widget suitable for a terminal.
48 widget to provide a text-rendering widget suitable for a terminal.
68 """
49 """
69
50
70 output_prompt_template = string.Template(prompt_out)
71
72 input_prompt_template = string.Template(prompt_in1)
73
74 # Print debug info on what is happening to the console.
51 # Print debug info on what is happening to the console.
75 debug = False
52 debug = False
76
53
@@ -138,25 +115,24 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 size=wx.DefaultSize,
116 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
118 styledef=None,
141 *args, **kwds):
119 *args, **kwds):
142 """ Create Shell instance.
120 """ Create Shell instance.
121
122 Parameters
123 -----------
124 styledef : dict, optional
125 styledef is the dictionary of options used to define the
126 style.
143 """
127 """
128 if styledef is not None:
129 self.style = styledef
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
145 PrefilterFrontEnd.__init__(self, **kwds)
131 PrefilterFrontEnd.__init__(self, **kwds)
146
132
147 # Stick in our own raw_input:
133 # Stick in our own raw_input:
148 self.ipython0.raw_input = self.raw_input
134 self.ipython0.raw_input = self.raw_input
149
135
150 # Marker for complete buffer.
151 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
152 background=_COMPLETE_BUFFER_BG)
153 # Marker for current input buffer.
154 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
155 background=_INPUT_BUFFER_BG)
156 # Marker for tracebacks.
157 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
158 background=_ERROR_BG)
159
160 # A time for flushing the write buffer
136 # A time for flushing the write buffer
161 BUFFER_FLUSH_TIMER_ID = 100
137 BUFFER_FLUSH_TIMER_ID = 100
162 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
138 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
@@ -171,8 +147,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
171 self.shell.user_ns['self'] = self
147 self.shell.user_ns['self'] = self
172 # Inject our own raw_input in namespace
148 # Inject our own raw_input in namespace
173 self.shell.user_ns['raw_input'] = self.raw_input
149 self.shell.user_ns['raw_input'] = self.raw_input
174
150
175
176 def raw_input(self, prompt=''):
151 def raw_input(self, prompt=''):
177 """ A replacement from python's raw_input.
152 """ A replacement from python's raw_input.
178 """
153 """
@@ -251,11 +226,8 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
251 if (self.AutoCompActive() and line and not line[-1] == '.') \
226 if (self.AutoCompActive() and line and not line[-1] == '.') \
252 or create==True:
227 or create==True:
253 suggestion, completions = self.complete(line)
228 suggestion, completions = self.complete(line)
254 offset=0
255 if completions:
229 if completions:
256 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
230 offset = len(self._get_completion_text(line))
257 residual = complete_sep.split(line)[-1]
258 offset = len(residual)
259 self.pop_completion(completions, offset=offset)
231 self.pop_completion(completions, offset=offset)
260 if self.debug:
232 if self.debug:
261 print >>sys.__stdout__, completions
233 print >>sys.__stdout__, completions
@@ -276,6 +248,14 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
276 milliseconds=100, oneShot=True)
248 milliseconds=100, oneShot=True)
277
249
278
250
251 def clear_screen(self):
252 """ Empty completely the widget.
253 """
254 self.ClearAll()
255 self.new_prompt(self.input_prompt_template.substitute(
256 number=(self.last_result['number'] + 1)))
257
258
279 #--------------------------------------------------------------------------
259 #--------------------------------------------------------------------------
280 # LineFrontEnd interface
260 # LineFrontEnd interface
281 #--------------------------------------------------------------------------
261 #--------------------------------------------------------------------------
@@ -299,6 +279,41 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
299 raw_string=raw_string)
279 raw_string=raw_string)
300 wx.CallAfter(callback)
280 wx.CallAfter(callback)
301
281
282
283 def execute_command(self, command, hidden=False):
284 """ Execute a command, not only in the model, but also in the
285 view.
286 """
287 # XXX: This method needs to be integrated in the base fronted
288 # interface
289 if hidden:
290 return self.shell.execute(command)
291 else:
292 # XXX: we are not storing the input buffer previous to the
293 # execution, as this forces us to run the execution
294 # input_buffer a yield, which is not good.
295 ##current_buffer = self.shell.control.input_buffer
296 command = command.rstrip()
297 if len(command.split('\n')) > 1:
298 # The input command is several lines long, we need to
299 # force the execution to happen
300 command += '\n'
301 cleaned_command = self.prefilter_input(command)
302 self.input_buffer = command
303 # Do not use wx.Yield() (aka GUI.process_events()) to avoid
304 # recursive yields.
305 self.ProcessEvent(wx.PaintEvent())
306 self.write('\n')
307 if not self.is_complete(cleaned_command + '\n'):
308 self._colorize_input_buffer()
309 self.render_error('Incomplete or invalid input')
310 self.new_prompt(self.input_prompt_template.substitute(
311 number=(self.last_result['number'] + 1)))
312 return False
313 self._on_enter()
314 return True
315
316
302 def save_output_hooks(self):
317 def save_output_hooks(self):
303 self.__old_raw_input = __builtin__.raw_input
318 self.__old_raw_input = __builtin__.raw_input
304 PrefilterFrontEnd.save_output_hooks(self)
319 PrefilterFrontEnd.save_output_hooks(self)
@@ -356,10 +371,16 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
356 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
371 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
357
372
358
373
374 def continuation_prompt(self, *args, **kwargs):
375 # Avoid multiple inheritence, be explicit about which
376 # parent method class gets called
377 return ConsoleWidget.continuation_prompt(self, *args, **kwargs)
378
379
359 def write(self, *args, **kwargs):
380 def write(self, *args, **kwargs):
360 # Avoid multiple inheritence, be explicit about which
381 # Avoid multiple inheritence, be explicit about which
361 # parent method class gets called
382 # parent method class gets called
362 ConsoleWidget.write(self, *args, **kwargs)
383 return ConsoleWidget.write(self, *args, **kwargs)
363
384
364
385
365 def _on_key_down(self, event, skip=True):
386 def _on_key_down(self, event, skip=True):
@@ -367,7 +388,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
367 widget handle them, and put our logic afterward.
388 widget handle them, and put our logic afterward.
368 """
389 """
369 # FIXME: This method needs to be broken down in smaller ones.
390 # FIXME: This method needs to be broken down in smaller ones.
370 current_line_number = self.GetCurrentLine()
391 current_line_num = self.GetCurrentLine()
371 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
392 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
372 # Capture Control-C
393 # Capture Control-C
373 if self._input_state == 'subprocess':
394 if self._input_state == 'subprocess':
@@ -413,7 +434,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
413 else:
434 else:
414 # Up history
435 # Up history
415 if event.KeyCode == wx.WXK_UP and (
436 if event.KeyCode == wx.WXK_UP and (
416 ( current_line_number == self.current_prompt_line and
437 ( current_line_num == self.current_prompt_line and
417 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
438 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
418 or event.ControlDown() ):
439 or event.ControlDown() ):
419 new_buffer = self.get_history_previous(
440 new_buffer = self.get_history_previous(
@@ -425,7 +446,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
425 self.GotoPos(self.current_prompt_pos)
446 self.GotoPos(self.current_prompt_pos)
426 # Down history
447 # Down history
427 elif event.KeyCode == wx.WXK_DOWN and (
448 elif event.KeyCode == wx.WXK_DOWN and (
428 ( current_line_number == self.LineCount -1 and
449 ( current_line_num == self.LineCount -1 and
429 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
450 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
430 or event.ControlDown() ):
451 or event.ControlDown() ):
431 new_buffer = self.get_history_next()
452 new_buffer = self.get_history_next()
@@ -433,15 +454,43 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
433 self.input_buffer = new_buffer
454 self.input_buffer = new_buffer
434 # Tab-completion
455 # Tab-completion
435 elif event.KeyCode == ord('\t'):
456 elif event.KeyCode == ord('\t'):
436 current_line, current_line_number = self.CurLine
457 current_line, current_line_num = self.CurLine
437 if not re.match(r'^\s*$', current_line):
458 if not re.match(r'^\s*$', current_line):
438 self.complete_current_input()
459 self.complete_current_input()
439 if self.AutoCompActive():
460 if self.AutoCompActive():
440 wx.CallAfter(self._popup_completion, create=True)
461 wx.CallAfter(self._popup_completion, create=True)
441 else:
462 else:
442 event.Skip()
463 event.Skip()
464 elif event.KeyCode == wx.WXK_BACK:
465 # If characters where erased, check if we have to
466 # remove a line.
467 # XXX: What about DEL?
468 # FIXME: This logics should be in ConsoleWidget, as it is
469 # independant of IPython
470 current_line, _ = self.CurLine
471 current_pos = self.GetCurrentPos()
472 current_line_num = self.LineFromPosition(current_pos)
473 current_col = self.GetColumn(current_pos)
474 len_prompt = len(self.continuation_prompt())
475 if ( current_line.startswith(self.continuation_prompt())
476 and current_col == len_prompt):
477 new_lines = []
478 for line_num, line in enumerate(
479 self.input_buffer.split('\n')):
480 if (line_num + self.current_prompt_line ==
481 current_line_num):
482 new_lines.append(line[len_prompt:])
483 else:
484 new_lines.append('\n'+line)
485 # The first character is '\n', due to the above
486 # code:
487 self.input_buffer = ''.join(new_lines)[1:]
488 self.GotoPos(current_pos - 1 - len_prompt)
489 else:
490 ConsoleWidget._on_key_down(self, event, skip=skip)
443 else:
491 else:
444 ConsoleWidget._on_key_down(self, event, skip=skip)
492 ConsoleWidget._on_key_down(self, event, skip=skip)
493
445
494
446
495
447 def _on_key_up(self, event, skip=True):
496 def _on_key_up(self, event, skip=True):
@@ -453,14 +502,40 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
453 wx.CallAfter(self._popup_completion, create=True)
502 wx.CallAfter(self._popup_completion, create=True)
454 else:
503 else:
455 ConsoleWidget._on_key_up(self, event, skip=skip)
504 ConsoleWidget._on_key_up(self, event, skip=skip)
505 # Make sure the continuation_prompts are always followed by a
506 # whitespace
507 new_lines = []
508 if self._input_state == 'readline':
509 position = self.GetCurrentPos()
510 continuation_prompt = self.continuation_prompt()[:-1]
511 for line in self.input_buffer.split('\n'):
512 if not line == continuation_prompt:
513 new_lines.append(line)
514 self.input_buffer = '\n'.join(new_lines)
515 self.GotoPos(position)
456
516
457
517
458 def _on_enter(self):
518 def _on_enter(self):
459 """ Called on return key down, in readline input_state.
519 """ Called on return key down, in readline input_state.
460 """
520 """
521 last_line_num = self.LineFromPosition(self.GetLength())
522 current_line_num = self.LineFromPosition(self.GetCurrentPos())
523 new_line_pos = (last_line_num - current_line_num)
461 if self.debug:
524 if self.debug:
462 print >>sys.__stdout__, repr(self.input_buffer)
525 print >>sys.__stdout__, repr(self.input_buffer)
463 PrefilterFrontEnd._on_enter(self)
526 self.write('\n', refresh=False)
527 # Under windows scintilla seems to be doing funny
528 # stuff to the line returns here, but the getter for
529 # input_buffer filters this out.
530 if sys.platform == 'win32':
531 self.input_buffer = self.input_buffer
532 old_prompt_num = self.current_prompt_pos
533 has_executed = PrefilterFrontEnd._on_enter(self,
534 new_line_pos=new_line_pos)
535 if old_prompt_num == self.current_prompt_pos:
536 # No execution has happened
537 self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
538 return has_executed
464
539
465
540
466 #--------------------------------------------------------------------------
541 #--------------------------------------------------------------------------
@@ -1007,7 +1007,17 b' def get_security_dir():'
1007 else:
1007 else:
1008 os.chmod(security_dir, 0700)
1008 os.chmod(security_dir, 0700)
1009 return security_dir
1009 return security_dir
1010
1010
1011 def get_log_dir():
1012 """Get the IPython log directory.
1013
1014 If the log directory does not exist, it is created.
1015 """
1016 log_dir = os.path.join(get_ipython_dir(), 'log')
1017 if not os.path.isdir(log_dir):
1018 os.mkdir(log_dir, 0777)
1019 return log_dir
1020
1011 #****************************************************************************
1021 #****************************************************************************
1012 # strings and text
1022 # strings and text
1013
1023
@@ -152,7 +152,11 b" def user_setup(ipythondir,rc_suffix,mode='install',interactive=True):"
152 printf = lambda s : None
152 printf = lambda s : None
153
153
154 # Install mode should be re-entrant: if the install dir already exists,
154 # Install mode should be re-entrant: if the install dir already exists,
155 # bail out cleanly
155 # bail out cleanly.
156 # XXX. This is too hasty to return. We need to check to make sure that
157 # all the expected config files and directories are actually there. We
158 # currently have a failure mode if someone deletes a needed config file
159 # but still has the ipythondir.
156 if mode == 'install' and os.path.isdir(ipythondir):
160 if mode == 'install' and os.path.isdir(ipythondir):
157 return
161 return
158
162
@@ -1474,8 +1478,9 b' class InteractiveShell(object,Magic):'
1474 #print "loading rl:",rlcommand # dbg
1478 #print "loading rl:",rlcommand # dbg
1475 readline.parse_and_bind(rlcommand)
1479 readline.parse_and_bind(rlcommand)
1476
1480
1477 # remove some chars from the delimiters list
1481 # Remove some chars from the delimiters list. If we encounter
1478 delims = readline.get_completer_delims()
1482 # unicode chars, discard them.
1483 delims = readline.get_completer_delims().encode("ascii", "ignore")
1479 delims = delims.translate(string._idmap,
1484 delims = delims.translate(string._idmap,
1480 self.rc.readline_remove_delims)
1485 self.rc.readline_remove_delims)
1481 readline.set_completer_delims(delims)
1486 readline.set_completer_delims(delims)
@@ -55,9 +55,9 b' from IPython.iplib import InteractiveShell'
55 from IPython.usage import cmd_line_usage,interactive_usage
55 from IPython.usage import cmd_line_usage,interactive_usage
56 from IPython.genutils import *
56 from IPython.genutils import *
57
57
58 def force_import(modname):
58 def force_import(modname,force_reload=False):
59 if modname in sys.modules:
59 if modname in sys.modules and force_reload:
60 print "reload",modname
60 info("reloading: %s" % modname)
61 reload(sys.modules[modname])
61 reload(sys.modules[modname])
62 else:
62 else:
63 __import__(modname)
63 __import__(modname)
@@ -625,25 +625,25 b" object? -> Details about 'object'. ?object also works, ?? prints more."
625 except:
625 except:
626 IP.InteractiveTB()
626 IP.InteractiveTB()
627 import_fail_info('ipy_system_conf')
627 import_fail_info('ipy_system_conf')
628
628
629 # only import prof module if ipythonrc-PROF was not found
629 # only import prof module if ipythonrc-PROF was not found
630 if opts_all.profile and not profile_handled_by_legacy:
630 if opts_all.profile and not profile_handled_by_legacy:
631 profmodname = 'ipy_profile_' + opts_all.profile
631 profmodname = 'ipy_profile_' + opts_all.profile
632 try:
632 try:
633
634 force_import(profmodname)
633 force_import(profmodname)
635 except:
634 except:
636 IP.InteractiveTB()
635 IP.InteractiveTB()
637 print "Error importing",profmodname,"- perhaps you should run %upgrade?"
636 print "Error importing",profmodname,\
637 "- perhaps you should run %upgrade?"
638 import_fail_info(profmodname)
638 import_fail_info(profmodname)
639 else:
639 else:
640 opts.profile = opts_all.profile
640 opts.profile = opts_all.profile
641 else:
641 else:
642 force_import('ipy_profile_none')
642 force_import('ipy_profile_none')
643 # XXX - this is wrong: ipy_user_conf should not be loaded unconditionally,
644 # since the user could have specified a config file path by hand.
643 try:
645 try:
644
645 force_import('ipy_user_conf')
646 force_import('ipy_user_conf')
646
647 except:
647 except:
648 conf = opts_all.ipythondir + "/ipy_user_conf.py"
648 conf = opts_all.ipythondir + "/ipy_user_conf.py"
649 IP.InteractiveTB()
649 IP.InteractiveTB()
@@ -15,10 +15,11 b' if they need blocking clients or in `asyncclient.py` if they want asynchronous,'
15 deferred/Twisted using clients.
15 deferred/Twisted using clients.
16 """
16 """
17 __docformat__ = "restructuredtext en"
17 __docformat__ = "restructuredtext en"
18 #-------------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Copyright (C) 2008 The IPython Development Team
19 # Copyright (C) 2008 The IPython Development Team
20 #
20 #
21 # Distributed under the terms of the BSD License. The full license is in
21 # Distributed under the terms of the BSD License. The full license is in
22 # the file COPYING, distributed as part of this software.
22 # the file COPYING, distributed as part of this software.
23 #-------------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 No newline at end of file
24
25 from IPython.kernel.error import TaskRejectError No newline at end of file
@@ -15,6 +15,7 b' __docformat__ = "restructuredtext en"'
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 import os, sys
18 from os.path import join as pjoin
19 from os.path import join as pjoin
19
20
20 from IPython.external.configobj import ConfigObj
21 from IPython.external.configobj import ConfigObj
@@ -23,6 +24,7 b' from IPython.genutils import get_ipython_dir, get_security_dir'
23
24
24 default_kernel_config = ConfigObj()
25 default_kernel_config = ConfigObj()
25
26
27 # This will raise OSError if ipythondir doesn't exist.
26 security_dir = get_security_dir()
28 security_dir = get_security_dir()
27
29
28 #-------------------------------------------------------------------------------
30 #-------------------------------------------------------------------------------
@@ -679,21 +679,22 b' class Interpreter(object):'
679 # to exec will fail however. There seems to be some inconsistency in
679 # to exec will fail however. There seems to be some inconsistency in
680 # how trailing whitespace is handled, but this seems to work.
680 # how trailing whitespace is handled, but this seems to work.
681 python = python.strip()
681 python = python.strip()
682
682
683 # The compiler module does not like unicode. We need to convert
683 # The compiler module does not like unicode. We need to convert
684 # it encode it:
684 # it encode it:
685 if isinstance(python, unicode):
685 if isinstance(python, unicode):
686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
687 # encode string.
687 # encode string.
688 python = '\xef\xbb\xbf' + python.encode('utf-8')
688 python = '\xef\xbb\xbf' + python.encode('utf-8')
689
689
690 # The compiler module will parse the code into an abstract syntax tree.
690 # The compiler module will parse the code into an abstract syntax tree.
691 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
691 ast = compiler.parse(python)
692 ast = compiler.parse(python)
692
693
693 # Uncomment to help debug the ast tree
694 # Uncomment to help debug the ast tree
694 # for n in ast.node:
695 # for n in ast.node:
695 # print n.lineno,'->',n
696 # print n.lineno,'->',n
696
697
697 # Each separate command is available by iterating over ast.node. The
698 # Each separate command is available by iterating over ast.node. The
698 # lineno attribute is the line number (1-indexed) beginning the commands
699 # lineno attribute is the line number (1-indexed) beginning the commands
699 # suite.
700 # suite.
@@ -703,20 +704,26 b' class Interpreter(object):'
703 # We might eventually discover other cases where lineno is None and have
704 # We might eventually discover other cases where lineno is None and have
704 # to put in a more sophisticated test.
705 # to put in a more sophisticated test.
705 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
706 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
706
707
707 # When we finally get the slices, we will need to slice all the way to
708 # When we finally get the slices, we will need to slice all the way to
708 # the end even though we don't have a line number for it. Fortunately,
709 # the end even though we don't have a line number for it. Fortunately,
709 # None does the job nicely.
710 # None does the job nicely.
710 linenos.append(None)
711 linenos.append(None)
712
713 # Same problem at the other end: sometimes the ast tree has its
714 # first complete statement not starting on line 0. In this case
715 # we might miss part of it. This fixes ticket 266993. Thanks Gael!
716 linenos[0] = 0
717
711 lines = python.splitlines()
718 lines = python.splitlines()
712
719
713 # Create a list of atomic commands.
720 # Create a list of atomic commands.
714 cmds = []
721 cmds = []
715 for i, j in zip(linenos[:-1], linenos[1:]):
722 for i, j in zip(linenos[:-1], linenos[1:]):
716 cmd = lines[i:j]
723 cmd = lines[i:j]
717 if cmd:
724 if cmd:
718 cmds.append('\n'.join(cmd)+'\n')
725 cmds.append('\n'.join(cmd)+'\n')
719
726
720 return cmds
727 return cmds
721
728
722 def error(self, text):
729 def error(self, text):
@@ -15,6 +15,8 b' __docformat__ = "restructuredtext en"'
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Tell nose to skip the testing of this module
19 __test__ = {}
18
20
19 class NotificationCenter(object):
21 class NotificationCenter(object):
20 """Synchronous notification center
22 """Synchronous notification center
@@ -2,25 +2,61 b''
2
2
3 """This file contains unittests for the interpreter.py module."""
3 """This file contains unittests for the interpreter.py module."""
4
4
5 __docformat__ = "restructuredtext en"
6
7 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
6 # Copyright (C) 2008-2009 The IPython Development Team
9 #
7 #
10 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is
11 # the file COPYING, distributed as part of this software.
9 # in the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
13
11
14 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
15 # Imports
13 # Imports
16 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
17
15
16 # Tell nose to skip this module
17 __test__ = {}
18
19 from twisted.trial import unittest
18 from IPython.kernel.core.interpreter import Interpreter
20 from IPython.kernel.core.interpreter import Interpreter
19
21
20 def test_unicode():
22 #-----------------------------------------------------------------------------
21 """ Test unicode handling with the interpreter.
23 # Tests
22 """
24 #-----------------------------------------------------------------------------
23 i = Interpreter()
25
24 i.execute_python(u'print "ù"')
26 class TestInterpreter(unittest.TestCase):
25 i.execute_python('print "ù"')
27
28 def test_unicode(self):
29 """ Test unicode handling with the interpreter."""
30 i = Interpreter()
31 i.execute_python(u'print "ù"')
32 i.execute_python('print "ù"')
33
34 def test_ticket266993(self):
35 """ Test for ticket 266993."""
36 i = Interpreter()
37 i.execute('str("""a\nb""")')
38
39 def test_ticket364347(self):
40 """Test for ticket 364347."""
41 i = Interpreter()
42 i.split_commands('str("a\\nb")')
43
44 def test_split_commands(self):
45 """ Test that commands are indeed individually split."""
46 i = Interpreter()
47 test_atoms = [('(1\n + 1)', ),
48 ('1', '1', ),
49 ]
50 for atoms in test_atoms:
51 atoms = [atom.rstrip() + '\n' for atom in atoms]
52 self.assertEquals(i.split_commands(''.join(atoms)),atoms)
53
54 def test_long_lines(self):
55 """ Test for spurious syntax error created by the interpreter."""
56 test_strings = [u'( 1 +\n 1\n )\n\n',
57 u'(1 \n + 1\n )\n\n',
58 ]
59 i = Interpreter()
60 for s in test_strings:
61 i.execute(s)
26
62
@@ -2,26 +2,26 b''
2
2
3 """This file contains unittests for the notification.py module."""
3 """This file contains unittests for the notification.py module."""
4
4
5 __docformat__ = "restructuredtext en"
6
7 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
6 # Copyright (C) 2008-2009 The IPython Development Team
9 #
7 #
10 # Distributed under the terms of the BSD License. The full license is in
8 # Distributed under the terms of the BSD License. The full license is
11 # the file COPYING, distributed as part of this software.
9 # in the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
13
11
14 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
15 # Imports
13 # Imports
16 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
17
15
18 import unittest
16 # Tell nose to skip this module
17 __test__ = {}
18
19 from twisted.trial import unittest
19 import IPython.kernel.core.notification as notification
20 import IPython.kernel.core.notification as notification
20 from nose.tools import timed
21
21
22 #
22 #-----------------------------------------------------------------------------
23 # Supporting test classes
23 # Support Classes
24 #
24 #-----------------------------------------------------------------------------
25
25
26 class Observer(object):
26 class Observer(object):
27 """docstring for Observer"""
27 """docstring for Observer"""
@@ -36,7 +36,6 b' class Observer(object):'
36 self.expectedType,
36 self.expectedType,
37 self.expectedSender)
37 self.expectedSender)
38
38
39
40 def callback(self, theType, sender, args={}):
39 def callback(self, theType, sender, args={}):
41 """callback"""
40 """callback"""
42
41
@@ -47,7 +46,6 b' class Observer(object):'
47 assert(args == self.expectedKwArgs)
46 assert(args == self.expectedKwArgs)
48 self.recieved = True
47 self.recieved = True
49
48
50
51 def verify(self):
49 def verify(self):
52 """verify"""
50 """verify"""
53
51
@@ -57,7 +55,6 b' class Observer(object):'
57 """reset"""
55 """reset"""
58
56
59 self.recieved = False
57 self.recieved = False
60
61
58
62
59
63 class Notifier(object):
60 class Notifier(object):
@@ -72,11 +69,10 b' class Notifier(object):'
72
69
73 center.post_notification(self.theType, self,
70 center.post_notification(self.theType, self,
74 **self.kwargs)
71 **self.kwargs)
75
76
72
77 #
73 #-----------------------------------------------------------------------------
78 # Test Cases
74 # Tests
79 #
75 #-----------------------------------------------------------------------------
80
76
81 class NotificationTests(unittest.TestCase):
77 class NotificationTests(unittest.TestCase):
82 """docstring for NotificationTests"""
78 """docstring for NotificationTests"""
@@ -94,7 +90,6 b' class NotificationTests(unittest.TestCase):'
94
90
95 observer.verify()
91 observer.verify()
96
92
97
98 def test_type_specificity(self):
93 def test_type_specificity(self):
99 """Test that observers are registered by type"""
94 """Test that observers are registered by type"""
100
95
@@ -109,7 +104,6 b' class NotificationTests(unittest.TestCase):'
109
104
110 observer.verify()
105 observer.verify()
111
106
112
113 def test_sender_specificity(self):
107 def test_sender_specificity(self):
114 """Test that observers are registered by sender"""
108 """Test that observers are registered by sender"""
115
109
@@ -123,7 +117,6 b' class NotificationTests(unittest.TestCase):'
123
117
124 observer.verify()
118 observer.verify()
125
119
126
127 def test_remove_all_observers(self):
120 def test_remove_all_observers(self):
128 """White-box test for remove_all_observers"""
121 """White-box test for remove_all_observers"""
129
122
@@ -136,8 +129,7 b' class NotificationTests(unittest.TestCase):'
136 notification.sharedCenter.remove_all_observers()
129 notification.sharedCenter.remove_all_observers()
137
130
138 self.assert_(len(notification.sharedCenter.observers) == 0, "observers removed")
131 self.assert_(len(notification.sharedCenter.observers) == 0, "observers removed")
139
132
140
141 def test_any_sender(self):
133 def test_any_sender(self):
142 """test_any_sender"""
134 """test_any_sender"""
143
135
@@ -153,9 +145,7 b' class NotificationTests(unittest.TestCase):'
153 observer.reset()
145 observer.reset()
154 sender2.post()
146 sender2.post()
155 observer.verify()
147 observer.verify()
156
148
157
158 @timed(.01)
159 def test_post_performance(self):
149 def test_post_performance(self):
160 """Test that post_notification, even with many registered irrelevant
150 """Test that post_notification, even with many registered irrelevant
161 observers is fast"""
151 observers is fast"""
@@ -168,4 +158,4 b' class NotificationTests(unittest.TestCase):'
168 notification.sharedCenter.post_notification('EXPECTED_TYPE', self)
158 notification.sharedCenter.post_notification('EXPECTED_TYPE', self)
169
159
170 o.verify()
160 o.verify()
171
161
@@ -2,69 +2,77 b''
2 """
2 """
3 Test the output capture at the OS level, using file descriptors.
3 Test the output capture at the OS level, using file descriptors.
4 """
4 """
5
5 #-----------------------------------------------------------------------------
6 __docformat__ = "restructuredtext en"
6 # Copyright (C) 2008-2009 The IPython Development Team
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
10 #
7 #
11 # Distributed under the terms of the BSD License. The full license is
8 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
9 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
14
11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 # Tell nose to skip this module
17 __test__ = {}
15
18
16 # Stdlib imports
17 import os
18 from cStringIO import StringIO
19 from cStringIO import StringIO
20 import os
21
22 from twisted.trial import unittest
19
23
20 # Our own imports
24 from IPython.testing import decorators_trial as dec
21 from IPython.testing import decorators as dec
22
25
23 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
24 # Test functions
27 # Tests
28 #-----------------------------------------------------------------------------
25
29
26 @dec.skip_win32
27 def test_redirector():
28 """ Checks that the redirector can be used to do synchronous capture.
29 """
30 from IPython.kernel.core.fd_redirector import FDRedirector
31 r = FDRedirector()
32 out = StringIO()
33 try:
34 r.start()
35 for i in range(10):
36 os.system('echo %ic' % i)
37 print >>out, r.getvalue(),
38 print >>out, i
39 except:
40 r.stop()
41 raise
42 r.stop()
43 result1 = out.getvalue()
44 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
45 assert result1 == result2
46
30
31 class TestRedirector(unittest.TestCase):
47
32
48 @dec.skip_win32
33 @dec.skip_win32
49 def test_redirector_output_trap():
34 def test_redirector(self):
50 """ This test check not only that the redirector_output_trap does
35 """Checks that the redirector can be used to do synchronous capture.
36 """
37 from IPython.kernel.core.fd_redirector import FDRedirector
38 r = FDRedirector()
39 out = StringIO()
40 try:
41 r.start()
42 for i in range(10):
43 os.system('echo %ic' % i)
44 print >>out, r.getvalue(),
45 print >>out, i
46 except:
47 r.stop()
48 raise
49 r.stop()
50 result1 = out.getvalue()
51 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
52 self.assertEquals(result1, result2)
53
54 @dec.skip_win32
55 def test_redirector_output_trap(self):
56 """Check the greedy trapping behavior of the traps.
57
58 This test check not only that the redirector_output_trap does
51 trap the output, but also that it does it in a gready way, that
59 trap the output, but also that it does it in a gready way, that
52 is by calling the callback ASAP.
60 is by calling the callback ASAP.
53 """
61 """
54 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
62 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
55 out = StringIO()
63 out = StringIO()
56 trap = RedirectorOutputTrap(out.write, out.write)
64 trap = RedirectorOutputTrap(out.write, out.write)
57 try:
65 try:
58 trap.set()
66 trap.set()
59 for i in range(10):
67 for i in range(10):
60 os.system('echo %ic' % i)
68 os.system('echo %ic' % i)
61 print "%ip" % i
69 print "%ip" % i
62 print >>out, i
70 print >>out, i
63 except:
71 except:
72 trap.unset()
73 raise
64 trap.unset()
74 trap.unset()
65 raise
75 result1 = out.getvalue()
66 trap.unset()
76 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
67 result1 = out.getvalue()
77 self.assertEquals(result1, result2)
68 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
78
69 assert result1 == result2
70
@@ -268,6 +268,8 b' def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):'
268 # This lets us get fully syntax-highlighted tracebacks.
268 # This lets us get fully syntax-highlighted tracebacks.
269 if scheme is None:
269 if scheme is None:
270 try:
270 try:
271 # Again, reference to a global __IPYTHON__ that doesn't exist.
272 # XXX
271 scheme = __IPYTHON__.rc.colors
273 scheme = __IPYTHON__.rc.colors
272 except:
274 except:
273 scheme = DEFAULT_SCHEME
275 scheme = DEFAULT_SCHEME
@@ -487,10 +489,14 b' class ListTB(TBTools):'
487 else:
489 else:
488 list.append('%s\n' % str(stype))
490 list.append('%s\n' % str(stype))
489
491
490 # vds:>>
492 # This is being commented out for now as the __IPYTHON__ variable
491 if have_filedata:
493 # referenced here is not resolved and causes massive test failures
492 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
494 # and errors. B. Granger, 04/2009. XXX
493 # vds:<<
495 # See https://bugs.launchpad.net/bugs/362137
496 # # vds:>>
497 # if have_filedata:
498 # __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
499 # # vds:<<
494
500
495 return list
501 return list
496
502
@@ -804,13 +810,17 b' class VerboseTB(TBTools):'
804 value = text_repr(getattr(evalue, name))
810 value = text_repr(getattr(evalue, name))
805 exception.append('\n%s%s = %s' % (indent, name, value))
811 exception.append('\n%s%s = %s' % (indent, name, value))
806
812
807 # vds: >>
813 # This is being commented out for now as the __IPYTHON__ variable
808 if records:
814 # referenced here is not resolved and causes massive test failures
809 filepath, lnum = records[-1][1:3]
815 # and errors. B. Granger, 04/2009. XXX
810 #print "file:", str(file), "linenb", str(lnum) # dbg
816 # See https://bugs.launchpad.net/bugs/362137
811 filepath = os.path.abspath(filepath)
817 # # vds: >>
812 __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
818 # if records:
813 # vds: <<
819 # filepath, lnum = records[-1][1:3]
820 # #print "file:", str(file), "linenb", str(lnum) # dbg
821 # filepath = os.path.abspath(filepath)
822 # __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
823 # # vds: <<
814
824
815 # return all our info assembled as a single string
825 # return all our info assembled as a single string
816 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
826 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
@@ -67,10 +67,10 b' class EngineConnector(object):'
67 self.furl = find_furl(furl_or_file)
67 self.furl = find_furl(furl_or_file)
68 except ValueError:
68 except ValueError:
69 return defer.fail(failure.Failure())
69 return defer.fail(failure.Failure())
70 # return defer.fail(failure.Failure(ValueError('not a valid furl or furl file: %r' % furl_or_file)))
70 else:
71 d = self.tub.getReference(self.furl)
71 d = self.tub.getReference(self.furl)
72 d.addCallbacks(self._register, self._log_failure)
72 d.addCallbacks(self._register, self._log_failure)
73 return d
73 return d
74
74
75 def _log_failure(self, reason):
75 def _log_failure(self, reason):
76 log.err('EngineConnector: engine registration failed:')
76 log.err('EngineConnector: engine registration failed:')
@@ -34,6 +34,9 b' __docformat__ = "restructuredtext en"'
34 # Imports
34 # Imports
35 #-------------------------------------------------------------------------------
35 #-------------------------------------------------------------------------------
36
36
37 # Tell nose to skip the testing of this module
38 __test__ = {}
39
37 import os, sys, copy
40 import os, sys, copy
38 import cPickle as pickle
41 import cPickle as pickle
39 from new import instancemethod
42 from new import instancemethod
@@ -266,8 +269,8 b' class StrictDict(dict):'
266 pickle.dumps(key, 2)
269 pickle.dumps(key, 2)
267 pickle.dumps(value, 2)
270 pickle.dumps(value, 2)
268 newvalue = copy.deepcopy(value)
271 newvalue = copy.deepcopy(value)
269 except:
272 except Exception, e:
270 raise error.InvalidProperty(value)
273 raise error.InvalidProperty("can't be a value: %r" % value)
271 dict.__setitem__(self, key, newvalue)
274 dict.__setitem__(self, key, newvalue)
272 self.modified = True
275 self.modified = True
273
276
@@ -104,6 +104,23 b' class StopLocalExecution(KernelError):'
104 class SecurityError(KernelError):
104 class SecurityError(KernelError):
105 pass
105 pass
106
106
107 class FileTimeoutError(KernelError):
108 pass
109
110 class TaskRejectError(KernelError):
111 """Exception to raise when a task should be rejected by an engine.
112
113 This exception can be used to allow a task running on an engine to test
114 if the engine (or the user's namespace on the engine) has the needed
115 task dependencies. If not, the task should raise this exception. For
116 the task to be retried on another engine, the task should be created
117 with the `retries` argument > 1.
118
119 The advantage of this approach over our older properties system is that
120 tasks have full access to the user's namespace on the engines and the
121 properties don't have to be managed or tested by the controller.
122 """
123
107 class CompositeError(KernelError):
124 class CompositeError(KernelError):
108 def __init__(self, message, elist):
125 def __init__(self, message, elist):
109 Exception.__init__(self, *(message, elist))
126 Exception.__init__(self, *(message, elist))
@@ -20,6 +20,7 b' import sys'
20 import cPickle as pickle
20 import cPickle as pickle
21 from types import FunctionType
21 from types import FunctionType
22 import linecache
22 import linecache
23 import warnings
23
24
24 from twisted.internet import reactor
25 from twisted.internet import reactor
25 from twisted.python import components, log
26 from twisted.python import components, log
@@ -389,6 +390,14 b' def strip_whitespace(source):'
389 #-------------------------------------------------------------------------------
390 #-------------------------------------------------------------------------------
390
391
391
392
393 _prop_warn = """\
394
395 We are currently refactoring the task dependency system. This might
396 involve the removal of this method and other methods related to engine
397 properties. Please see the docstrings for IPython.kernel.TaskRejectError
398 for more information."""
399
400
392 class IFullBlockingMultiEngineClient(Interface):
401 class IFullBlockingMultiEngineClient(Interface):
393 pass
402 pass
394
403
@@ -730,22 +739,27 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
730 return self._blockFromThread(self.smultiengine.queue_status, targets=targets, block=block)
739 return self._blockFromThread(self.smultiengine.queue_status, targets=targets, block=block)
731
740
732 def set_properties(self, properties, targets=None, block=None):
741 def set_properties(self, properties, targets=None, block=None):
742 warnings.warn(_prop_warn)
733 targets, block = self._findTargetsAndBlock(targets, block)
743 targets, block = self._findTargetsAndBlock(targets, block)
734 return self._blockFromThread(self.smultiengine.set_properties, properties, targets=targets, block=block)
744 return self._blockFromThread(self.smultiengine.set_properties, properties, targets=targets, block=block)
735
745
736 def get_properties(self, keys=None, targets=None, block=None):
746 def get_properties(self, keys=None, targets=None, block=None):
747 warnings.warn(_prop_warn)
737 targets, block = self._findTargetsAndBlock(targets, block)
748 targets, block = self._findTargetsAndBlock(targets, block)
738 return self._blockFromThread(self.smultiengine.get_properties, keys, targets=targets, block=block)
749 return self._blockFromThread(self.smultiengine.get_properties, keys, targets=targets, block=block)
739
750
740 def has_properties(self, keys, targets=None, block=None):
751 def has_properties(self, keys, targets=None, block=None):
752 warnings.warn(_prop_warn)
741 targets, block = self._findTargetsAndBlock(targets, block)
753 targets, block = self._findTargetsAndBlock(targets, block)
742 return self._blockFromThread(self.smultiengine.has_properties, keys, targets=targets, block=block)
754 return self._blockFromThread(self.smultiengine.has_properties, keys, targets=targets, block=block)
743
755
744 def del_properties(self, keys, targets=None, block=None):
756 def del_properties(self, keys, targets=None, block=None):
757 warnings.warn(_prop_warn)
745 targets, block = self._findTargetsAndBlock(targets, block)
758 targets, block = self._findTargetsAndBlock(targets, block)
746 return self._blockFromThread(self.smultiengine.del_properties, keys, targets=targets, block=block)
759 return self._blockFromThread(self.smultiengine.del_properties, keys, targets=targets, block=block)
747
760
748 def clear_properties(self, targets=None, block=None):
761 def clear_properties(self, targets=None, block=None):
762 warnings.warn(_prop_warn)
749 targets, block = self._findTargetsAndBlock(targets, block)
763 targets, block = self._findTargetsAndBlock(targets, block)
750 return self._blockFromThread(self.smultiengine.clear_properties, targets=targets, block=block)
764 return self._blockFromThread(self.smultiengine.clear_properties, targets=targets, block=block)
751
765
@@ -1,4 +1,4 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 """Start an IPython cluster = (controller + engines)."""
4 """Start an IPython cluster = (controller + engines)."""
@@ -29,29 +29,35 b' from twisted.python import failure, log'
29
29
30 from IPython.external import argparse
30 from IPython.external import argparse
31 from IPython.external import Itpl
31 from IPython.external import Itpl
32 from IPython.genutils import get_ipython_dir, num_cpus
32 from IPython.genutils import (
33 get_ipython_dir,
34 get_log_dir,
35 get_security_dir,
36 num_cpus
37 )
33 from IPython.kernel.fcutil import have_crypto
38 from IPython.kernel.fcutil import have_crypto
34 from IPython.kernel.error import SecurityError
39
40 # Create various ipython directories if they don't exist.
41 # This must be done before IPython.kernel.config is imported.
42 from IPython.iplib import user_setup
43 if os.name == 'posix':
44 rc_suffix = ''
45 else:
46 rc_suffix = '.ini'
47 user_setup(get_ipython_dir(), rc_suffix, mode='install', interactive=False)
48 get_log_dir()
49 get_security_dir()
50
51 from IPython.kernel.config import config_manager as kernel_config_manager
52 from IPython.kernel.error import SecurityError, FileTimeoutError
35 from IPython.kernel.fcutil import have_crypto
53 from IPython.kernel.fcutil import have_crypto
36 from IPython.kernel.twistedutil import gatherBoth
54 from IPython.kernel.twistedutil import gatherBoth, wait_for_file
37 from IPython.kernel.util import printer
55 from IPython.kernel.util import printer
38
56
39
40 #-----------------------------------------------------------------------------
57 #-----------------------------------------------------------------------------
41 # General process handling code
58 # General process handling code
42 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
43
60
44 def find_exe(cmd):
45 try:
46 import win32api
47 except ImportError:
48 raise ImportError('you need to have pywin32 installed for this to work')
49 else:
50 try:
51 (path, offest) = win32api.SearchPath(os.environ['PATH'],cmd + '.exe')
52 except:
53 (path, offset) = win32api.SearchPath(os.environ['PATH'],cmd + '.bat')
54 return path
55
61
56 class ProcessStateError(Exception):
62 class ProcessStateError(Exception):
57 pass
63 pass
@@ -184,8 +190,10 b' class ControllerLauncher(ProcessLauncher):'
184 from IPython.kernel.scripts import ipcontroller
190 from IPython.kernel.scripts import ipcontroller
185 script_location = ipcontroller.__file__.replace('.pyc', '.py')
191 script_location = ipcontroller.__file__.replace('.pyc', '.py')
186 # The -u option here turns on unbuffered output, which is required
192 # The -u option here turns on unbuffered output, which is required
187 # on Win32 to prevent wierd conflict and problems with Twisted
193 # on Win32 to prevent wierd conflict and problems with Twisted.
188 args = [find_exe('python'), '-u', script_location]
194 # Also, use sys.executable to make sure we are picking up the
195 # right python exe.
196 args = [sys.executable, '-u', script_location]
189 else:
197 else:
190 args = ['ipcontroller']
198 args = ['ipcontroller']
191 self.extra_args = extra_args
199 self.extra_args = extra_args
@@ -204,8 +212,10 b' class EngineLauncher(ProcessLauncher):'
204 from IPython.kernel.scripts import ipengine
212 from IPython.kernel.scripts import ipengine
205 script_location = ipengine.__file__.replace('.pyc', '.py')
213 script_location = ipengine.__file__.replace('.pyc', '.py')
206 # The -u option here turns on unbuffered output, which is required
214 # The -u option here turns on unbuffered output, which is required
207 # on Win32 to prevent wierd conflict and problems with Twisted
215 # on Win32 to prevent wierd conflict and problems with Twisted.
208 args = [find_exe('python'), '-u', script_location]
216 # Also, use sys.executable to make sure we are picking up the
217 # right python exe.
218 args = [sys.executable, '-u', script_location]
209 else:
219 else:
210 args = ['ipengine']
220 args = ['ipengine']
211 self.extra_args = extra_args
221 self.extra_args = extra_args
@@ -465,7 +475,9 b' class SSHEngineSet(object):'
465 # The main functions should then just parse the command line arguments, create
475 # The main functions should then just parse the command line arguments, create
466 # the appropriate class and call a 'start' method.
476 # the appropriate class and call a 'start' method.
467
477
478
468 def check_security(args, cont_args):
479 def check_security(args, cont_args):
480 """Check to see if we should run with SSL support."""
469 if (not args.x or not args.y) and not have_crypto:
481 if (not args.x or not args.y) and not have_crypto:
470 log.err("""
482 log.err("""
471 OpenSSL/pyOpenSSL is not available, so we can't run in secure mode.
483 OpenSSL/pyOpenSSL is not available, so we can't run in secure mode.
@@ -478,7 +490,9 b' Try running ipcluster with the -xy flags: ipcluster local -xy -n 4""")'
478 cont_args.append('-y')
490 cont_args.append('-y')
479 return True
491 return True
480
492
493
481 def check_reuse(args, cont_args):
494 def check_reuse(args, cont_args):
495 """Check to see if we should try to resuse FURL files."""
482 if args.r:
496 if args.r:
483 cont_args.append('-r')
497 cont_args.append('-r')
484 if args.client_port == 0 or args.engine_port == 0:
498 if args.client_port == 0 or args.engine_port == 0:
@@ -491,6 +505,25 b' the --client-port and --engine-port options.""")'
491 cont_args.append('--engine-port=%i' % args.engine_port)
505 cont_args.append('--engine-port=%i' % args.engine_port)
492 return True
506 return True
493
507
508
509 def _err_and_stop(f):
510 """Errback to log a failure and halt the reactor on a fatal error."""
511 log.err(f)
512 reactor.stop()
513
514
515 def _delay_start(cont_pid, start_engines, furl_file, reuse):
516 """Wait for controller to create FURL files and the start the engines."""
517 if not reuse:
518 if os.path.isfile(furl_file):
519 os.unlink(furl_file)
520 log.msg('Waiting for controller to finish starting...')
521 d = wait_for_file(furl_file, delay=0.2, max_tries=50)
522 d.addCallback(lambda _: log.msg('Controller started'))
523 d.addCallback(lambda _: start_engines(cont_pid))
524 return d
525
526
494 def main_local(args):
527 def main_local(args):
495 cont_args = []
528 cont_args = []
496 cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
529 cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
@@ -520,13 +553,10 b' def main_local(args):'
520 signal.signal(signal.SIGINT,shutdown)
553 signal.signal(signal.SIGINT,shutdown)
521 d = eset.start(args.n)
554 d = eset.start(args.n)
522 return d
555 return d
523 def delay_start(cont_pid):
556 config = kernel_config_manager.get_config_obj()
524 # This is needed because the controller doesn't start listening
557 furl_file = config['controller']['engine_furl_file']
525 # right when it starts and the controller needs to write
558 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
526 # furl files for the engine to pick up
559 dstart.addErrback(_err_and_stop)
527 reactor.callLater(1.0, start_engines, cont_pid)
528 dstart.addCallback(delay_start)
529 dstart.addErrback(lambda f: f.raiseException())
530
560
531
561
532 def main_mpi(args):
562 def main_mpi(args):
@@ -562,13 +592,10 b' def main_mpi(args):'
562 signal.signal(signal.SIGINT,shutdown)
592 signal.signal(signal.SIGINT,shutdown)
563 d = eset.start()
593 d = eset.start()
564 return d
594 return d
565 def delay_start(cont_pid):
595 config = kernel_config_manager.get_config_obj()
566 # This is needed because the controller doesn't start listening
596 furl_file = config['controller']['engine_furl_file']
567 # right when it starts and the controller needs to write
597 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
568 # furl files for the engine to pick up
598 dstart.addErrback(_err_and_stop)
569 reactor.callLater(1.0, start_engines, cont_pid)
570 dstart.addCallback(delay_start)
571 dstart.addErrback(lambda f: f.raiseException())
572
599
573
600
574 def main_pbs(args):
601 def main_pbs(args):
@@ -595,8 +622,10 b' def main_pbs(args):'
595 signal.signal(signal.SIGINT,shutdown)
622 signal.signal(signal.SIGINT,shutdown)
596 d = pbs_set.start(args.n)
623 d = pbs_set.start(args.n)
597 return d
624 return d
598 dstart.addCallback(start_engines)
625 config = kernel_config_manager.get_config_obj()
599 dstart.addErrback(lambda f: f.raiseException())
626 furl_file = config['controller']['engine_furl_file']
627 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
628 dstart.addErrback(_err_and_stop)
600
629
601
630
602 def main_ssh(args):
631 def main_ssh(args):
@@ -637,12 +666,10 b' def main_ssh(args):'
637 signal.signal(signal.SIGINT,shutdown)
666 signal.signal(signal.SIGINT,shutdown)
638 d = ssh_set.start(clusterfile['send_furl'])
667 d = ssh_set.start(clusterfile['send_furl'])
639 return d
668 return d
640
669 config = kernel_config_manager.get_config_obj()
641 def delay_start(cont_pid):
670 furl_file = config['controller']['engine_furl_file']
642 reactor.callLater(1.0, start_engines, cont_pid)
671 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
643
672 dstart.addErrback(_err_and_stop)
644 dstart.addCallback(delay_start)
645 dstart.addErrback(lambda f: f.raiseException())
646
673
647
674
648 def get_args():
675 def get_args():
@@ -697,8 +724,11 b' def get_args():'
697
724
698 parser = argparse.ArgumentParser(
725 parser = argparse.ArgumentParser(
699 description='IPython cluster startup. This starts a controller and\
726 description='IPython cluster startup. This starts a controller and\
700 engines using various approaches. THIS IS A TECHNOLOGY PREVIEW AND\
727 engines using various approaches. Use the IPYTHONDIR environment\
701 THE API WILL CHANGE SIGNIFICANTLY BEFORE THE FINAL RELEASE.'
728 variable to change your IPython directory from the default of\
729 .ipython or _ipython. The log and security subdirectories of your\
730 IPython directory will be used by this script for log files and\
731 security files.'
702 )
732 )
703 subparsers = parser.add_subparsers(
733 subparsers = parser.add_subparsers(
704 help='available cluster types. For help, do "ipcluster TYPE --help"')
734 help='available cluster types. For help, do "ipcluster TYPE --help"')
@@ -21,8 +21,10 b' __docformat__ = "restructuredtext en"'
21 import sys
21 import sys
22 sys.path.insert(0, '')
22 sys.path.insert(0, '')
23
23
24 import sys, time, os
25 from optparse import OptionParser
24 from optparse import OptionParser
25 import os
26 import time
27 import tempfile
26
28
27 from twisted.application import internet, service
29 from twisted.application import internet, service
28 from twisted.internet import reactor, error, defer
30 from twisted.internet import reactor, error, defer
@@ -37,6 +39,18 b' from IPython.kernel.error import SecurityError'
37 from IPython.kernel import controllerservice
39 from IPython.kernel import controllerservice
38 from IPython.kernel.fcutil import check_furl_file_security
40 from IPython.kernel.fcutil import check_furl_file_security
39
41
42 # Create various ipython directories if they don't exist.
43 # This must be done before IPython.kernel.config is imported.
44 from IPython.iplib import user_setup
45 from IPython.genutils import get_ipython_dir, get_log_dir, get_security_dir
46 if os.name == 'posix':
47 rc_suffix = ''
48 else:
49 rc_suffix = '.ini'
50 user_setup(get_ipython_dir(), rc_suffix, mode='install', interactive=False)
51 get_log_dir()
52 get_security_dir()
53
40 from IPython.kernel.config import config_manager as kernel_config_manager
54 from IPython.kernel.config import config_manager as kernel_config_manager
41 from IPython.config.cutils import import_item
55 from IPython.config.cutils import import_item
42
56
@@ -45,6 +59,10 b' from IPython.config.cutils import import_item'
45 # Code
59 # Code
46 #-------------------------------------------------------------------------------
60 #-------------------------------------------------------------------------------
47
61
62 def get_temp_furlfile(filename):
63 return tempfile.mktemp(dir=os.path.dirname(filename),
64 prefix=os.path.basename(filename))
65
48 def make_tub(ip, port, secure, cert_file):
66 def make_tub(ip, port, secure, cert_file):
49 """
67 """
50 Create a listening tub given an ip, port, and cert_file location.
68 Create a listening tub given an ip, port, and cert_file location.
@@ -107,13 +125,18 b' def make_client_service(controller_service, config):'
107 """Set the location for the tub and return a deferred."""
125 """Set the location for the tub and return a deferred."""
108
126
109 def register(empty, ref, furl_file):
127 def register(empty, ref, furl_file):
110 client_tub.registerReference(ref, furlFile=furl_file)
128 # We create and then move to make sure that when the file
129 # appears to other processes, the buffer has the flushed
130 # and the file has been closed
131 temp_furl_file = get_temp_furlfile(furl_file)
132 client_tub.registerReference(ref, furlFile=temp_furl_file)
133 os.rename(temp_furl_file, furl_file)
111
134
112 if location == '':
135 if location == '':
113 d = client_tub.setLocationAutomatically()
136 d = client_tub.setLocationAutomatically()
114 else:
137 else:
115 d = defer.maybeDeferred(client_tub.setLocation, "%s:%i" % (location, client_listener.getPortnum()))
138 d = defer.maybeDeferred(client_tub.setLocation, "%s:%i" % (location, client_listener.getPortnum()))
116
139
117 for ciname, ci in config['controller']['controller_interfaces'].iteritems():
140 for ciname, ci in config['controller']['controller_interfaces'].iteritems():
118 log.msg("Adapting Controller to interface: %s" % ciname)
141 log.msg("Adapting Controller to interface: %s" % ciname)
119 furl_file = ci['furl_file']
142 furl_file = ci['furl_file']
@@ -154,7 +177,12 b' def make_engine_service(controller_service, config):'
154 """Set the location for the tub and return a deferred."""
177 """Set the location for the tub and return a deferred."""
155
178
156 def register(empty, ref, furl_file):
179 def register(empty, ref, furl_file):
157 engine_tub.registerReference(ref, furlFile=furl_file)
180 # We create and then move to make sure that when the file
181 # appears to other processes, the buffer has the flushed
182 # and the file has been closed
183 temp_furl_file = get_temp_furlfile(furl_file)
184 engine_tub.registerReference(ref, furlFile=temp_furl_file)
185 os.rename(temp_furl_file, furl_file)
158
186
159 if location == '':
187 if location == '':
160 d = engine_tub.setLocationAutomatically()
188 d = engine_tub.setLocationAutomatically()
@@ -236,7 +264,14 b' def init_config():'
236 Initialize the configuration using default and command line options.
264 Initialize the configuration using default and command line options.
237 """
265 """
238
266
239 parser = OptionParser()
267 parser = OptionParser("""ipcontroller [options]
268
269 Start an IPython controller.
270
271 Use the IPYTHONDIR environment variable to change your IPython directory
272 from the default of .ipython or _ipython. The log and security
273 subdirectories of your IPython directory will be used by this script
274 for log files and security files.""")
240
275
241 # Client related options
276 # Client related options
242 parser.add_option(
277 parser.add_option(
@@ -325,12 +360,6 b' def init_config():'
325 help="log file name (default is stdout)"
360 help="log file name (default is stdout)"
326 )
361 )
327 parser.add_option(
362 parser.add_option(
328 "--ipythondir",
329 type="string",
330 dest="ipythondir",
331 help="look for config files and profiles in this directory"
332 )
333 parser.add_option(
334 "-r",
363 "-r",
335 action="store_true",
364 action="store_true",
336 dest="reuse_furls",
365 dest="reuse_furls",
@@ -339,7 +368,6 b' def init_config():'
339
368
340 (options, args) = parser.parse_args()
369 (options, args) = parser.parse_args()
341
370
342 kernel_config_manager.update_config_obj_from_default_file(options.ipythondir)
343 config = kernel_config_manager.get_config_obj()
371 config = kernel_config_manager.get_config_obj()
344
372
345 # Update with command line options
373 # Update with command line options
@@ -21,8 +21,8 b' __docformat__ = "restructuredtext en"'
21 import sys
21 import sys
22 sys.path.insert(0, '')
22 sys.path.insert(0, '')
23
23
24 import sys, os
25 from optparse import OptionParser
24 from optparse import OptionParser
25 import os
26
26
27 from twisted.application import service
27 from twisted.application import service
28 from twisted.internet import reactor
28 from twisted.internet import reactor
@@ -33,6 +33,19 b' from IPython.kernel.fcutil import Tub, UnauthenticatedTub'
33 from IPython.kernel.core.config import config_manager as core_config_manager
33 from IPython.kernel.core.config import config_manager as core_config_manager
34 from IPython.config.cutils import import_item
34 from IPython.config.cutils import import_item
35 from IPython.kernel.engineservice import EngineService
35 from IPython.kernel.engineservice import EngineService
36
37 # Create various ipython directories if they don't exist.
38 # This must be done before IPython.kernel.config is imported.
39 from IPython.iplib import user_setup
40 from IPython.genutils import get_ipython_dir, get_log_dir, get_security_dir
41 if os.name == 'posix':
42 rc_suffix = ''
43 else:
44 rc_suffix = '.ini'
45 user_setup(get_ipython_dir(), rc_suffix, mode='install', interactive=False)
46 get_log_dir()
47 get_security_dir()
48
36 from IPython.kernel.config import config_manager as kernel_config_manager
49 from IPython.kernel.config import config_manager as kernel_config_manager
37 from IPython.kernel.engineconnector import EngineConnector
50 from IPython.kernel.engineconnector import EngineConnector
38
51
@@ -106,13 +119,19 b' def start_engine():'
106 engine_connector = EngineConnector(tub_service)
119 engine_connector = EngineConnector(tub_service)
107 furl_file = kernel_config['engine']['furl_file']
120 furl_file = kernel_config['engine']['furl_file']
108 log.msg("Using furl file: %s" % furl_file)
121 log.msg("Using furl file: %s" % furl_file)
109 d = engine_connector.connect_to_controller(engine_service, furl_file)
110 def handle_error(f):
111 log.err(f)
112 if reactor.running:
113 reactor.stop()
114 d.addErrback(handle_error)
115
122
123 def call_connect(engine_service, furl_file):
124 d = engine_connector.connect_to_controller(engine_service, furl_file)
125 def handle_error(f):
126 # If this print statement is replaced by a log.err(f) I get
127 # an unhandled error, which makes no sense. I shouldn't have
128 # to use a print statement here. My only thought is that
129 # at the beginning of the process the logging is still starting up
130 print "error connecting to controller:", f.getErrorMessage()
131 reactor.callLater(0.1, reactor.stop)
132 d.addErrback(handle_error)
133
134 reactor.callWhenRunning(call_connect, engine_service, furl_file)
116 reactor.run()
135 reactor.run()
117
136
118
137
@@ -121,7 +140,14 b' def init_config():'
121 Initialize the configuration using default and command line options.
140 Initialize the configuration using default and command line options.
122 """
141 """
123
142
124 parser = OptionParser()
143 parser = OptionParser("""ipengine [options]
144
145 Start an IPython engine.
146
147 Use the IPYTHONDIR environment variable to change your IPython directory
148 from the default of .ipython or _ipython. The log and security
149 subdirectories of your IPython directory will be used by this script
150 for log files and security files.""")
125
151
126 parser.add_option(
152 parser.add_option(
127 "--furl-file",
153 "--furl-file",
@@ -142,18 +168,9 b' def init_config():'
142 dest="logfile",
168 dest="logfile",
143 help="log file name (default is stdout)"
169 help="log file name (default is stdout)"
144 )
170 )
145 parser.add_option(
146 "--ipythondir",
147 type="string",
148 dest="ipythondir",
149 help="look for config files and profiles in this directory"
150 )
151
171
152 (options, args) = parser.parse_args()
172 (options, args) = parser.parse_args()
153
173
154 kernel_config_manager.update_config_obj_from_default_file(options.ipythondir)
155 core_config_manager.update_config_obj_from_default_file(options.ipythondir)
156
157 kernel_config = kernel_config_manager.get_config_obj()
174 kernel_config = kernel_config_manager.get_config_obj()
158 # Now override with command line options
175 # Now override with command line options
159 if options.furl_file is not None:
176 if options.furl_file is not None:
@@ -16,6 +16,9 b' __docformat__ = "restructuredtext en"'
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # Tell nose to skip the testing of this module
20 __test__ = {}
21
19 import copy, time
22 import copy, time
20 from types import FunctionType
23 from types import FunctionType
21
24
@@ -49,7 +49,7 b' class BlockingTaskClient(object):'
49 """
49 """
50
50
51 implements(
51 implements(
52 IBlockingTaskClient,
52 IBlockingTaskClient,
53 ITaskMapperFactory,
53 ITaskMapperFactory,
54 IMapper,
54 IMapper,
55 ITaskParallelDecorator
55 ITaskParallelDecorator
@@ -62,7 +62,7 b' class BlockingTaskClient(object):'
62 def run(self, task, block=False):
62 def run(self, task, block=False):
63 """Run a task on the `TaskController`.
63 """Run a task on the `TaskController`.
64
64
65 See the documentation of the `MapTask` and `StringTask` classes for
65 See the documentation of the `MapTask` and `StringTask` classes for
66 details on how to build a task of different types.
66 details on how to build a task of different types.
67
67
68 :Parameters:
68 :Parameters:
@@ -363,7 +363,8 b' class IEnginePropertiesTestCase(object):'
363 p = get_engine(%s).properties"""%self.engine.id
363 p = get_engine(%s).properties"""%self.engine.id
364 d = self.engine.execute(s)
364 d = self.engine.execute(s)
365 d.addCallback(lambda r: self.engine.execute("p['a'] = lambda _:None"))
365 d.addCallback(lambda r: self.engine.execute("p['a'] = lambda _:None"))
366 d = self.assertDeferredRaises(d, error.InvalidProperty)
366 d.addErrback(lambda f: self.assertRaises(error.InvalidProperty,
367 f.raiseException))
367 d.addCallback(lambda r: self.engine.execute("p['a'] = range(5)"))
368 d.addCallback(lambda r: self.engine.execute("p['a'] = range(5)"))
368 d.addCallback(lambda r: self.engine.execute("p['a'].append(5)"))
369 d.addCallback(lambda r: self.engine.execute("p['a'].append(5)"))
369 d.addCallback(lambda r: self.engine.get_properties('a'))
370 d.addCallback(lambda r: self.engine.get_properties('a'))
@@ -1,3 +1,6 b''
1 # Tell nose to skip this module
2 __test__ = {}
3
1 #from __future__ import with_statement
4 #from __future__ import with_statement
2
5
3 # XXX This file is currently disabled to preserve 2.4 compatibility.
6 # XXX This file is currently disabled to preserve 2.4 compatibility.
@@ -23,15 +23,15 b' __docformat__ = "restructuredtext en"'
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 try:
26 # Tell nose to skip this module
27 from twisted.application.service import IService
27 __test__ = {}
28 from IPython.kernel.controllerservice import ControllerService
28
29 from IPython.kernel.tests import multienginetest as met
29 from twisted.application.service import IService
30 from controllertest import IControllerCoreTestCase
30 from IPython.kernel.controllerservice import ControllerService
31 from IPython.testing.util import DeferredTestCase
31 from IPython.kernel.tests import multienginetest as met
32 except ImportError:
32 from controllertest import IControllerCoreTestCase
33 import nose
33 from IPython.testing.util import DeferredTestCase
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
34
35
35
36 class BasicControllerServiceTest(DeferredTestCase,
36 class BasicControllerServiceTest(DeferredTestCase,
37 IControllerCoreTestCase):
37 IControllerCoreTestCase):
@@ -15,30 +15,29 b' __docformat__ = "restructuredtext en"'
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 try:
18 # Tell nose to skip this module
19 from twisted.python import components
19 __test__ = {}
20 from twisted.internet import reactor, defer
21 from twisted.spread import pb
22 from twisted.internet.base import DelayedCall
23 DelayedCall.debug = True
24
20
25 import zope.interface as zi
21 from twisted.python import components
22 from twisted.internet import reactor, defer
23 from twisted.spread import pb
24 from twisted.internet.base import DelayedCall
25 DelayedCall.debug = True
26
26
27 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
27 import zope.interface as zi
28 from IPython.kernel import engineservice as es
28
29 from IPython.testing.util import DeferredTestCase
29 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
30 from IPython.kernel.controllerservice import IControllerBase
30 from IPython.kernel import engineservice as es
31 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
31 from IPython.testing.util import DeferredTestCase
32 from IPython.kernel.engineservice import IEngineQueued
32 from IPython.kernel.controllerservice import IControllerBase
33 from IPython.kernel.engineconnector import EngineConnector
33 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
34
34 from IPython.kernel.engineservice import IEngineQueued
35 from IPython.kernel.tests.engineservicetest import \
35 from IPython.kernel.engineconnector import EngineConnector
36 IEngineCoreTestCase, \
36
37 IEngineSerializedTestCase, \
37 from IPython.kernel.tests.engineservicetest import \
38 IEngineQueuedTestCase
38 IEngineCoreTestCase, \
39 except ImportError:
39 IEngineSerializedTestCase, \
40 import nose
40 IEngineQueuedTestCase
41 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
42
41
43
42
44 class EngineFCTest(DeferredTestCase,
43 class EngineFCTest(DeferredTestCase,
@@ -23,20 +23,19 b' __docformat__ = "restructuredtext en"'
23 # Imports
23 # Imports
24 #-------------------------------------------------------------------------------
24 #-------------------------------------------------------------------------------
25
25
26 try:
26 # Tell nose to skip this module
27 from twisted.internet import defer
27 __test__ = {}
28 from twisted.application.service import IService
28
29
29 from twisted.internet import defer
30 from IPython.kernel import engineservice as es
30 from twisted.application.service import IService
31 from IPython.testing.util import DeferredTestCase
31
32 from IPython.kernel.tests.engineservicetest import \
32 from IPython.kernel import engineservice as es
33 IEngineCoreTestCase, \
33 from IPython.testing.util import DeferredTestCase
34 IEngineSerializedTestCase, \
34 from IPython.kernel.tests.engineservicetest import \
35 IEngineQueuedTestCase, \
35 IEngineCoreTestCase, \
36 IEnginePropertiesTestCase
36 IEngineSerializedTestCase, \
37 except ImportError:
37 IEngineQueuedTestCase, \
38 import nose
38 IEnginePropertiesTestCase
39 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
40
39
41
40
42 class BasicEngineServiceTest(DeferredTestCase,
41 class BasicEngineServiceTest(DeferredTestCase,
@@ -4,29 +4,28 b''
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 # Tell nose to skip this module
19 from twisted.internet import defer
19 __test__ = {}
20 from IPython.testing.util import DeferredTestCase
20
21 from IPython.kernel.controllerservice import ControllerService
21 from twisted.internet import defer
22 from IPython.kernel import multiengine as me
22 from IPython.testing.util import DeferredTestCase
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
23 from IPython.kernel.controllerservice import ControllerService
24 ISynchronousMultiEngineTestCase)
24 from IPython.kernel import multiengine as me
25 except ImportError:
25 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
26 import nose
26 ISynchronousMultiEngineTestCase)
27 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27
28
28
29
30 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
29 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
31
30
32 def setUp(self):
31 def setUp(self):
@@ -14,25 +14,25 b' __docformat__ = "restructuredtext en"'
14 # Imports
14 # Imports
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 try:
17 # Tell nose to skip this module
18 from twisted.internet import defer, reactor
18 __test__ = {}
19
20 from twisted.internet import defer, reactor
21
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
23
24 from IPython.testing.util import DeferredTestCase
25 from IPython.kernel.controllerservice import ControllerService
26 from IPython.kernel.multiengine import IMultiEngine
27 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 from IPython.kernel import multiengine as me
30 from IPython.kernel.clientconnector import ClientConnector
31 from IPython.kernel.parallelfunction import ParallelFunction
32 from IPython.kernel.error import CompositeError
33 from IPython.kernel.util import printer
19
34
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
21
35
22 from IPython.testing.util import DeferredTestCase
23 from IPython.kernel.controllerservice import ControllerService
24 from IPython.kernel.multiengine import IMultiEngine
25 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
27 from IPython.kernel import multiengine as me
28 from IPython.kernel.clientconnector import ClientConnector
29 from IPython.kernel.parallelfunction import ParallelFunction
30 from IPython.kernel.error import CompositeError
31 from IPython.kernel.util import printer
32 except ImportError:
33 import nose
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35
36 def _raise_it(f):
36 def _raise_it(f):
37 try:
37 try:
38 f.raiseException()
38 f.raiseException()
@@ -4,36 +4,36 b''
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 # Tell nose to skip this module
19 import zope.interface as zi
19 __test__ = {}
20 from twisted.trial import unittest
21 from IPython.testing.util import DeferredTestCase
22
20
23 from IPython.kernel.newserialized import \
21 import zope.interface as zi
24 ISerialized, \
22 from twisted.trial import unittest
25 IUnSerialized, \
23 from IPython.testing.util import DeferredTestCase
26 Serialized, \
27 UnSerialized, \
28 SerializeIt, \
29 UnSerializeIt
30 except ImportError:
31 import nose
32 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
33
24
34 #-------------------------------------------------------------------------------
25 from IPython.kernel.newserialized import \
26 ISerialized, \
27 IUnSerialized, \
28 Serialized, \
29 UnSerialized, \
30 SerializeIt, \
31 UnSerializeIt
32
33
34 #-----------------------------------------------------------------------------
35 # Tests
35 # Tests
36 #-------------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38 class SerializedTestCase(unittest.TestCase):
38 class SerializedTestCase(unittest.TestCase):
39
39
@@ -16,18 +16,18 b' __docformat__ = "restructuredtext en"'
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 try:
19 # Tell nose to skip this module
20 from twisted.internet import defer
20 __test__ = {}
21 from twisted.python import failure
21
22
22 from twisted.internet import defer
23 from IPython.testing.util import DeferredTestCase
23 from twisted.python import failure
24 import IPython.kernel.pendingdeferred as pd
24
25 from IPython.kernel import error
25 from IPython.testing.util import DeferredTestCase
26 from IPython.kernel.util import printer
26 import IPython.kernel.pendingdeferred as pd
27 except ImportError:
27 from IPython.kernel import error
28 import nose
28 from IPython.kernel.util import printer
29 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
29
30
30
31 class Foo(object):
31 class Foo(object):
32
32
33 def bar(self, bahz):
33 def bar(self, bahz):
@@ -15,19 +15,19 b' __docformat__ = "restructuredtext en"'
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 try:
18 # Tell nose to skip this module
19 import time
19 __test__ = {}
20
20
21 from twisted.internet import defer
21 import time
22 from twisted.trial import unittest
22
23
23 from twisted.internet import defer
24 from IPython.kernel import task, controllerservice as cs, engineservice as es
24 from twisted.trial import unittest
25 from IPython.kernel.multiengine import IMultiEngine
25
26 from IPython.testing.util import DeferredTestCase
26 from IPython.kernel import task, controllerservice as cs, engineservice as es
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
27 from IPython.kernel.multiengine import IMultiEngine
28 except ImportError:
28 from IPython.testing.util import DeferredTestCase
29 import nose
29 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
30 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
30
31
31
32 #-------------------------------------------------------------------------------
32 #-------------------------------------------------------------------------------
33 # Tests
33 # Tests
@@ -14,27 +14,26 b' __docformat__ = "restructuredtext en"'
14 # Imports
14 # Imports
15 #-------------------------------------------------------------------------------
15 #-------------------------------------------------------------------------------
16
16
17 try:
17 # Tell nose to skip this module
18 import time
18 __test__ = {}
19
19
20 from twisted.internet import defer, reactor
20 import time
21
21
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
22 from twisted.internet import defer, reactor
23
23
24 from IPython.kernel import task as taskmodule
24 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
25 from IPython.kernel import controllerservice as cs
25
26 import IPython.kernel.multiengine as me
26 from IPython.kernel import task as taskmodule
27 from IPython.testing.util import DeferredTestCase
27 from IPython.kernel import controllerservice as cs
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
28 import IPython.kernel.multiengine as me
29 from IPython.kernel.taskfc import IFCTaskController
29 from IPython.testing.util import DeferredTestCase
30 from IPython.kernel.util import printer
30 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
31 from IPython.kernel.taskfc import IFCTaskController
32 from IPython.kernel.clientconnector import ClientConnector
32 from IPython.kernel.util import printer
33 from IPython.kernel.error import CompositeError
33 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
34 from IPython.kernel.parallelfunction import ParallelFunction
34 from IPython.kernel.clientconnector import ClientConnector
35 except ImportError:
35 from IPython.kernel.error import CompositeError
36 import nose
36 from IPython.kernel.parallelfunction import ParallelFunction
37 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
38
37
39
38
40 #-------------------------------------------------------------------------------
39 #-------------------------------------------------------------------------------
@@ -16,12 +16,15 b' __docformat__ = "restructuredtext en"'
16 # Imports
16 # Imports
17 #-------------------------------------------------------------------------------
17 #-------------------------------------------------------------------------------
18
18
19 import os, sys
19 import threading, Queue, atexit
20 import threading, Queue, atexit
20 import twisted
21
21
22 import twisted
22 from twisted.internet import defer, reactor
23 from twisted.internet import defer, reactor
23 from twisted.python import log, failure
24 from twisted.python import log, failure
24
25
26 from IPython.kernel.error import FileTimeoutError
27
25 #-------------------------------------------------------------------------------
28 #-------------------------------------------------------------------------------
26 # Classes related to twisted and threads
29 # Classes related to twisted and threads
27 #-------------------------------------------------------------------------------
30 #-------------------------------------------------------------------------------
@@ -204,3 +207,43 b' class DeferredList(defer.Deferred):'
204 result = None
207 result = None
205
208
206 return result
209 return result
210
211
212 def wait_for_file(filename, delay=0.1, max_tries=10):
213 """Wait (poll) for a file to be created.
214
215 This method returns a Deferred that will fire when a file exists. It
216 works by polling os.path.isfile in time intervals specified by the
217 delay argument. If `max_tries` is reached, it will errback with a
218 `FileTimeoutError`.
219
220 Parameters
221 ----------
222 filename : str
223 The name of the file to wait for.
224 delay : float
225 The time to wait between polls.
226 max_tries : int
227 The max number of attempts before raising `FileTimeoutError`
228
229 Returns
230 -------
231 d : Deferred
232 A Deferred instance that will fire when the file exists.
233 """
234
235 d = defer.Deferred()
236
237 def _test_for_file(filename, attempt=0):
238 if attempt >= max_tries:
239 d.errback(FileTimeoutError(
240 'timeout waiting for file to be created: %s' % filename
241 ))
242 else:
243 if os.path.isfile(filename):
244 d.callback(True)
245 else:
246 reactor.callLater(delay, _test_for_file, filename, attempt+1)
247
248 _test_for_file(filename)
249 return d
@@ -61,6 +61,41 b' def set_term_title(title):'
61 _platutils.set_term_title(title)
61 _platutils.set_term_title(title)
62
62
63
63
64 class FindCmdError(Exception):
65 pass
66
67 def find_cmd(cmd):
68 """Find full path to executable cmd in a cross platform manner.
69
70 This function tries to determine the full path to a command line program
71 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
72 time it will use the version that is first on the users `PATH`. If
73 cmd is `python` return `sys.executable`.
74
75 Parameters
76 ----------
77 cmd : str
78 The command line program to look for.
79 """
80 if cmd == 'python':
81 return sys.executable
82 try:
83 path = _platutils.find_cmd(cmd)
84 except:
85 raise FindCmdError('command could not be found: %s' % cmd)
86 # which returns empty if not found
87 if path == '':
88 raise FindCmdError('command could not be found: %s' % cmd)
89 return path
90
91 def get_long_path_name(path):
92 """Expand a path into its long form.
93
94 On Windows this expands any ~ in the paths. On other platforms, it is
95 a null operation.
96 """
97 return _platutils.get_long_path_name(path)
98
64 #-----------------------------------------------------------------------------
99 #-----------------------------------------------------------------------------
65 # Deprecated functions
100 # Deprecated functions
66 #-----------------------------------------------------------------------------
101 #-----------------------------------------------------------------------------
@@ -23,3 +23,11 b' ignore_termtitle = True'
23 def set_term_title(*args,**kw):
23 def set_term_title(*args,**kw):
24 """Dummy no-op."""
24 """Dummy no-op."""
25 pass
25 pass
26
27 def find_cmd(cmd):
28 """Find the full path to a command using which."""
29 return os.popen('which %s' % cmd).read().strip()
30
31 def get_long_path_name(path):
32 """Dummy no-op."""
33 return path
@@ -30,3 +30,11 b" if os.environ.get('TERM','') == 'xterm':"
30 set_term_title = _set_term_title_xterm
30 set_term_title = _set_term_title_xterm
31 else:
31 else:
32 set_term_title = _dummy_op
32 set_term_title = _dummy_op
33
34 def find_cmd(cmd):
35 """Find the full path to a command using which."""
36 return os.popen('which %s' % cmd).read().strip()
37
38 def get_long_path_name(path):
39 """Dummy no-op."""
40 return path
@@ -41,3 +41,42 b' except ImportError:'
41 if ret:
41 if ret:
42 # non-zero return code signals error, don't try again
42 # non-zero return code signals error, don't try again
43 ignore_termtitle = True
43 ignore_termtitle = True
44
45 def find_cmd(cmd):
46 """Find the full path to a .bat or .exe using the win32api module."""
47 try:
48 import win32api
49 except ImportError:
50 raise ImportError('you need to have pywin32 installed for this to work')
51 else:
52 try:
53 (path, offest) = win32api.SearchPath(os.environ['PATH'],cmd + '.exe')
54 except:
55 (path, offset) = win32api.SearchPath(os.environ['PATH'],cmd + '.bat')
56 return path
57
58
59 def get_long_path_name(path):
60 """Get a long path name (expand ~) on Windows using ctypes.
61
62 Examples
63 --------
64
65 >>> get_long_path_name('c:\\docume~1')
66 u'c:\\\\Documents and Settings'
67
68 """
69 try:
70 import ctypes
71 except ImportError:
72 raise ImportError('you need to have ctypes installed for this to work')
73 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
74 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
75 ctypes.c_uint ]
76
77 buf = ctypes.create_unicode_buffer(260)
78 rv = _GetLongPathName(path, buf, 260)
79 if rv == 0 or rv > 260:
80 return path
81 else:
82 return buf.value
@@ -123,10 +123,10 b' def skipif(skip_condition, msg=None):'
123 Parameters
123 Parameters
124 ----------
124 ----------
125 skip_condition : bool or callable.
125 skip_condition : bool or callable.
126 Flag to determine whether to skip test. If the condition is a
126 Flag to determine whether to skip test. If the condition is a
127 callable, it is used at runtime to dynamically make the decision. This
127 callable, it is used at runtime to dynamically make the decision. This
128 is useful for tests that may require costly imports, to delay the cost
128 is useful for tests that may require costly imports, to delay the cost
129 until the test suite is actually executed.
129 until the test suite is actually executed.
130 msg : string
130 msg : string
131 Message to give on raising a SkipTest exception
131 Message to give on raising a SkipTest exception
132
132
@@ -29,13 +29,6 b' def setastest(tf=True):'
29 tf : bool
29 tf : bool
30 If True specifies this is a test, not a test otherwise
30 If True specifies this is a test, not a test otherwise
31
31
32 e.g
33 >>> from numpy.testing.decorators import setastest
34 >>> @setastest(False)
35 ... def func_with_test_in_name(arg1, arg2): pass
36 ...
37 >>>
38
39 This decorator cannot use the nose namespace, because it can be
32 This decorator cannot use the nose namespace, because it can be
40 called from a non-test module. See also istest and nottest in
33 called from a non-test module. See also istest and nottest in
41 nose.tools
34 nose.tools
@@ -1,54 +1,121 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
3
4 This module provides a main entry point to a user script to test IPython itself
4 This module provides a main entry point to a user script to test IPython
5 from the command line. The main() routine can be used in a similar manner to
5 itself from the command line. There are two ways of running this script:
6 the ``nosetests`` script, and it takes similar arguments, but if no arguments
6
7 are given it defaults to testing all of IPython. This should be preferred to
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 using plain ``nosetests`` because a number of nose plugins necessary to test
8 calling this script (with different arguments) or trial recursively. This
9 IPython correctly are automatically configured by this code.
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
14
15 For now, this script requires that both nose and twisted are installed. This
16 will change in the future.
10 """
17 """
11
18
12 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
13 # Module imports
20 # Module imports
14 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
15
22
16 # stdlib
23 import os
24 import os.path as path
17 import sys
25 import sys
26 import subprocess
27 import time
18 import warnings
28 import warnings
19
29
20 # third-party
21 import nose.plugins.builtin
30 import nose.plugins.builtin
22 from nose.core import TestProgram
31 from nose.core import TestProgram
23
32
24 # Our own imports
33 from IPython.platutils import find_cmd
25 from IPython.testing.plugin.ipdoctest import IPythonDoctest
34 from IPython.testing.plugin.ipdoctest import IPythonDoctest
26
35
36 pjoin = path.join
37
27 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
28 # Constants and globals
39 # Logic for skipping doctests
29 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
30
41
42 def test_for(mod):
43 """Test to see if mod is importable."""
44 try:
45 __import__(mod)
46 except ImportError:
47 return False
48 else:
49 return True
50
51 have_curses = test_for('_curses')
52 have_wx = test_for('wx')
53 have_zi = test_for('zope.interface')
54 have_twisted = test_for('twisted')
55 have_foolscap = test_for('foolscap')
56 have_objc = test_for('objc')
57 have_pexpect = test_for('pexpect')
58
31 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
59 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
32 # testing problems. We should strive to minimize the number of skipped
60 # testing problems. We should strive to minimize the number of skipped
33 # modules, since this means untested code. As the testing machinery
61 # modules, since this means untested code. As the testing machinery
34 # solidifies, this list should eventually become empty.
62 # solidifies, this list should eventually become empty.
35 EXCLUDE = ['IPython/external/',
63 EXCLUDE = [pjoin('IPython', 'external'),
36 'IPython/platutils_win32',
64 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
37 'IPython/frontend/cocoa',
65 pjoin('IPython_doctest_plugin'),
38 'IPython_doctest_plugin',
66 pjoin('IPython', 'Gnuplot'),
39 'IPython/Gnuplot',
67 pjoin('IPython', 'Extensions', 'ipy_'),
40 'IPython/Extensions/ipy_',
68 pjoin('IPython', 'Extensions', 'clearcmd'),
41 'IPython/Extensions/clearcmd',
69 pjoin('IPython', 'Extensions', 'PhysicalQInteractive'),
42 'IPython/Extensions/PhysicalQIn',
70 pjoin('IPython', 'Extensions', 'scitedirector'),
43 'IPython/Extensions/scitedirector',
71 pjoin('IPython', 'Extensions', 'numeric_formats'),
72 pjoin('IPython', 'testing', 'attic'),
73 pjoin('IPython', 'testing', 'tutils'),
74 pjoin('IPython', 'testing', 'tools'),
75 pjoin('IPython', 'testing', 'mkdoctests')
44 ]
76 ]
45
77
78 if not have_wx:
79 EXCLUDE.append(pjoin('IPython', 'Extensions', 'igrid'))
80 EXCLUDE.append(pjoin('IPython', 'gui'))
81 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
82
83 if not have_objc:
84 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
85
86 if not have_curses:
87 EXCLUDE.append(pjoin('IPython', 'Extensions', 'ibrowse'))
88
89 if not sys.platform == 'win32':
90 EXCLUDE.append(pjoin('IPython', 'platutils_win32'))
91
92 # These have to be skipped on win32 because the use echo, rm, cd, etc.
93 # See ticket https://bugs.launchpad.net/bugs/366982
94 if sys.platform == 'win32':
95 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
96 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
97
98 if not os.name == 'posix':
99 EXCLUDE.append(pjoin('IPython', 'platutils_posix'))
100
101 if not have_pexpect:
102 EXCLUDE.append(pjoin('IPython', 'irunner'))
103
104 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
105 if sys.platform == 'win32':
106 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
107
108
46 #-----------------------------------------------------------------------------
109 #-----------------------------------------------------------------------------
47 # Functions and classes
110 # Functions and classes
48 #-----------------------------------------------------------------------------
111 #-----------------------------------------------------------------------------
49
112
50 def main():
113 def run_iptest():
51 """Run the IPython test suite.
114 """Run the IPython test suite using nose.
115
116 This function is called when this script is **not** called with the form
117 `iptest all`. It simply calls nose with appropriate command line flags
118 and accepts all of the standard nose arguments.
52 """
119 """
53
120
54 warnings.filterwarnings('ignore',
121 warnings.filterwarnings('ignore',
@@ -60,7 +127,7 b' def main():'
60 # test suite back into working shape. Our nose
127 # test suite back into working shape. Our nose
61 # plugin needs to be gone through with a fine
128 # plugin needs to be gone through with a fine
62 # toothed comb to find what is causing the problem.
129 # toothed comb to find what is causing the problem.
63 # '--with-ipdoctest',
130 '--with-ipdoctest',
64 '--ipdoctest-tests','--ipdoctest-extension=txt',
131 '--ipdoctest-tests','--ipdoctest-extension=txt',
65 '--detailed-errors',
132 '--detailed-errors',
66
133
@@ -98,3 +165,136 b' def main():'
98 plugins.append(plug)
165 plugins.append(plug)
99
166
100 TestProgram(argv=argv,plugins=plugins)
167 TestProgram(argv=argv,plugins=plugins)
168
169
170 class IPTester(object):
171 """Call that calls iptest or trial in a subprocess.
172 """
173 def __init__(self,runner='iptest',params=None):
174 """ """
175 if runner == 'iptest':
176 self.runner = ['iptest','-v']
177 else:
178 self.runner = [find_cmd('trial')]
179 if params is None:
180 params = []
181 if isinstance(params,str):
182 params = [params]
183 self.params = params
184
185 # Assemble call
186 self.call_args = self.runner+self.params
187
188 def run(self):
189 """Run the stored commands"""
190 return subprocess.call(self.call_args)
191
192
193 def make_runners():
194 """Define the modules and packages that need to be tested.
195 """
196
197 # This omits additional top-level modules that should not be doctested.
198 # XXX: Shell.py is also ommited because of a bug in the skip_doctest
199 # decorator. See ticket https://bugs.launchpad.net/bugs/366209
200 top_mod = \
201 ['background_jobs.py', 'ColorANSI.py', 'completer.py', 'ConfigLoader.py',
202 'CrashHandler.py', 'Debugger.py', 'deep_reload.py', 'demo.py',
203 'DPyGetOpt.py', 'dtutils.py', 'excolors.py', 'FakeModule.py',
204 'generics.py', 'genutils.py', 'history.py', 'hooks.py', 'ipapi.py',
205 'iplib.py', 'ipmaker.py', 'ipstruct.py', 'Itpl.py',
206 'Logger.py', 'macro.py', 'Magic.py', 'OInspect.py',
207 'OutputTrap.py', 'platutils.py', 'prefilter.py', 'Prompts.py',
208 'PyColorize.py', 'Release.py', 'rlineimpl.py', 'shadowns.py',
209 'shellglobals.py', 'strdispatch.py', 'twshell.py',
210 'ultraTB.py', 'upgrade_dir.py', 'usage.py', 'wildcard.py',
211 # See note above for why this is skipped
212 # 'Shell.py',
213 'winconsole.py']
214
215 if have_pexpect:
216 top_mod.append('irunner.py')
217
218 if sys.platform == 'win32':
219 top_mod.append('platutils_win32.py')
220 elif os.name == 'posix':
221 top_mod.append('platutils_posix.py')
222 else:
223 top_mod.append('platutils_dummy.py')
224
225 # These are tested by nose, so skip IPython.kernel
226 top_pack = ['config','Extensions','frontend',
227 'testing','tests','tools','UserConfig']
228
229 if have_wx:
230 top_pack.append('gui')
231
232 modules = ['IPython.%s' % m[:-3] for m in top_mod ]
233 packages = ['IPython.%s' % m for m in top_pack ]
234
235 # Make runners
236 runners = dict(zip(top_pack, [IPTester(params=v) for v in packages]))
237
238 # Test IPython.kernel using trial if twisted is installed
239 if have_zi and have_twisted and have_foolscap:
240 runners['trial'] = IPTester('trial',['IPython'])
241
242 runners['modules'] = IPTester(params=modules)
243
244 return runners
245
246
247 def run_iptestall():
248 """Run the entire IPython test suite by calling nose and trial.
249
250 This function constructs :class:`IPTester` instances for all IPython
251 modules and package and then runs each of them. This causes the modules
252 and packages of IPython to be tested each in their own subprocess using
253 nose or twisted.trial appropriately.
254 """
255 runners = make_runners()
256 # Run all test runners, tracking execution time
257 failed = {}
258 t_start = time.time()
259 for name,runner in runners.iteritems():
260 print '*'*77
261 print 'IPython test set:',name
262 res = runner.run()
263 if res:
264 failed[name] = res
265 t_end = time.time()
266 t_tests = t_end - t_start
267 nrunners = len(runners)
268 nfail = len(failed)
269 # summarize results
270 print
271 print '*'*77
272 print 'Ran %s test sets in %.3fs' % (nrunners, t_tests)
273 print
274 if not failed:
275 print 'OK'
276 else:
277 # If anything went wrong, point out what command to rerun manually to
278 # see the actual errors and individual summary
279 print 'ERROR - %s out of %s test sets failed.' % (nfail, nrunners)
280 for name in failed:
281 failed_runner = runners[name]
282 print '-'*40
283 print 'Runner failed:',name
284 print 'You may wish to rerun this one individually, with:'
285 print ' '.join(failed_runner.call_args)
286 print
287
288
289 def main():
290 if len(sys.argv) == 1:
291 run_iptestall()
292 else:
293 if sys.argv[1] == 'all':
294 run_iptestall()
295 else:
296 run_iptest()
297
298
299 if __name__ == '__main__':
300 main() No newline at end of file
@@ -59,6 +59,19 b' log = logging.getLogger(__name__)'
59 # machinery into a fit. This code should be considered a gross hack, but it
59 # machinery into a fit. This code should be considered a gross hack, but it
60 # gets the job done.
60 # gets the job done.
61
61
62 def default_argv():
63 """Return a valid default argv for creating testing instances of ipython"""
64
65 # Get the install directory for the user configuration and tell ipython to
66 # use the default profile from there.
67 from IPython import UserConfig
68 ipcdir = os.path.dirname(UserConfig.__file__)
69 #ipconf = os.path.join(ipcdir,'ipy_user_conf.py')
70 ipconf = os.path.join(ipcdir,'ipythonrc')
71 #print 'conf:',ipconf # dbg
72
73 return ['--colors=NoColor','--noterm_title','-rcfile=%s' % ipconf]
74
62
75
63 # Hack to modify the %run command so we can sync the user's namespace with the
76 # Hack to modify the %run command so we can sync the user's namespace with the
64 # test globals. Once we move over to a clean magic system, this will be done
77 # test globals. Once we move over to a clean magic system, this will be done
@@ -167,10 +180,11 b' def start_ipython():'
167 _excepthook = sys.excepthook
180 _excepthook = sys.excepthook
168 _main = sys.modules.get('__main__')
181 _main = sys.modules.get('__main__')
169
182
183 argv = default_argv()
184
170 # Start IPython instance. We customize it to start with minimal frills.
185 # Start IPython instance. We customize it to start with minimal frills.
171 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
186 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
172 IPython.Shell.IPShell(['--colors=NoColor','--noterm_title'],
187 IPython.Shell.IPShell(argv,user_ns,global_ns)
173 user_ns,global_ns)
174
188
175 # Deactivate the various python system hooks added by ipython for
189 # Deactivate the various python system hooks added by ipython for
176 # interactive convenience so we don't confuse the doctest system
190 # interactive convenience so we don't confuse the doctest system
@@ -826,11 +840,11 b' class ExtensionDoctest(doctests.Doctest):'
826 Modified version that accepts extension modules as valid containers for
840 Modified version that accepts extension modules as valid containers for
827 doctests.
841 doctests.
828 """
842 """
829 #print '*** ipdoctest- wantFile:',filename # dbg
843 # print '*** ipdoctest- wantFile:',filename # dbg
830
844
831 for pat in self.exclude_patterns:
845 for pat in self.exclude_patterns:
832 if pat.search(filename):
846 if pat.search(filename):
833 #print '###>>> SKIP:',filename # dbg
847 # print '###>>> SKIP:',filename # dbg
834 return False
848 return False
835
849
836 if is_extension_module(filename):
850 if is_extension_module(filename):
@@ -46,7 +46,7 b' def test_deliberately_broken():'
46 """A deliberately broken test - we want to skip this one."""
46 """A deliberately broken test - we want to skip this one."""
47 1/0
47 1/0
48
48
49 @dec.skip('foo')
49 @dec.skip('Testing the skip decorator')
50 def test_deliberately_broken2():
50 def test_deliberately_broken2():
51 """Another deliberately broken test - we want to skip this one."""
51 """Another deliberately broken test - we want to skip this one."""
52 1/0
52 1/0
@@ -26,15 +26,13 b' Authors'
26 # Required modules and packages
26 # Required modules and packages
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 # Standard Python lib
30 import os
29 import os
31 import sys
30 import sys
32
31
33 # Third-party
34 import nose.tools as nt
32 import nose.tools as nt
35
33
36 # From this project
37 from IPython.tools import utils
34 from IPython.tools import utils
35 from IPython.testing import decorators as dec
38
36
39 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
40 # Globals
38 # Globals
@@ -55,6 +53,7 b" for _x in [a for a in dir(nt) if a.startswith('assert')]:"
55 # Functions and classes
53 # Functions and classes
56 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
57
55
56
58 def full_path(startPath,files):
57 def full_path(startPath,files):
59 """Make full paths for all the listed files, based on startPath.
58 """Make full paths for all the listed files, based on startPath.
60
59
@@ -62,7 +61,8 b' def full_path(startPath,files):'
62 used with a script's __file__ variable as startPath. The base of startPath
61 used with a script's __file__ variable as startPath. The base of startPath
63 is then prepended to all the listed files, forming the output list.
62 is then prepended to all the listed files, forming the output list.
64
63
65 :Parameters:
64 Parameters
65 ----------
66 startPath : string
66 startPath : string
67 Initial path to use as the base for the results. This path is split
67 Initial path to use as the base for the results. This path is split
68 using os.path.split() and only its first component is kept.
68 using os.path.split() and only its first component is kept.
@@ -70,7 +70,8 b' def full_path(startPath,files):'
70 files : string or list
70 files : string or list
71 One or more files.
71 One or more files.
72
72
73 :Examples:
73 Examples
74 --------
74
75
75 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
76 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
76 ['/foo/a.txt', '/foo/b.txt']
77 ['/foo/a.txt', '/foo/b.txt']
@@ -210,7 +210,6 b' def test_get_home_dir_7():'
210 home_dir = genutils.get_home_dir()
210 home_dir = genutils.get_home_dir()
211 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
211 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
212
212
213
214 #
213 #
215 # Tests for get_ipython_dir
214 # Tests for get_ipython_dir
216 #
215 #
@@ -239,7 +238,6 b' def test_get_ipython_dir_3():'
239 ipdir = genutils.get_ipython_dir()
238 ipdir = genutils.get_ipython_dir()
240 nt.assert_equal(ipdir, os.path.abspath(os.path.join("someplace", "_ipython")))
239 nt.assert_equal(ipdir, os.path.abspath(os.path.join("someplace", "_ipython")))
241
240
242
243 #
241 #
244 # Tests for get_security_dir
242 # Tests for get_security_dir
245 #
243 #
@@ -249,6 +247,14 b' def test_get_security_dir():'
249 """Testcase to see if we can call get_security_dir without Exceptions."""
247 """Testcase to see if we can call get_security_dir without Exceptions."""
250 sdir = genutils.get_security_dir()
248 sdir = genutils.get_security_dir()
251
249
250 #
251 # Tests for get_log_dir
252 #
253
254 @with_enivronment
255 def test_get_log_dir():
256 """Testcase to see if we can call get_log_dir without Exceptions."""
257 sdir = genutils.get_log_dir()
252
258
253 #
259 #
254 # Tests for popkey
260 # Tests for popkey
@@ -289,3 +295,12 b' def test_popkey_3():'
289 nt.assert_equal(dct, dict(c=3))
295 nt.assert_equal(dct, dict(c=3))
290 nt.assert_equal(genutils.popkey(dct, "c"), 3)
296 nt.assert_equal(genutils.popkey(dct, "c"), 3)
291 nt.assert_equal(dct, dict())
297 nt.assert_equal(dct, dict())
298
299
300 def test_filefind():
301 """Various tests for filefind"""
302 f = tempfile.NamedTemporaryFile()
303 print 'fname:',f.name
304 alt_dirs = genutils.get_ipython_dir()
305 t = genutils.filefind(f.name,alt_dirs)
306 print 'found:',t
@@ -13,7 +13,7 b' import tempfile'
13 import nose.tools as nt
13 import nose.tools as nt
14
14
15 # our own packages
15 # our own packages
16 from IPython import iplib
16 from IPython import ipapi, iplib
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Globals
19 # Globals
@@ -28,7 +28,15 b' from IPython import iplib'
28 # ipapi instance should be read from there, but we also will often need to use
28 # ipapi instance should be read from there, but we also will often need to use
29 # the actual IPython one.
29 # the actual IPython one.
30
30
31 ip = _ip # This is the ipapi instance
31 # Get the public instance of IPython, and if it's None, make one so we can use
32 # it for testing
33 ip = ipapi.get()
34 if ip is None:
35 # IPython not running yet, make one from the testing machinery for
36 # consistency when the test suite is being run via iptest
37 from IPython.testing.plugin import ipdoctest
38 ip = ipapi.get()
39
32 IP = ip.IP # This is the actual IPython shell 'raw' object.
40 IP = ip.IP # This is the actual IPython shell 'raw' object.
33
41
34 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
@@ -60,9 +68,13 b' def test_user_setup():'
60 # Now repeat the operation with a non-existent directory. Check both that
68 # Now repeat the operation with a non-existent directory. Check both that
61 # the call succeeds and that the directory is created.
69 # the call succeeds and that the directory is created.
62 tmpdir = tempfile.mktemp(prefix='ipython-test-')
70 tmpdir = tempfile.mktemp(prefix='ipython-test-')
71 # Use a try with an empty except because try/finally doesn't work with a
72 # yield in Python 2.4.
63 try:
73 try:
64 yield user_setup, (tmpdir,''), kw
74 yield user_setup, (tmpdir,''), kw
65 yield os.path.isdir, tmpdir
75 yield os.path.isdir, tmpdir
66 finally:
76 except:
67 # In this case, clean up the temp dir once done
77 pass
68 shutil.rmtree(tmpdir)
78 # Clean up the temp dir once done
79 shutil.rmtree(tmpdir)
80 No newline at end of file
@@ -3,16 +3,14 b''
3 Needs to be run by nose (to make ipython session available).
3 Needs to be run by nose (to make ipython session available).
4 """
4 """
5
5
6 # Standard library imports
7 import os
6 import os
8 import sys
7 import sys
9 import tempfile
8 import tempfile
10 import types
9 import types
11
10
12 # Third-party imports
13 import nose.tools as nt
11 import nose.tools as nt
14
12
15 # From our own code
13 from IPython.platutils import find_cmd, get_long_path_name
16 from IPython.testing import decorators as dec
14 from IPython.testing import decorators as dec
17 from IPython.testing import tools as tt
15 from IPython.testing import tools as tt
18
16
@@ -60,12 +58,14 b' def doctest_hist_r():'
60 hist -n -r 2 # random
58 hist -n -r 2 # random
61 """
59 """
62
60
63
61 # This test is known to fail on win32.
62 # See ticket https://bugs.launchpad.net/bugs/366334
64 def test_obj_del():
63 def test_obj_del():
65 """Test that object's __del__ methods are called on exit."""
64 """Test that object's __del__ methods are called on exit."""
66 test_dir = os.path.dirname(__file__)
65 test_dir = os.path.dirname(__file__)
67 del_file = os.path.join(test_dir,'obj_del.py')
66 del_file = os.path.join(test_dir,'obj_del.py')
68 out = _ip.IP.getoutput('ipython %s' % del_file)
67 ipython_cmd = find_cmd('ipython')
68 out = _ip.IP.getoutput('%s %s' % (ipython_cmd, del_file))
69 nt.assert_equals(out,'obj_del.py: object A deleted')
69 nt.assert_equals(out,'obj_del.py: object A deleted')
70
70
71
71
@@ -159,6 +159,7 b' def doctest_run_ns2():'
159 tclass.py: deleting object: C-first_pass
159 tclass.py: deleting object: C-first_pass
160 """
160 """
161
161
162 @dec.skip_win32
162 def doctest_run_builtins():
163 def doctest_run_builtins():
163 """Check that %run doesn't damage __builtins__ via a doctest.
164 """Check that %run doesn't damage __builtins__ via a doctest.
164
165
@@ -204,8 +205,17 b' class TestMagicRun(object):'
204 self.tmpfile = f
205 self.tmpfile = f
205
206
206 def run_tmpfile(self):
207 def run_tmpfile(self):
208 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
209 # See below and ticket https://bugs.launchpad.net/bugs/366353
207 _ip.magic('run %s' % self.tmpfile.name)
210 _ip.magic('run %s' % self.tmpfile.name)
208
211
212 # See https://bugs.launchpad.net/bugs/366353
213 @dec.skip_if_not_win32
214 def test_run_tempfile_path(self):
215 tt.assert_equals(True,False,"%run doesn't work with tempfile paths on win32.")
216
217 # See https://bugs.launchpad.net/bugs/366353
218 @dec.skip_win32
209 def test_builtins_id(self):
219 def test_builtins_id(self):
210 """Check that %run doesn't damage __builtins__ """
220 """Check that %run doesn't damage __builtins__ """
211
221
@@ -215,6 +225,8 b' class TestMagicRun(object):'
215 bid2 = id(_ip.user_ns['__builtins__'])
225 bid2 = id(_ip.user_ns['__builtins__'])
216 tt.assert_equals(bid1, bid2)
226 tt.assert_equals(bid1, bid2)
217
227
228 # See https://bugs.launchpad.net/bugs/366353
229 @dec.skip_win32
218 def test_builtins_type(self):
230 def test_builtins_type(self):
219 """Check that the type of __builtins__ doesn't change with %run.
231 """Check that the type of __builtins__ doesn't change with %run.
220
232
@@ -225,6 +237,8 b' class TestMagicRun(object):'
225 self.run_tmpfile()
237 self.run_tmpfile()
226 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
238 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
227
239
240 # See https://bugs.launchpad.net/bugs/366353
241 @dec.skip_win32
228 def test_prompts(self):
242 def test_prompts(self):
229 """Test that prompts correctly generate after %run"""
243 """Test that prompts correctly generate after %run"""
230 self.run_tmpfile()
244 self.run_tmpfile()
@@ -25,6 +25,7 b" if __name__ == '__main__':"
25 r'\.cocoa',
25 r'\.cocoa',
26 r'\.ipdoctest',
26 r'\.ipdoctest',
27 r'\.Gnuplot',
27 r'\.Gnuplot',
28 r'\.frontend.process.winprocess',
28 ]
29 ]
29 docwriter.write_api_docs(outdir)
30 docwriter.write_api_docs(outdir)
30 docwriter.write_index(outdir, 'gen',
31 docwriter.write_index(outdir, 'gen',
@@ -302,18 +302,33 b' The ``--furl-file`` flag works like this::'
302 Make FURL files persistent
302 Make FURL files persistent
303 ---------------------------
303 ---------------------------
304
304
305 At fist glance it may seem that that managing the FURL files is a bit annoying. Going back to the house and key analogy, copying the FURL around each time you start the controller is like having to make a new key every time you want to unlock the door and enter your house. As with your house, you want to be able to create the key (or FURL file) once, and then simply use it at any point in the future.
305 At fist glance it may seem that that managing the FURL files is a bit
306 annoying. Going back to the house and key analogy, copying the FURL around
307 each time you start the controller is like having to make a new key every time
308 you want to unlock the door and enter your house. As with your house, you want
309 to be able to create the key (or FURL file) once, and then simply use it at
310 any point in the future.
306
311
307 This is possible. The only thing you have to do is decide what ports the controller will listen on for the engines and clients. This is done as follows::
312 This is possible, but before you do this, you **must** remove any old FURL
313 files in the :file:`~/.ipython/security` directory.
314
315 .. warning::
316
317 You **must** remove old FURL files before using persistent FURL files.
318
319 Then, The only thing you have to do is decide what ports the controller will
320 listen on for the engines and clients. This is done as follows::
308
321
309 $ ipcontroller -r --client-port=10101 --engine-port=10102
322 $ ipcontroller -r --client-port=10101 --engine-port=10102
310
323
311 These options also work with all of the various modes of
324 These options also work with all of the various modes of
312 :command:`ipcluster`::
325 :command:`ipcluster`::
313
326
314 $ ipcluster local -n 2 -r --client-port=10101 --engine-port=10102
327 $ ipcluster local -n 2 -r --client-port=10101 --engine-port=10102
315
328
316 Then, just copy the furl files over the first time and you are set. You can start and stop the controller and engines any many times as you want in the future, just make sure to tell the controller to use the *same* ports.
329 Then, just copy the furl files over the first time and you are set. You can
330 start and stop the controller and engines any many times as you want in the
331 future, just make sure to tell the controller to use the *same* ports.
317
332
318 .. note::
333 .. note::
319
334
@@ -2,6 +2,10 b''
2 IPython/Vision Beam Pattern Demo
2 IPython/Vision Beam Pattern Demo
3 ==================================
3 ==================================
4
4
5 .. note::
6
7 This page has not been updated to reflect the recent work on ipcluster.
8 This work makes it much easier to use IPython on a cluster.
5
9
6 Installing and testing IPython at OSC systems
10 Installing and testing IPython at OSC systems
7 =============================================
11 =============================================
@@ -109,7 +109,7 b' def find_packages():'
109 add_package(packages, 'gui')
109 add_package(packages, 'gui')
110 add_package(packages, 'gui.wx')
110 add_package(packages, 'gui.wx')
111 add_package(packages, 'frontend', tests=True)
111 add_package(packages, 'frontend', tests=True)
112 add_package(packages, 'frontend._process')
112 add_package(packages, 'frontend.process')
113 add_package(packages, 'frontend.wx')
113 add_package(packages, 'frontend.wx')
114 add_package(packages, 'frontend.cocoa', tests=True)
114 add_package(packages, 'frontend.cocoa', tests=True)
115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
General Comments 0
You need to be logged in to leave comments. Login now