##// 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 66 return res
67 67
68 68 def main():
69 import readline
69 import IPython.rlineimpl as readline
70 70 readline.set_completer_delims(" \n\t")
71 71 # monkeypatch - the code will be folded to normal completer later on
72 72 import IPython.completer
@@ -99,16 +99,14 b' def main():'
99 99 #import readline
100 100 #readline.parse_and_bind('set completion-query-items 1000')
101 101 #readline.parse_and_bind('set page-completions no')
102
103
104
105
102
103
106 104 # some config helper functions you can use
107 105 def import_all(modules):
108 106 """ Usage: import_all("os sys") """
109 107 for m in modules.split():
110 108 ip.ex("from %s import *" % m)
111
109
112 110 def execf(fname):
113 111 """ Execute a file in user namespace """
114 112 ip.ex('execfile("%s")' % os.path.expanduser(fname))
@@ -14,12 +14,13 b' __docformat__ = "restructuredtext en"'
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 from IPython.external import guid
18 17
18 from IPython.external import guid
19 19
20 20 from zope.interface import Interface, Attribute, implements, classProvides
21 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 24 from IPython.kernel.core.history import FrontEndHistory
24 25 from IPython.kernel.engineservice import IEngineCore
25 26
@@ -15,30 +15,38 b' __docformat__ = "restructuredtext en"'
15 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 27 try:
19 from IPython.kernel.core.interpreter import Interpreter
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
28 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
24 29 from Foundation import NSMakeRect
25 from AppKit import NSTextView, NSScrollView
30 from AppKit import NSTextView, NSScrollView
26 31 except ImportError:
27 import nose
28 raise nose.SkipTest("This test requires zope.interface, Twisted, Foolscap and PyObjC")
32 # This tells twisted.trial to skip this module if PyObjC is not found
33 skip = True
29 34
30 class TestIPythonCocoaControler(DeferredTestCase):
35 #---------------------------------------------------------------------------
36 # Tests
37 #---------------------------------------------------------------------------
38 class TestIPythonCocoaControler(unittest.TestCase):
31 39 """Tests for IPythonCocoaController"""
32
40
33 41 def setUp(self):
34 42 self.controller = IPythonCocoaController.alloc().init()
35 43 self.engine = es.EngineService()
36 44 self.engine.startService()
37
45
38 46 def tearDown(self):
39 47 self.controller = None
40 48 self.engine.stopService()
41
49
42 50 def testControllerExecutesCode(self):
43 51 code ="""5+5"""
44 52 expected = Interpreter().execute(code)
@@ -47,48 +55,46 b' class TestIPythonCocoaControler(DeferredTestCase):'
47 55 del result['number']
48 56 del result['id']
49 57 return result
50 self.assertDeferredEquals(
51 self.controller.execute(code).addCallback(removeNumberAndID),
52 expected)
53
58 d = self.controller.execute(code)
59 d.addCallback(removeNumberAndID)
60 d.addCallback(lambda r: self.assertEquals(r, expected))
61
54 62 def testControllerMirrorsUserNSWithValuesAsStrings(self):
55 63 code = """userns1=1;userns2=2"""
56 64 def testControllerUserNS(result):
57 65 self.assertEquals(self.controller.userNS['userns1'], 1)
58 66 self.assertEquals(self.controller.userNS['userns2'], 2)
59
60 67 self.controller.execute(code).addCallback(testControllerUserNS)
61
62
68
63 69 def testControllerInstantiatesIEngine(self):
64 70 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
65
71
66 72 def testControllerCompletesToken(self):
67 73 code = """longNameVariable=10"""
68 74 def testCompletes(result):
69 75 self.assert_("longNameVariable" in result)
70
76
71 77 def testCompleteToken(result):
72 78 self.controller.complete("longNa").addCallback(testCompletes)
73
79
74 80 self.controller.execute(code).addCallback(testCompletes)
75
76
81
82
77 83 def testCurrentIndent(self):
78 84 """test that current_indent_string returns current indent or None.
79 85 Uses _indent_for_block for direct unit testing.
80 86 """
81
87
82 88 self.controller.tabUsesSpaces = True
83 89 self.assert_(self.controller._indent_for_block("""a=3""") == None)
84 90 self.assert_(self.controller._indent_for_block("") == None)
85 91 block = """def test():\n a=3"""
86 92 self.assert_(self.controller._indent_for_block(block) == \
87 93 ' ' * self.controller.tabSpaces)
88
94
89 95 block = """if(True):\n%sif(False):\n%spass""" % \
90 96 (' '*self.controller.tabSpaces,
91 97 2*' '*self.controller.tabSpaces)
92 98 self.assert_(self.controller._indent_for_block(block) == \
93 99 2*(' '*self.controller.tabSpaces))
94
100
@@ -18,10 +18,8 b' __docformat__ = "restructuredtext en"'
18 18 #-------------------------------------------------------------------------------
19 19 import re
20 20
21 import IPython
22 21 import sys
23 22 import codeop
24 import traceback
25 23
26 24 from frontendbase import FrontEndBase
27 25 from IPython.kernel.core.interpreter import Interpreter
@@ -58,6 +56,9 b' class LineFrontEndBase(FrontEndBase):'
58 56 # programatic control of the frontend.
59 57 last_result = dict(number=0)
60 58
59 # The last prompt displayed. Useful for continuation prompts.
60 last_prompt = ''
61
61 62 # The input buffer being edited
62 63 input_buffer = ''
63 64
@@ -151,8 +152,12 b' class LineFrontEndBase(FrontEndBase):'
151 152 self.capture_output()
152 153 try:
153 154 # Add line returns here, to make sure that the statement is
154 # complete.
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
155 # complete (except if '\' was used).
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 161 "<string>", "exec")
157 162 self.release_output()
158 163 except Exception, e:
@@ -183,16 +188,6 b' class LineFrontEndBase(FrontEndBase):'
183 188 # Create a false result, in case there is an exception
184 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 191 try:
197 192 try:
198 193 self.history.input_cache[-1] = raw_string.rstrip()
@@ -272,15 +267,15 b' class LineFrontEndBase(FrontEndBase):'
272 267 symbols_per_line = max(1, chars_per_line/max_len)
273 268
274 269 pos = 1
275 buf = []
270 completion_string = []
276 271 for symbol in possibilities:
277 272 if pos < symbols_per_line:
278 buf.append(symbol.ljust(max_len))
273 completion_string.append(symbol.ljust(max_len))
279 274 pos += 1
280 275 else:
281 buf.append(symbol.rstrip() + '\n')
276 completion_string.append(symbol.rstrip() + '\n')
282 277 pos = 1
283 self.write(''.join(buf))
278 self.write(''.join(completion_string))
284 279 self.new_prompt(self.input_prompt_template.substitute(
285 280 number=self.last_result['number'] + 1))
286 281 self.input_buffer = new_line
@@ -297,26 +292,70 b' class LineFrontEndBase(FrontEndBase):'
297 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 308 # Private API
302 309 #--------------------------------------------------------------------------
303 310
304 def _on_enter(self):
311 def _on_enter(self, new_line_pos=0):
305 312 """ Called when the return key is pressed in a line editing
306 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 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 333 if self.is_complete(cleaned_buffer):
311 334 self.execute(cleaned_buffer, raw_string=current_buffer)
335 return True
312 336 else:
313 self.input_buffer += self._get_indent_string(
314 current_buffer[:-1])
315 if len(current_buffer.split('\n')) == 2:
316 self.input_buffer += '\t\t'
317 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
318 self.input_buffer += '\t'
319
337 # Start a new line.
338 new_line_pos = -new_line_pos
339 lines = current_buffer.split('\n')[:-1]
340 prompt_less_lines = prompt_less_buffer.split('\n')
341 # Create the new line, with the continuation prompt, and the
342 # same amount of indent than the line above it.
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 360 def _get_indent_string(self, string):
322 361 """ Return the string of whitespace that prefixes a line. Used to
@@ -22,9 +22,10 b' __docformat__ = "restructuredtext en"'
22 22 # Imports
23 23 #-------------------------------------------------------------------------------
24 24 import sys
25
26 from linefrontendbase import LineFrontEndBase, common_prefix
27 from frontendbase import FrontEndBase
25 import pydoc
26 import os
27 import re
28 import __builtin__
28 29
29 30 from IPython.ipmaker import make_IPython
30 31 from IPython.ipapi import IPApi
@@ -33,9 +34,8 b' from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap'
33 34 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34 35
35 36 from IPython.genutils import Term
36 import pydoc
37 import os
38 import sys
37
38 from linefrontendbase import LineFrontEndBase, common_prefix
39 39
40 40
41 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 46 def my_system_call(args):
47 47 system_call_function("%s %s" % (command, args))
48
49 my_system_call.__doc__ = "Calls %s" % command
48 50 return my_system_call
49 51
50 52 #-------------------------------------------------------------------------------
@@ -62,13 +64,25 b' class PrefilterFrontEnd(LineFrontEndBase):'
62 64
63 65 debug = False
64 66
65 def __init__(self, ipython0=None, *args, **kwargs):
67 def __init__(self, ipython0=None, argv=None, *args, **kwargs):
66 68 """ Parameters:
67 69 -----------
68 70
69 71 ipython0: an optional ipython0 instance to use for command
70 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 86 LineFrontEndBase.__init__(self, *args, **kwargs)
73 87 self.shell.output_trap = RedirectorOutputTrap(
74 88 out_callback=self.write,
@@ -83,10 +97,16 b' class PrefilterFrontEnd(LineFrontEndBase):'
83 97 if ipython0 is None:
84 98 # Instanciate an IPython0 interpreter to be able to use the
85 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 105 # XXX: argv=[] is a bit bold.
87 ipython0 = make_IPython(argv=[],
106 ipython0 = make_IPython(argv=argv,
88 107 user_ns=self.shell.user_ns,
89 108 user_global_ns=self.shell.user_global_ns)
109 __builtin__.raw_input = old_rawinput
90 110 self.ipython0 = ipython0
91 111 # Set the pager:
92 112 self.ipython0.set_hook('show_in_pager',
@@ -98,16 +118,17 b' class PrefilterFrontEnd(LineFrontEndBase):'
98 118 self._ip.system = self.system_call
99 119 # XXX: Muck around with magics so that they work better
100 120 # in our environment
101 self.ipython0.magic_ls = mk_system_call(self.system_call,
102 'ls -CF')
121 if not sys.platform.startswith('win'):
122 self.ipython0.magic_ls = mk_system_call(self.system_call,
123 'ls -CF')
103 124 # And now clean up the mess created by ipython0
104 125 self.release_output()
105 126
106 127
107 128 if not 'banner' in kwargs and self.banner is None:
108 self.banner = self.ipython0.BANNER + """
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
129 self.banner = self.ipython0.BANNER
110 130
131 # FIXME: __init__ and start should be two different steps
111 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 138 def show_traceback(self):
118 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 145 self.ipython0.showtraceback(tb_offset=-1)
122 146 self.release_output()
123 147
@@ -171,7 +195,7 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
171 195 def complete(self, line):
172 196 # FIXME: This should be factored out in the linefrontendbase
173 197 # method.
174 word = line.split('\n')[-1].split(' ')[-1]
198 word = self._get_completion_text(line)
175 199 completions = self.ipython0.complete(word)
176 200 # FIXME: The proper sort should be done in the complete method.
177 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 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
@@ -151,7 +151,12 b' else:'
151 151 self._thread = ht
152 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 160 winprocess.ResumeThread(ht)
156 161
157 162 if p2cread is not None:
1 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
@@ -1,6 +1,6 b''
1 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 5 __docformat__ = "restructuredtext en"
6 6
@@ -15,17 +15,15 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #---------------------------------------------------------------------------
17 17
18 import unittest
18 # Tell nose to skip this module
19 __test__ = {}
19 20
20 try:
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
24 except ImportError:
25 import nose
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
21 from twisted.trial import unittest
22 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
23 from IPython.frontend import frontendbase
24 from IPython.kernel.engineservice import EngineService
25 from IPython.testing.parametric import Parametric, parametric
27 26
28 from IPython.testing.decorators import skip
29 27
30 28 class FrontEndCallbackChecker(AsyncFrontEndBase):
31 29 """FrontEndBase subclass for checking callbacks"""
@@ -44,14 +42,11 b' class FrontEndCallbackChecker(AsyncFrontEndBase):'
44 42 self.renderResultCalled = True
45 43 return result
46 44
47
48 45 def render_error(self, failure):
49 46 self.renderErrorCalled = True
50 47 return failure
51
52 48
53 49
54
55 50 class TestAsyncFrontendBase(unittest.TestCase):
56 51 def setUp(self):
57 52 """Setup the EngineService and FrontEndBase"""
@@ -59,97 +54,56 b' class TestAsyncFrontendBase(unittest.TestCase):'
59 54 self.fb = FrontEndCallbackChecker(engine=EngineService())
60 55
61 56 def test_implements_IFrontEnd(self):
62 assert(frontendbase.IFrontEnd.implementedBy(
57 self.assert_(frontendbase.IFrontEnd.implementedBy(
63 58 AsyncFrontEndBase))
64 59
65 60 def test_is_complete_returns_False_for_incomplete_block(self):
66 """"""
67
68 61 block = """def test(a):"""
69
70 assert(self.fb.is_complete(block) == False)
62 self.assert_(self.fb.is_complete(block) == False)
71 63
72 64 def test_is_complete_returns_True_for_complete_block(self):
73 """"""
74
75 65 block = """def test(a): pass"""
76
77 assert(self.fb.is_complete(block))
78
66 self.assert_(self.fb.is_complete(block))
79 67 block = """a=3"""
80
81 assert(self.fb.is_complete(block))
68 self.assert_(self.fb.is_complete(block))
82 69
83 70 def test_blockID_added_to_result(self):
84 71 block = """3+3"""
85
86 72 d = self.fb.execute(block, blockID='TEST_ID')
87
88 d.addCallback(self.checkBlockID, expected='TEST_ID')
73 d.addCallback(lambda r: self.assert_(r['blockID']=='TEST_ID'))
74 return d
89 75
90 76 def test_blockID_added_to_failure(self):
91 77 block = "raise Exception()"
92
93 78 d = self.fb.execute(block,blockID='TEST_ID')
94 d.addErrback(self.checkFailureID, expected='TEST_ID')
95
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
79 d.addErrback(lambda f: self.assert_(f.blockID=='TEST_ID'))
80 return d
103 81
104 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 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 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 90 d = self.fb.execute("raise Exception()")
124 d.addCallback(self.checkRenderError)
125
126 def checkRenderError(self, result):
127 assert(self.fb.renderErrorCalled)
91 d.addErrback(lambda f: self.assert_(self.fb.renderErrorCalled))
92 return d
128 93
129 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 97 blocks = ["a=1","a=2","a=3"]
133 for b in blocks:
134 d = self.fb.execute(b)
135
136 # d is now the deferred for the last executed block
137 d.addCallback(self.historyTests, blocks)
138
139
140 def historyTests(self, result, blocks):
141 """historyTests"""
142
143 assert(len(blocks) >= 3)
144 assert(self.fb.get_history_previous("") == blocks[-2])
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
98 d = self.fb.execute(blocks[0])
99 d.addCallback(lambda _: self.fb.execute(blocks[1]))
100 d.addCallback(lambda _: self.fb.execute(blocks[2]))
101 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-2]))
102 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-3]))
103 d.addCallback(lambda _: self.assert_(self.fb.get_history_next()==blocks[-2]))
104 return d
105
106 def test_history_returns_none_at_startup(self):
107 self.assert_(self.fb.get_history_previous("")==None)
108 self.assert_(self.fb.get_history_next()==None)
109
@@ -12,12 +12,32 b' __docformat__ = "restructuredtext en"'
12 12 # in the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 from copy import copy, deepcopy
15 16 from cStringIO import StringIO
16 17 import string
17 18
18 from IPython.ipapi import get as get_ipython0
19 from nose.tools import assert_equal
20
19 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 42 class TestPrefilterFrontEnd(PrefilterFrontEnd):
23 43
@@ -26,16 +46,8 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
26 46 banner = ''
27 47
28 48 def __init__(self):
29 ipython0 = get_ipython0().IP
30 49 self.out = StringIO()
31 PrefilterFrontEnd.__init__(self, ipython0=ipython0)
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)
50 PrefilterFrontEnd.__init__(self,argv=default_argv())
39 51 # Some more code for isolation (yeah, crazy)
40 52 self._on_enter()
41 53 self.out.flush()
@@ -52,17 +64,31 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
52 64
53 65 def isolate_ipython0(func):
54 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):
57 ipython0 = get_ipython0().IP
58 user_ns = deepcopy(ipython0.user_ns)
59 global_ns = deepcopy(ipython0.global_ns)
74 def my_func():
75 iplib = get_ipython0()
76 if iplib is None:
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 81 try:
61 func(*args, **kwargs)
82 out = func()
62 83 finally:
63 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 92 return my_func
67 93
68 94
@@ -74,7 +100,7 b' def test_execution():'
74 100 f.input_buffer = 'print 1'
75 101 f._on_enter()
76 102 out_value = f.out.getvalue()
77 assert out_value == '1\n'
103 assert_equal(out_value, '1\n')
78 104
79 105
80 106 @isolate_ipython0
@@ -87,20 +113,20 b' def test_multiline():'
87 113 f.input_buffer += 'print 1'
88 114 f._on_enter()
89 115 out_value = f.out.getvalue()
90 assert out_value == ''
116 yield assert_equal, out_value, ''
91 117 f._on_enter()
92 118 out_value = f.out.getvalue()
93 assert out_value == '1\n'
119 yield assert_equal, out_value, '1\n'
94 120 f = TestPrefilterFrontEnd()
95 121 f.input_buffer='(1 +'
96 122 f._on_enter()
97 123 f.input_buffer += '0)'
98 124 f._on_enter()
99 125 out_value = f.out.getvalue()
100 assert out_value == ''
126 yield assert_equal, out_value, ''
101 127 f._on_enter()
102 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 132 @isolate_ipython0
@@ -113,13 +139,13 b' def test_capture():'
113 139 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()'
114 140 f._on_enter()
115 141 out_value = f.out.getvalue()
116 assert out_value == '1'
142 yield assert_equal, out_value, '1'
117 143 f = TestPrefilterFrontEnd()
118 144 f.input_buffer = \
119 145 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()'
120 146 f._on_enter()
121 147 out_value = f.out.getvalue()
122 assert out_value == '1'
148 yield assert_equal, out_value, '1'
123 149
124 150
125 151 @isolate_ipython0
@@ -129,10 +155,16 b' def test_magic():'
129 155 This test is fairly fragile and will break when magics change.
130 156 """
131 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 164 f.input_buffer += '%who'
133 165 f._on_enter()
134 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 170 @isolate_ipython0
@@ -156,8 +188,8 b' def test_help():'
156 188
157 189
158 190 @isolate_ipython0
159 def test_completion():
160 """ Test command-line completion.
191 def test_completion_simple():
192 """ Test command-line completion on trivial examples.
161 193 """
162 194 f = TestPrefilterFrontEnd()
163 195 f.input_buffer = 'zzza = 1'
@@ -167,8 +199,47 b' def test_completion():'
167 199 f.input_buffer = 'zz'
168 200 f.complete_current_input()
169 201 out_value = f.out.getvalue()
170 assert out_value == '\nzzza zzzb '
171 assert f.input_buffer == 'zzz'
202 yield assert_equal, out_value, '\nzzza zzzb '
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 245 if __name__ == '__main__':
@@ -177,4 +248,5 b" if __name__ == '__main__':"
177 248 test_execution()
178 249 test_multiline()
179 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 6 __docformat__ = "restructuredtext en"
7 7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2009 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is
12 12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 14
15 15 from cStringIO import StringIO
16 16 from time import sleep
17 17 import sys
18 18
19 from IPython.frontend._process import PipedProcess
19 from IPython.frontend.process import PipedProcess
20 20 from IPython.testing import decorators as testdec
21 21
22 22
@@ -25,6 +25,8 b' import wx.stc as stc'
25 25 from wx.py import editwindow
26 26 import time
27 27 import sys
28 import string
29
28 30 LINESEP = '\n'
29 31 if sys.platform == 'win32':
30 32 LINESEP = '\n\r'
@@ -33,20 +35,26 b' import re'
33 35
34 36 # FIXME: Need to provide an API for non user-generated display on the
35 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 45 _DEFAULT_SIZE = 10
38 46 if sys.platform == 'darwin':
39 47 _DEFAULT_SIZE = 12
40 48
41 49 _DEFAULT_STYLE = {
42 'stdout' : 'fore:#0000FF',
43 'stderr' : 'fore:#007f00',
44 'trace' : 'fore:#FF0000',
45
50 #background definition
46 51 'default' : 'size:%d' % _DEFAULT_SIZE,
47 52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
48 53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
49 54
55 # Edge column: a number of None
56 'edge_column' : -1,
57
50 58 # properties for the various Python lexer styles
51 59 'comment' : 'fore:#007F00',
52 60 'number' : 'fore:#007F7F',
@@ -57,7 +65,24 b' _DEFAULT_STYLE = {'
57 65 'tripledouble' : 'fore:#7F0000',
58 66 'class' : 'fore:#0000FF,bold,underline',
59 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 88 # new style numbers
@@ -69,6 +94,47 b' _TRACE_STYLE = 17'
69 94 # system colors
70 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 139 # The console widget class
74 140 #-------------------------------------------------------------------------------
@@ -83,6 +149,9 b' class ConsoleWidget(editwindow.EditWindow):'
83 149 # stored.
84 150 title = 'Console'
85 151
152 # Last prompt printed
153 last_prompt = ''
154
86 155 # The buffer being edited.
87 156 def _set_input_buffer(self, string):
88 157 self.SetSelection(self.current_prompt_pos, self.GetLength())
@@ -103,19 +172,11 b' class ConsoleWidget(editwindow.EditWindow):'
103 172
104 173 # Translation table from ANSI escape sequences to color. Override
105 174 # this to specify your colors.
106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
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'
175 ANSI_STYLES = ANSI_STYLES.copy()
118 176
177 # Font faces
178 faces = FACES.copy()
179
119 180 # Store the last time a refresh was done
120 181 _last_refresh_time = 0
121 182
@@ -126,7 +187,11 b' class ConsoleWidget(editwindow.EditWindow):'
126 187 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
127 188 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
128 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 196 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
132 197 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
@@ -193,8 +258,19 b' class ConsoleWidget(editwindow.EditWindow):'
193 258 self.current_prompt_pos = self.GetLength()
194 259 self.current_prompt_line = self.GetCurrentLine()
195 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 274 def scroll_to_bottom(self):
199 275 maxrange = self.GetScrollRange(wx.VERTICAL)
200 276 self.ScrollLines(maxrange)
@@ -216,37 +292,24 b' class ConsoleWidget(editwindow.EditWindow):'
216 292 """
217 293 return self.GetSize()[0]/self.GetCharWidth()
218 294
219 #--------------------------------------------------------------------------
220 # EditWindow API
221 #--------------------------------------------------------------------------
222 295
223 def OnUpdateUI(self, event):
224 """ Override the OnUpdateUI of the EditWindow class, to prevent
225 syntax highlighting both for faster redraw, and for more
226 consistent look and feel.
296 def configure_scintilla(self):
297 """ Set up all the styling option of the embedded scintilla
298 widget.
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 #--------------------------------------------------------------------------
230 # Private API
231 #--------------------------------------------------------------------------
232
233 def _apply_style(self):
234 """ Applies the colors for the different text elements and the
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])
306 # Marker for current input buffer.
307 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
308 background=p['stdout'])
309 # Marker for tracebacks.
310 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
311 background=p['stderr'])
247 312
248
249 def _configure_scintilla(self):
250 313 self.SetEOLMode(stc.STC_EOL_LF)
251 314
252 315 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
@@ -268,7 +331,9 b' class ConsoleWidget(editwindow.EditWindow):'
268 331 self.SetWrapMode(stc.STC_WRAP_CHAR)
269 332 self.SetWrapMode(stc.STC_WRAP_WORD)
270 333 self.SetBufferedDraw(True)
271 self.SetUseAntiAliasing(True)
334
335 self.SetUseAntiAliasing(p['antialiasing'])
336
272 337 self.SetLayoutCache(stc.STC_CACHE_PAGE)
273 338 self.SetUndoCollection(False)
274 339 self.SetUseTabs(True)
@@ -289,23 +354,48 b' class ConsoleWidget(editwindow.EditWindow):'
289 354 self.SetMarginWidth(1, 0)
290 355 self.SetMarginWidth(2, 0)
291 356
292 self._apply_style()
293
294 357 # Xterm escape sequences
295 358 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
296 359 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
297 360
298 #self.SetEdgeMode(stc.STC_EDGE_LINE)
299 #self.SetEdgeColumn(80)
300
301 361 # styles
302 p = self.style
303 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
362
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 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 396 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
306 397 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
307 398 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
308
309 399 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
310 400 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
311 401 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
@@ -321,6 +411,28 b' class ConsoleWidget(editwindow.EditWindow):'
321 411 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
322 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 436 def _on_key_down(self, event, skip=True):
325 437 """ Key press callback used for correcting behavior for
326 438 console-like interfaces: the cursor is constraint to be after
@@ -329,6 +441,11 b' class ConsoleWidget(editwindow.EditWindow):'
329 441 Return True if event as been catched.
330 442 """
331 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 449 # Intercept some specific keys.
333 450 if event.KeyCode == ord('L') and event.ControlDown() :
334 451 self.scroll_to_bottom()
@@ -346,6 +463,10 b' class ConsoleWidget(editwindow.EditWindow):'
346 463 self.ScrollPages(-1)
347 464 elif event.KeyCode == wx.WXK_PAGEDOWN:
348 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 470 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
350 471 self.ScrollLines(-1)
351 472 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
@@ -357,16 +478,20 b' class ConsoleWidget(editwindow.EditWindow):'
357 478 event.Skip()
358 479 else:
359 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 483 catched = True
362 self.CallTipCancel()
363 self.write('\n', refresh=False)
364 # Under windows scintilla seems to be doing funny stuff to the
365 # line returns here, but the getter for input_buffer filters
366 # this out.
367 if sys.platform == 'win32':
368 self.input_buffer = self.input_buffer
369 self._on_enter()
484 if not self.enter_catched:
485 self.CallTipCancel()
486 if event.Modifiers == wx.MOD_SHIFT:
487 # Try to force execution
488 self.GotoPos(self.GetLength())
489 self.write('\n' + self.continuation_prompt(),
490 refresh=False)
491 self._on_enter()
492 else:
493 self._on_enter()
494 self.enter_catched = True
370 495
371 496 elif event.KeyCode == wx.WXK_HOME:
372 497 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
@@ -391,16 +516,28 b' class ConsoleWidget(editwindow.EditWindow):'
391 516 catched = True
392 517
393 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 531 event.Skip()
396 532 catched = True
397 533
398 534 if skip and not catched:
399 535 # Put the cursor back in the edit region
400 if self.GetCurrentPos() < self.current_prompt_pos:
401 self.GotoPos(self.current_prompt_pos)
402 else:
403 event.Skip()
536 if not self._keep_cursor_in_buffer():
537 if not (self.GetCurrentPos() == self.GetLength()
538 and event.KeyCode == wx.WXK_DELETE):
539 event.Skip()
540 catched = True
404 541
405 542 return catched
406 543
@@ -408,17 +545,69 b' class ConsoleWidget(editwindow.EditWindow):'
408 545 def _on_key_up(self, event, skip=True):
409 546 """ If cursor is outside the editing region, put it back.
410 547 """
411 event.Skip()
412 if self.GetCurrentPos() < self.current_prompt_pos:
413 self.GotoPos(self.current_prompt_pos)
548 if skip:
549 event.Skip()
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 606 if __name__ == '__main__':
418 607 # Some simple code to test the console widget.
419 608 class MainWindow(wx.Frame):
420 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 611 self._sizer = wx.BoxSizer(wx.VERTICAL)
423 612 self.console_widget = ConsoleWidget(self)
424 613 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
@@ -80,6 +80,15 b' class IPythonX(wx.Frame):'
80 80 self.SetSizer(self._sizer)
81 81 self.SetAutoLayout(1)
82 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 94 def main():
@@ -25,38 +25,19 b' __docformat__ = "restructuredtext en"'
25 25 # Major library imports
26 26 import re
27 27 import __builtin__
28 from time import sleep
29 28 import sys
30 29 from threading import Lock
31 import string
32 30
33 31 import wx
34 32 from wx import stc
35 33
36 34 # Ipython-specific imports.
37 from IPython.frontend._process import PipedProcess
38 from console_widget import ConsoleWidget
35 from IPython.frontend.process import PipedProcess
36 from console_widget import ConsoleWidget, _COMPLETE_BUFFER_MARKER, \
37 _ERROR_MARKER, _INPUT_MARKER
39 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 41 # Classes to implement the Wx frontend
61 42 #-------------------------------------------------------------------------------
62 43 class WxController(ConsoleWidget, PrefilterFrontEnd):
@@ -66,11 +47,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
66 47 This class inherits from ConsoleWidget, that provides a console-like
67 48 widget to provide a text-rendering widget suitable for a terminal.
68 49 """
69
70 output_prompt_template = string.Template(prompt_out)
71
72 input_prompt_template = string.Template(prompt_in1)
73
50
74 51 # Print debug info on what is happening to the console.
75 52 debug = False
76 53
@@ -138,25 +115,24 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
138 115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 116 size=wx.DefaultSize,
140 117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
118 styledef=None,
141 119 *args, **kwds):
142 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 130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
145 131 PrefilterFrontEnd.__init__(self, **kwds)
146 132
147 133 # Stick in our own raw_input:
148 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 136 # A time for flushing the write buffer
161 137 BUFFER_FLUSH_TIMER_ID = 100
162 138 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
@@ -171,8 +147,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
171 147 self.shell.user_ns['self'] = self
172 148 # Inject our own raw_input in namespace
173 149 self.shell.user_ns['raw_input'] = self.raw_input
174
175
150
176 151 def raw_input(self, prompt=''):
177 152 """ A replacement from python's raw_input.
178 153 """
@@ -251,11 +226,8 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
251 226 if (self.AutoCompActive() and line and not line[-1] == '.') \
252 227 or create==True:
253 228 suggestion, completions = self.complete(line)
254 offset=0
255 229 if completions:
256 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
257 residual = complete_sep.split(line)[-1]
258 offset = len(residual)
230 offset = len(self._get_completion_text(line))
259 231 self.pop_completion(completions, offset=offset)
260 232 if self.debug:
261 233 print >>sys.__stdout__, completions
@@ -276,6 +248,14 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
276 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 260 # LineFrontEnd interface
281 261 #--------------------------------------------------------------------------
@@ -299,6 +279,41 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
299 279 raw_string=raw_string)
300 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 317 def save_output_hooks(self):
303 318 self.__old_raw_input = __builtin__.raw_input
304 319 PrefilterFrontEnd.save_output_hooks(self)
@@ -356,10 +371,16 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
356 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 380 def write(self, *args, **kwargs):
360 381 # Avoid multiple inheritence, be explicit about which
361 382 # parent method class gets called
362 ConsoleWidget.write(self, *args, **kwargs)
383 return ConsoleWidget.write(self, *args, **kwargs)
363 384
364 385
365 386 def _on_key_down(self, event, skip=True):
@@ -367,7 +388,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
367 388 widget handle them, and put our logic afterward.
368 389 """
369 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 392 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
372 393 # Capture Control-C
373 394 if self._input_state == 'subprocess':
@@ -413,7 +434,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
413 434 else:
414 435 # Up history
415 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 438 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
418 439 or event.ControlDown() ):
419 440 new_buffer = self.get_history_previous(
@@ -425,7 +446,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
425 446 self.GotoPos(self.current_prompt_pos)
426 447 # Down history
427 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 450 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
430 451 or event.ControlDown() ):
431 452 new_buffer = self.get_history_next()
@@ -433,15 +454,43 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
433 454 self.input_buffer = new_buffer
434 455 # Tab-completion
435 456 elif event.KeyCode == ord('\t'):
436 current_line, current_line_number = self.CurLine
457 current_line, current_line_num = self.CurLine
437 458 if not re.match(r'^\s*$', current_line):
438 459 self.complete_current_input()
439 460 if self.AutoCompActive():
440 461 wx.CallAfter(self._popup_completion, create=True)
441 462 else:
442 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 491 else:
444 492 ConsoleWidget._on_key_down(self, event, skip=skip)
493
445 494
446 495
447 496 def _on_key_up(self, event, skip=True):
@@ -453,14 +502,40 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
453 502 wx.CallAfter(self._popup_completion, create=True)
454 503 else:
455 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 518 def _on_enter(self):
459 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 524 if self.debug:
462 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 1007 else:
1008 1008 os.chmod(security_dir, 0700)
1009 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 1022 # strings and text
1013 1023
@@ -152,7 +152,11 b" def user_setup(ipythondir,rc_suffix,mode='install',interactive=True):"
152 152 printf = lambda s : None
153 153
154 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 160 if mode == 'install' and os.path.isdir(ipythondir):
157 161 return
158 162
@@ -1474,8 +1478,9 b' class InteractiveShell(object,Magic):'
1474 1478 #print "loading rl:",rlcommand # dbg
1475 1479 readline.parse_and_bind(rlcommand)
1476 1480
1477 # remove some chars from the delimiters list
1478 delims = readline.get_completer_delims()
1481 # Remove some chars from the delimiters list. If we encounter
1482 # unicode chars, discard them.
1483 delims = readline.get_completer_delims().encode("ascii", "ignore")
1479 1484 delims = delims.translate(string._idmap,
1480 1485 self.rc.readline_remove_delims)
1481 1486 readline.set_completer_delims(delims)
@@ -55,9 +55,9 b' from IPython.iplib import InteractiveShell'
55 55 from IPython.usage import cmd_line_usage,interactive_usage
56 56 from IPython.genutils import *
57 57
58 def force_import(modname):
59 if modname in sys.modules:
60 print "reload",modname
58 def force_import(modname,force_reload=False):
59 if modname in sys.modules and force_reload:
60 info("reloading: %s" % modname)
61 61 reload(sys.modules[modname])
62 62 else:
63 63 __import__(modname)
@@ -625,25 +625,25 b" object? -> Details about 'object'. ?object also works, ?? prints more."
625 625 except:
626 626 IP.InteractiveTB()
627 627 import_fail_info('ipy_system_conf')
628
628
629 629 # only import prof module if ipythonrc-PROF was not found
630 630 if opts_all.profile and not profile_handled_by_legacy:
631 631 profmodname = 'ipy_profile_' + opts_all.profile
632 632 try:
633
634 633 force_import(profmodname)
635 634 except:
636 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 638 import_fail_info(profmodname)
639 639 else:
640 640 opts.profile = opts_all.profile
641 641 else:
642 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 645 try:
644
645 646 force_import('ipy_user_conf')
646
647 647 except:
648 648 conf = opts_all.ipythondir + "/ipy_user_conf.py"
649 649 IP.InteractiveTB()
@@ -15,10 +15,11 b' if they need blocking clients or in `asyncclient.py` if they want asynchronous,'
15 15 deferred/Twisted using clients.
16 16 """
17 17 __docformat__ = "restructuredtext en"
18 #-------------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 19 # Copyright (C) 2008 The IPython Development Team
20 20 #
21 21 # Distributed under the terms of the BSD License. The full license is in
22 22 # the file COPYING, distributed as part of this software.
23 #-------------------------------------------------------------------------------
24 No newline at end of file
23 #-----------------------------------------------------------------------------
24
25 from IPython.kernel.error import TaskRejectError No newline at end of file
@@ -15,6 +15,7 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 import os, sys
18 19 from os.path import join as pjoin
19 20
20 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 25 default_kernel_config = ConfigObj()
25 26
27 # This will raise OSError if ipythondir doesn't exist.
26 28 security_dir = get_security_dir()
27 29
28 30 #-------------------------------------------------------------------------------
@@ -679,21 +679,22 b' class Interpreter(object):'
679 679 # to exec will fail however. There seems to be some inconsistency in
680 680 # how trailing whitespace is handled, but this seems to work.
681 681 python = python.strip()
682
682
683 683 # The compiler module does not like unicode. We need to convert
684 684 # it encode it:
685 685 if isinstance(python, unicode):
686 686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
687 687 # encode string.
688 688 python = '\xef\xbb\xbf' + python.encode('utf-8')
689
689
690 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 692 ast = compiler.parse(python)
692
693
693 694 # Uncomment to help debug the ast tree
694 695 # for n in ast.node:
695 696 # print n.lineno,'->',n
696
697
697 698 # Each separate command is available by iterating over ast.node. The
698 699 # lineno attribute is the line number (1-indexed) beginning the commands
699 700 # suite.
@@ -703,20 +704,26 b' class Interpreter(object):'
703 704 # We might eventually discover other cases where lineno is None and have
704 705 # to put in a more sophisticated test.
705 706 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
706
707
707 708 # When we finally get the slices, we will need to slice all the way to
708 709 # the end even though we don't have a line number for it. Fortunately,
709 710 # None does the job nicely.
710 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 718 lines = python.splitlines()
712
719
713 720 # Create a list of atomic commands.
714 721 cmds = []
715 722 for i, j in zip(linenos[:-1], linenos[1:]):
716 723 cmd = lines[i:j]
717 724 if cmd:
718 725 cmds.append('\n'.join(cmd)+'\n')
719
726
720 727 return cmds
721 728
722 729 def error(self, text):
@@ -15,6 +15,8 b' __docformat__ = "restructuredtext en"'
15 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 21 class NotificationCenter(object):
20 22 """Synchronous notification center
@@ -2,25 +2,61 b''
2 2
3 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
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.
6 # Copyright (C) 2008-2009 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is
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 20 from IPython.kernel.core.interpreter import Interpreter
19 21
20 def test_unicode():
21 """ Test unicode handling with the interpreter.
22 """
23 i = Interpreter()
24 i.execute_python(u'print "ù"')
25 i.execute_python('print "ù"')
22 #-----------------------------------------------------------------------------
23 # Tests
24 #-----------------------------------------------------------------------------
25
26 class TestInterpreter(unittest.TestCase):
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 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
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.
6 # Copyright (C) 2008-2009 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is
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 20 import IPython.kernel.core.notification as notification
20 from nose.tools import timed
21 21
22 #
23 # Supporting test classes
24 #
22 #-----------------------------------------------------------------------------
23 # Support Classes
24 #-----------------------------------------------------------------------------
25 25
26 26 class Observer(object):
27 27 """docstring for Observer"""
@@ -36,7 +36,6 b' class Observer(object):'
36 36 self.expectedType,
37 37 self.expectedSender)
38 38
39
40 39 def callback(self, theType, sender, args={}):
41 40 """callback"""
42 41
@@ -47,7 +46,6 b' class Observer(object):'
47 46 assert(args == self.expectedKwArgs)
48 47 self.recieved = True
49 48
50
51 49 def verify(self):
52 50 """verify"""
53 51
@@ -57,7 +55,6 b' class Observer(object):'
57 55 """reset"""
58 56
59 57 self.recieved = False
60
61 58
62 59
63 60 class Notifier(object):
@@ -72,11 +69,10 b' class Notifier(object):'
72 69
73 70 center.post_notification(self.theType, self,
74 71 **self.kwargs)
75
76 72
77 #
78 # Test Cases
79 #
73 #-----------------------------------------------------------------------------
74 # Tests
75 #-----------------------------------------------------------------------------
80 76
81 77 class NotificationTests(unittest.TestCase):
82 78 """docstring for NotificationTests"""
@@ -94,7 +90,6 b' class NotificationTests(unittest.TestCase):'
94 90
95 91 observer.verify()
96 92
97
98 93 def test_type_specificity(self):
99 94 """Test that observers are registered by type"""
100 95
@@ -109,7 +104,6 b' class NotificationTests(unittest.TestCase):'
109 104
110 105 observer.verify()
111 106
112
113 107 def test_sender_specificity(self):
114 108 """Test that observers are registered by sender"""
115 109
@@ -123,7 +117,6 b' class NotificationTests(unittest.TestCase):'
123 117
124 118 observer.verify()
125 119
126
127 120 def test_remove_all_observers(self):
128 121 """White-box test for remove_all_observers"""
129 122
@@ -136,8 +129,7 b' class NotificationTests(unittest.TestCase):'
136 129 notification.sharedCenter.remove_all_observers()
137 130
138 131 self.assert_(len(notification.sharedCenter.observers) == 0, "observers removed")
139
140
132
141 133 def test_any_sender(self):
142 134 """test_any_sender"""
143 135
@@ -153,9 +145,7 b' class NotificationTests(unittest.TestCase):'
153 145 observer.reset()
154 146 sender2.post()
155 147 observer.verify()
156
157
158 @timed(.01)
148
159 149 def test_post_performance(self):
160 150 """Test that post_notification, even with many registered irrelevant
161 151 observers is fast"""
@@ -168,4 +158,4 b' class NotificationTests(unittest.TestCase):'
168 158 notification.sharedCenter.post_notification('EXPECTED_TYPE', self)
169 159
170 160 o.verify()
171
161
@@ -2,69 +2,77 b''
2 2 """
3 3 Test the output capture at the OS level, using file descriptors.
4 4 """
5
6 __docformat__ = "restructuredtext en"
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2008-2009 The IPython Development Team
10 7 #
11 8 # Distributed under the terms of the BSD License. The full license is
12 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 19 from cStringIO import StringIO
20 import os
21
22 from twisted.trial import unittest
19 23
20 # Our own imports
21 from IPython.testing import decorators as dec
24 from IPython.testing import decorators_trial 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
49 def test_redirector_output_trap():
50 """ This test check not only that the redirector_output_trap does
33 @dec.skip_win32
34 def test_redirector(self):
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 59 trap the output, but also that it does it in a gready way, that
52 60 is by calling the callback ASAP.
53 """
54 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
55 out = StringIO()
56 trap = RedirectorOutputTrap(out.write, out.write)
57 try:
58 trap.set()
59 for i in range(10):
60 os.system('echo %ic' % i)
61 print "%ip" % i
62 print >>out, i
63 except:
61 """
62 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
63 out = StringIO()
64 trap = RedirectorOutputTrap(out.write, out.write)
65 try:
66 trap.set()
67 for i in range(10):
68 os.system('echo %ic' % i)
69 print "%ip" % i
70 print >>out, i
71 except:
72 trap.unset()
73 raise
64 74 trap.unset()
65 raise
66 trap.unset()
67 result1 = out.getvalue()
68 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
69 assert result1 == result2
70
75 result1 = out.getvalue()
76 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
77 self.assertEquals(result1, result2)
78
@@ -268,6 +268,8 b' def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):'
268 268 # This lets us get fully syntax-highlighted tracebacks.
269 269 if scheme is None:
270 270 try:
271 # Again, reference to a global __IPYTHON__ that doesn't exist.
272 # XXX
271 273 scheme = __IPYTHON__.rc.colors
272 274 except:
273 275 scheme = DEFAULT_SCHEME
@@ -487,10 +489,14 b' class ListTB(TBTools):'
487 489 else:
488 490 list.append('%s\n' % str(stype))
489 491
490 # vds:>>
491 if have_filedata:
492 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
493 # vds:<<
492 # This is being commented out for now as the __IPYTHON__ variable
493 # referenced here is not resolved and causes massive test failures
494 # and errors. B. Granger, 04/2009. XXX
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 501 return list
496 502
@@ -804,13 +810,17 b' class VerboseTB(TBTools):'
804 810 value = text_repr(getattr(evalue, name))
805 811 exception.append('\n%s%s = %s' % (indent, name, value))
806 812
807 # vds: >>
808 if records:
809 filepath, lnum = records[-1][1:3]
810 #print "file:", str(file), "linenb", str(lnum) # dbg
811 filepath = os.path.abspath(filepath)
812 __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
813 # vds: <<
813 # This is being commented out for now as the __IPYTHON__ variable
814 # referenced here is not resolved and causes massive test failures
815 # and errors. B. Granger, 04/2009. XXX
816 # See https://bugs.launchpad.net/bugs/362137
817 # # vds: >>
818 # if records:
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 825 # return all our info assembled as a single string
816 826 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
@@ -67,10 +67,10 b' class EngineConnector(object):'
67 67 self.furl = find_furl(furl_or_file)
68 68 except ValueError:
69 69 return defer.fail(failure.Failure())
70 # return defer.fail(failure.Failure(ValueError('not a valid furl or furl file: %r' % furl_or_file)))
71 d = self.tub.getReference(self.furl)
72 d.addCallbacks(self._register, self._log_failure)
73 return d
70 else:
71 d = self.tub.getReference(self.furl)
72 d.addCallbacks(self._register, self._log_failure)
73 return d
74 74
75 75 def _log_failure(self, reason):
76 76 log.err('EngineConnector: engine registration failed:')
@@ -34,6 +34,9 b' __docformat__ = "restructuredtext en"'
34 34 # Imports
35 35 #-------------------------------------------------------------------------------
36 36
37 # Tell nose to skip the testing of this module
38 __test__ = {}
39
37 40 import os, sys, copy
38 41 import cPickle as pickle
39 42 from new import instancemethod
@@ -266,8 +269,8 b' class StrictDict(dict):'
266 269 pickle.dumps(key, 2)
267 270 pickle.dumps(value, 2)
268 271 newvalue = copy.deepcopy(value)
269 except:
270 raise error.InvalidProperty(value)
272 except Exception, e:
273 raise error.InvalidProperty("can't be a value: %r" % value)
271 274 dict.__setitem__(self, key, newvalue)
272 275 self.modified = True
273 276
@@ -104,6 +104,23 b' class StopLocalExecution(KernelError):'
104 104 class SecurityError(KernelError):
105 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 124 class CompositeError(KernelError):
108 125 def __init__(self, message, elist):
109 126 Exception.__init__(self, *(message, elist))
@@ -20,6 +20,7 b' import sys'
20 20 import cPickle as pickle
21 21 from types import FunctionType
22 22 import linecache
23 import warnings
23 24
24 25 from twisted.internet import reactor
25 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 401 class IFullBlockingMultiEngineClient(Interface):
393 402 pass
394 403
@@ -730,22 +739,27 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
730 739 return self._blockFromThread(self.smultiengine.queue_status, targets=targets, block=block)
731 740
732 741 def set_properties(self, properties, targets=None, block=None):
742 warnings.warn(_prop_warn)
733 743 targets, block = self._findTargetsAndBlock(targets, block)
734 744 return self._blockFromThread(self.smultiengine.set_properties, properties, targets=targets, block=block)
735 745
736 746 def get_properties(self, keys=None, targets=None, block=None):
747 warnings.warn(_prop_warn)
737 748 targets, block = self._findTargetsAndBlock(targets, block)
738 749 return self._blockFromThread(self.smultiengine.get_properties, keys, targets=targets, block=block)
739 750
740 751 def has_properties(self, keys, targets=None, block=None):
752 warnings.warn(_prop_warn)
741 753 targets, block = self._findTargetsAndBlock(targets, block)
742 754 return self._blockFromThread(self.smultiengine.has_properties, keys, targets=targets, block=block)
743 755
744 756 def del_properties(self, keys, targets=None, block=None):
757 warnings.warn(_prop_warn)
745 758 targets, block = self._findTargetsAndBlock(targets, block)
746 759 return self._blockFromThread(self.smultiengine.del_properties, keys, targets=targets, block=block)
747 760
748 761 def clear_properties(self, targets=None, block=None):
762 warnings.warn(_prop_warn)
749 763 targets, block = self._findTargetsAndBlock(targets, block)
750 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 2 # encoding: utf-8
3 3
4 4 """Start an IPython cluster = (controller + engines)."""
@@ -29,29 +29,35 b' from twisted.python import failure, log'
29 29
30 30 from IPython.external import argparse
31 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 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 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 55 from IPython.kernel.util import printer
38 56
39
40 57 #-----------------------------------------------------------------------------
41 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 62 class ProcessStateError(Exception):
57 63 pass
@@ -184,8 +190,10 b' class ControllerLauncher(ProcessLauncher):'
184 190 from IPython.kernel.scripts import ipcontroller
185 191 script_location = ipcontroller.__file__.replace('.pyc', '.py')
186 192 # The -u option here turns on unbuffered output, which is required
187 # on Win32 to prevent wierd conflict and problems with Twisted
188 args = [find_exe('python'), '-u', script_location]
193 # on Win32 to prevent wierd conflict and problems with Twisted.
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 197 else:
190 198 args = ['ipcontroller']
191 199 self.extra_args = extra_args
@@ -204,8 +212,10 b' class EngineLauncher(ProcessLauncher):'
204 212 from IPython.kernel.scripts import ipengine
205 213 script_location = ipengine.__file__.replace('.pyc', '.py')
206 214 # The -u option here turns on unbuffered output, which is required
207 # on Win32 to prevent wierd conflict and problems with Twisted
208 args = [find_exe('python'), '-u', script_location]
215 # on Win32 to prevent wierd conflict and problems with Twisted.
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 219 else:
210 220 args = ['ipengine']
211 221 self.extra_args = extra_args
@@ -465,7 +475,9 b' class SSHEngineSet(object):'
465 475 # The main functions should then just parse the command line arguments, create
466 476 # the appropriate class and call a 'start' method.
467 477
478
468 479 def check_security(args, cont_args):
480 """Check to see if we should run with SSL support."""
469 481 if (not args.x or not args.y) and not have_crypto:
470 482 log.err("""
471 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 490 cont_args.append('-y')
479 491 return True
480 492
493
481 494 def check_reuse(args, cont_args):
495 """Check to see if we should try to resuse FURL files."""
482 496 if args.r:
483 497 cont_args.append('-r')
484 498 if args.client_port == 0 or args.engine_port == 0:
@@ -491,6 +505,25 b' the --client-port and --engine-port options.""")'
491 505 cont_args.append('--engine-port=%i' % args.engine_port)
492 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 527 def main_local(args):
495 528 cont_args = []
496 529 cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
@@ -520,13 +553,10 b' def main_local(args):'
520 553 signal.signal(signal.SIGINT,shutdown)
521 554 d = eset.start(args.n)
522 555 return d
523 def delay_start(cont_pid):
524 # This is needed because the controller doesn't start listening
525 # right when it starts and the controller needs to write
526 # furl files for the engine to pick up
527 reactor.callLater(1.0, start_engines, cont_pid)
528 dstart.addCallback(delay_start)
529 dstart.addErrback(lambda f: f.raiseException())
556 config = kernel_config_manager.get_config_obj()
557 furl_file = config['controller']['engine_furl_file']
558 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
559 dstart.addErrback(_err_and_stop)
530 560
531 561
532 562 def main_mpi(args):
@@ -562,13 +592,10 b' def main_mpi(args):'
562 592 signal.signal(signal.SIGINT,shutdown)
563 593 d = eset.start()
564 594 return d
565 def delay_start(cont_pid):
566 # This is needed because the controller doesn't start listening
567 # right when it starts and the controller needs to write
568 # furl files for the engine to pick up
569 reactor.callLater(1.0, start_engines, cont_pid)
570 dstart.addCallback(delay_start)
571 dstart.addErrback(lambda f: f.raiseException())
595 config = kernel_config_manager.get_config_obj()
596 furl_file = config['controller']['engine_furl_file']
597 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
598 dstart.addErrback(_err_and_stop)
572 599
573 600
574 601 def main_pbs(args):
@@ -595,8 +622,10 b' def main_pbs(args):'
595 622 signal.signal(signal.SIGINT,shutdown)
596 623 d = pbs_set.start(args.n)
597 624 return d
598 dstart.addCallback(start_engines)
599 dstart.addErrback(lambda f: f.raiseException())
625 config = kernel_config_manager.get_config_obj()
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 631 def main_ssh(args):
@@ -637,12 +666,10 b' def main_ssh(args):'
637 666 signal.signal(signal.SIGINT,shutdown)
638 667 d = ssh_set.start(clusterfile['send_furl'])
639 668 return d
640
641 def delay_start(cont_pid):
642 reactor.callLater(1.0, start_engines, cont_pid)
643
644 dstart.addCallback(delay_start)
645 dstart.addErrback(lambda f: f.raiseException())
669 config = kernel_config_manager.get_config_obj()
670 furl_file = config['controller']['engine_furl_file']
671 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
672 dstart.addErrback(_err_and_stop)
646 673
647 674
648 675 def get_args():
@@ -697,8 +724,11 b' def get_args():'
697 724
698 725 parser = argparse.ArgumentParser(
699 726 description='IPython cluster startup. This starts a controller and\
700 engines using various approaches. THIS IS A TECHNOLOGY PREVIEW AND\
701 THE API WILL CHANGE SIGNIFICANTLY BEFORE THE FINAL RELEASE.'
727 engines using various approaches. Use the IPYTHONDIR environment\
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 733 subparsers = parser.add_subparsers(
704 734 help='available cluster types. For help, do "ipcluster TYPE --help"')
@@ -21,8 +21,10 b' __docformat__ = "restructuredtext en"'
21 21 import sys
22 22 sys.path.insert(0, '')
23 23
24 import sys, time, os
25 24 from optparse import OptionParser
25 import os
26 import time
27 import tempfile
26 28
27 29 from twisted.application import internet, service
28 30 from twisted.internet import reactor, error, defer
@@ -37,6 +39,18 b' from IPython.kernel.error import SecurityError'
37 39 from IPython.kernel import controllerservice
38 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 54 from IPython.kernel.config import config_manager as kernel_config_manager
41 55 from IPython.config.cutils import import_item
42 56
@@ -45,6 +59,10 b' from IPython.config.cutils import import_item'
45 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 66 def make_tub(ip, port, secure, cert_file):
49 67 """
50 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 125 """Set the location for the tub and return a deferred."""
108 126
109 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 135 if location == '':
113 136 d = client_tub.setLocationAutomatically()
114 137 else:
115 138 d = defer.maybeDeferred(client_tub.setLocation, "%s:%i" % (location, client_listener.getPortnum()))
116
139
117 140 for ciname, ci in config['controller']['controller_interfaces'].iteritems():
118 141 log.msg("Adapting Controller to interface: %s" % ciname)
119 142 furl_file = ci['furl_file']
@@ -154,7 +177,12 b' def make_engine_service(controller_service, config):'
154 177 """Set the location for the tub and return a deferred."""
155 178
156 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 187 if location == '':
160 188 d = engine_tub.setLocationAutomatically()
@@ -236,7 +264,14 b' def init_config():'
236 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 276 # Client related options
242 277 parser.add_option(
@@ -325,12 +360,6 b' def init_config():'
325 360 help="log file name (default is stdout)"
326 361 )
327 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 363 "-r",
335 364 action="store_true",
336 365 dest="reuse_furls",
@@ -339,7 +368,6 b' def init_config():'
339 368
340 369 (options, args) = parser.parse_args()
341 370
342 kernel_config_manager.update_config_obj_from_default_file(options.ipythondir)
343 371 config = kernel_config_manager.get_config_obj()
344 372
345 373 # Update with command line options
@@ -21,8 +21,8 b' __docformat__ = "restructuredtext en"'
21 21 import sys
22 22 sys.path.insert(0, '')
23 23
24 import sys, os
25 24 from optparse import OptionParser
25 import os
26 26
27 27 from twisted.application import service
28 28 from twisted.internet import reactor
@@ -33,6 +33,19 b' from IPython.kernel.fcutil import Tub, UnauthenticatedTub'
33 33 from IPython.kernel.core.config import config_manager as core_config_manager
34 34 from IPython.config.cutils import import_item
35 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 49 from IPython.kernel.config import config_manager as kernel_config_manager
37 50 from IPython.kernel.engineconnector import EngineConnector
38 51
@@ -106,13 +119,19 b' def start_engine():'
106 119 engine_connector = EngineConnector(tub_service)
107 120 furl_file = kernel_config['engine']['furl_file']
108 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 135 reactor.run()
117 136
118 137
@@ -121,7 +140,14 b' def init_config():'
121 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 152 parser.add_option(
127 153 "--furl-file",
@@ -142,18 +168,9 b' def init_config():'
142 168 dest="logfile",
143 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 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 174 kernel_config = kernel_config_manager.get_config_obj()
158 175 # Now override with command line options
159 176 if options.furl_file is not None:
@@ -16,6 +16,9 b' __docformat__ = "restructuredtext en"'
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 # Tell nose to skip the testing of this module
20 __test__ = {}
21
19 22 import copy, time
20 23 from types import FunctionType
21 24
@@ -49,7 +49,7 b' class BlockingTaskClient(object):'
49 49 """
50 50
51 51 implements(
52 IBlockingTaskClient,
52 IBlockingTaskClient,
53 53 ITaskMapperFactory,
54 54 IMapper,
55 55 ITaskParallelDecorator
@@ -62,7 +62,7 b' class BlockingTaskClient(object):'
62 62 def run(self, task, block=False):
63 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 66 details on how to build a task of different types.
67 67
68 68 :Parameters:
@@ -363,7 +363,8 b' class IEnginePropertiesTestCase(object):'
363 363 p = get_engine(%s).properties"""%self.engine.id
364 364 d = self.engine.execute(s)
365 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 368 d.addCallback(lambda r: self.engine.execute("p['a'] = range(5)"))
368 369 d.addCallback(lambda r: self.engine.execute("p['a'].append(5)"))
369 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 4 #from __future__ import with_statement
2 5
3 6 # XXX This file is currently disabled to preserve 2.4 compatibility.
@@ -23,15 +23,15 b' __docformat__ = "restructuredtext en"'
23 23 # Imports
24 24 #-------------------------------------------------------------------------------
25 25
26 try:
27 from twisted.application.service import IService
28 from IPython.kernel.controllerservice import ControllerService
29 from IPython.kernel.tests import multienginetest as met
30 from controllertest import IControllerCoreTestCase
31 from IPython.testing.util import DeferredTestCase
32 except ImportError:
33 import nose
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
26 # Tell nose to skip this module
27 __test__ = {}
28
29 from twisted.application.service import IService
30 from IPython.kernel.controllerservice import ControllerService
31 from IPython.kernel.tests import multienginetest as met
32 from controllertest import IControllerCoreTestCase
33 from IPython.testing.util import DeferredTestCase
34
35 35
36 36 class BasicControllerServiceTest(DeferredTestCase,
37 37 IControllerCoreTestCase):
@@ -15,30 +15,29 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 try:
19 from twisted.python import components
20 from twisted.internet import reactor, defer
21 from twisted.spread import pb
22 from twisted.internet.base import DelayedCall
23 DelayedCall.debug = True
18 # Tell nose to skip this module
19 __test__ = {}
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
28 from IPython.kernel import engineservice as es
29 from IPython.testing.util import DeferredTestCase
30 from IPython.kernel.controllerservice import IControllerBase
31 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
32 from IPython.kernel.engineservice import IEngineQueued
33 from IPython.kernel.engineconnector import EngineConnector
34
35 from IPython.kernel.tests.engineservicetest import \
36 IEngineCoreTestCase, \
37 IEngineSerializedTestCase, \
38 IEngineQueuedTestCase
39 except ImportError:
40 import nose
41 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27 import zope.interface as zi
28
29 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
30 from IPython.kernel import engineservice as es
31 from IPython.testing.util import DeferredTestCase
32 from IPython.kernel.controllerservice import IControllerBase
33 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
34 from IPython.kernel.engineservice import IEngineQueued
35 from IPython.kernel.engineconnector import EngineConnector
36
37 from IPython.kernel.tests.engineservicetest import \
38 IEngineCoreTestCase, \
39 IEngineSerializedTestCase, \
40 IEngineQueuedTestCase
42 41
43 42
44 43 class EngineFCTest(DeferredTestCase,
@@ -23,20 +23,19 b' __docformat__ = "restructuredtext en"'
23 23 # Imports
24 24 #-------------------------------------------------------------------------------
25 25
26 try:
27 from twisted.internet import defer
28 from twisted.application.service import IService
29
30 from IPython.kernel import engineservice as es
31 from IPython.testing.util import DeferredTestCase
32 from IPython.kernel.tests.engineservicetest import \
33 IEngineCoreTestCase, \
34 IEngineSerializedTestCase, \
35 IEngineQueuedTestCase, \
36 IEnginePropertiesTestCase
37 except ImportError:
38 import nose
39 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
26 # Tell nose to skip this module
27 __test__ = {}
28
29 from twisted.internet import defer
30 from twisted.application.service import IService
31
32 from IPython.kernel import engineservice as es
33 from IPython.testing.util import DeferredTestCase
34 from IPython.kernel.tests.engineservicetest import \
35 IEngineCoreTestCase, \
36 IEngineSerializedTestCase, \
37 IEngineQueuedTestCase, \
38 IEnginePropertiesTestCase
40 39
41 40
42 41 class BasicEngineServiceTest(DeferredTestCase,
@@ -4,29 +4,28 b''
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 #-------------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 13
14 #-------------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 15 # Imports
16 #-------------------------------------------------------------------------------
17
18 try:
19 from twisted.internet import defer
20 from IPython.testing.util import DeferredTestCase
21 from IPython.kernel.controllerservice import ControllerService
22 from IPython.kernel import multiengine as me
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
24 ISynchronousMultiEngineTestCase)
25 except ImportError:
26 import nose
27 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
28
29
16 #-----------------------------------------------------------------------------
17
18 # Tell nose to skip this module
19 __test__ = {}
20
21 from twisted.internet import defer
22 from IPython.testing.util import DeferredTestCase
23 from IPython.kernel.controllerservice import ControllerService
24 from IPython.kernel import multiengine as me
25 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
26 ISynchronousMultiEngineTestCase)
27
28
30 29 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
31 30
32 31 def setUp(self):
@@ -14,25 +14,25 b' __docformat__ = "restructuredtext en"'
14 14 # Imports
15 15 #-------------------------------------------------------------------------------
16 16
17 try:
18 from twisted.internet import defer, reactor
17 # Tell nose to skip this module
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 36 def _raise_it(f):
37 37 try:
38 38 f.raiseException()
@@ -4,36 +4,36 b''
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 #-------------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 13
14 #-------------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 15 # Imports
16 #-------------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 17
18 try:
19 import zope.interface as zi
20 from twisted.trial import unittest
21 from IPython.testing.util import DeferredTestCase
18 # Tell nose to skip this module
19 __test__ = {}
22 20
23 from IPython.kernel.newserialized import \
24 ISerialized, \
25 IUnSerialized, \
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")
21 import zope.interface as zi
22 from twisted.trial import unittest
23 from IPython.testing.util import DeferredTestCase
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 35 # Tests
36 #-------------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 37
38 38 class SerializedTestCase(unittest.TestCase):
39 39
@@ -16,18 +16,18 b' __docformat__ = "restructuredtext en"'
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18
19 try:
20 from twisted.internet import defer
21 from twisted.python import failure
22
23 from IPython.testing.util import DeferredTestCase
24 import IPython.kernel.pendingdeferred as pd
25 from IPython.kernel import error
26 from IPython.kernel.util import printer
27 except ImportError:
28 import nose
29 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
30
19 # Tell nose to skip this module
20 __test__ = {}
21
22 from twisted.internet import defer
23 from twisted.python import failure
24
25 from IPython.testing.util import DeferredTestCase
26 import IPython.kernel.pendingdeferred as pd
27 from IPython.kernel import error
28 from IPython.kernel.util import printer
29
30
31 31 class Foo(object):
32 32
33 33 def bar(self, bahz):
@@ -15,19 +15,19 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 try:
19 import time
20
21 from twisted.internet import defer
22 from twisted.trial import unittest
23
24 from IPython.kernel import task, controllerservice as cs, engineservice as es
25 from IPython.kernel.multiengine import IMultiEngine
26 from IPython.testing.util import DeferredTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
28 except ImportError:
29 import nose
30 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
18 # Tell nose to skip this module
19 __test__ = {}
20
21 import time
22
23 from twisted.internet import defer
24 from twisted.trial import unittest
25
26 from IPython.kernel import task, controllerservice as cs, engineservice as es
27 from IPython.kernel.multiengine import IMultiEngine
28 from IPython.testing.util import DeferredTestCase
29 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
30
31 31
32 32 #-------------------------------------------------------------------------------
33 33 # Tests
@@ -14,27 +14,26 b' __docformat__ = "restructuredtext en"'
14 14 # Imports
15 15 #-------------------------------------------------------------------------------
16 16
17 try:
18 import time
17 # Tell nose to skip this module
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
25 from IPython.kernel import controllerservice as cs
26 import IPython.kernel.multiengine as me
27 from IPython.testing.util import DeferredTestCase
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 from IPython.kernel.taskfc import IFCTaskController
30 from IPython.kernel.util import printer
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
32 from IPython.kernel.clientconnector import ClientConnector
33 from IPython.kernel.error import CompositeError
34 from IPython.kernel.parallelfunction import ParallelFunction
35 except ImportError:
36 import nose
37 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
24 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
25
26 from IPython.kernel import task as taskmodule
27 from IPython.kernel import controllerservice as cs
28 import IPython.kernel.multiengine as me
29 from IPython.testing.util import DeferredTestCase
30 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
31 from IPython.kernel.taskfc import IFCTaskController
32 from IPython.kernel.util import printer
33 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
34 from IPython.kernel.clientconnector import ClientConnector
35 from IPython.kernel.error import CompositeError
36 from IPython.kernel.parallelfunction import ParallelFunction
38 37
39 38
40 39 #-------------------------------------------------------------------------------
@@ -16,12 +16,15 b' __docformat__ = "restructuredtext en"'
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18
19 import os, sys
19 20 import threading, Queue, atexit
20 import twisted
21 21
22 import twisted
22 23 from twisted.internet import defer, reactor
23 24 from twisted.python import log, failure
24 25
26 from IPython.kernel.error import FileTimeoutError
27
25 28 #-------------------------------------------------------------------------------
26 29 # Classes related to twisted and threads
27 30 #-------------------------------------------------------------------------------
@@ -204,3 +207,43 b' class DeferredList(defer.Deferred):'
204 207 result = None
205 208
206 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 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 100 # Deprecated functions
66 101 #-----------------------------------------------------------------------------
@@ -23,3 +23,11 b' ignore_termtitle = True'
23 23 def set_term_title(*args,**kw):
24 24 """Dummy no-op."""
25 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 30 set_term_title = _set_term_title_xterm
31 31 else:
32 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 41 if ret:
42 42 # non-zero return code signals error, don't try again
43 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 123 Parameters
124 124 ----------
125 125 skip_condition : bool or callable.
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
128 is useful for tests that may require costly imports, to delay the cost
129 until the test suite is actually executed.
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
128 is useful for tests that may require costly imports, to delay the cost
129 until the test suite is actually executed.
130 130 msg : string
131 131 Message to give on raising a SkipTest exception
132 132
@@ -29,13 +29,6 b' def setastest(tf=True):'
29 29 tf : bool
30 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 32 This decorator cannot use the nose namespace, because it can be
40 33 called from a non-test module. See also istest and nottest in
41 34 nose.tools
@@ -1,54 +1,121 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 This module provides a main entry point to a user script to test IPython itself
5 from the command line. The main() routine can be used in a similar manner to
6 the ``nosetests`` script, and it takes similar arguments, but if no arguments
7 are given it defaults to testing all of IPython. This should be preferred to
8 using plain ``nosetests`` because a number of nose plugins necessary to test
9 IPython correctly are automatically configured by this code.
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) or trial recursively. This
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 20 # Module imports
14 21 #-----------------------------------------------------------------------------
15 22
16 # stdlib
23 import os
24 import os.path as path
17 25 import sys
26 import subprocess
27 import time
18 28 import warnings
19 29
20 # third-party
21 30 import nose.plugins.builtin
22 31 from nose.core import TestProgram
23 32
24 # Our own imports
33 from IPython.platutils import find_cmd
25 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 59 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
32 60 # testing problems. We should strive to minimize the number of skipped
33 61 # modules, since this means untested code. As the testing machinery
34 62 # solidifies, this list should eventually become empty.
35 EXCLUDE = ['IPython/external/',
36 'IPython/platutils_win32',
37 'IPython/frontend/cocoa',
38 'IPython_doctest_plugin',
39 'IPython/Gnuplot',
40 'IPython/Extensions/ipy_',
41 'IPython/Extensions/clearcmd',
42 'IPython/Extensions/PhysicalQIn',
43 'IPython/Extensions/scitedirector',
63 EXCLUDE = [pjoin('IPython', 'external'),
64 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
65 pjoin('IPython_doctest_plugin'),
66 pjoin('IPython', 'Gnuplot'),
67 pjoin('IPython', 'Extensions', 'ipy_'),
68 pjoin('IPython', 'Extensions', 'clearcmd'),
69 pjoin('IPython', 'Extensions', 'PhysicalQInteractive'),
70 pjoin('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 110 # Functions and classes
48 111 #-----------------------------------------------------------------------------
49 112
50 def main():
51 """Run the IPython test suite.
113 def run_iptest():
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 121 warnings.filterwarnings('ignore',
@@ -60,7 +127,7 b' def main():'
60 127 # test suite back into working shape. Our nose
61 128 # plugin needs to be gone through with a fine
62 129 # toothed comb to find what is causing the problem.
63 # '--with-ipdoctest',
130 '--with-ipdoctest',
64 131 '--ipdoctest-tests','--ipdoctest-extension=txt',
65 132 '--detailed-errors',
66 133
@@ -98,3 +165,136 b' def main():'
98 165 plugins.append(plug)
99 166
100 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 59 # machinery into a fit. This code should be considered a gross hack, but it
60 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 76 # Hack to modify the %run command so we can sync the user's namespace with the
64 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 180 _excepthook = sys.excepthook
168 181 _main = sys.modules.get('__main__')
169 182
183 argv = default_argv()
184
170 185 # Start IPython instance. We customize it to start with minimal frills.
171 186 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
172 IPython.Shell.IPShell(['--colors=NoColor','--noterm_title'],
173 user_ns,global_ns)
187 IPython.Shell.IPShell(argv,user_ns,global_ns)
174 188
175 189 # Deactivate the various python system hooks added by ipython for
176 190 # interactive convenience so we don't confuse the doctest system
@@ -826,11 +840,11 b' class ExtensionDoctest(doctests.Doctest):'
826 840 Modified version that accepts extension modules as valid containers for
827 841 doctests.
828 842 """
829 #print '*** ipdoctest- wantFile:',filename # dbg
843 # print '*** ipdoctest- wantFile:',filename # dbg
830 844
831 845 for pat in self.exclude_patterns:
832 846 if pat.search(filename):
833 #print '###>>> SKIP:',filename # dbg
847 # print '###>>> SKIP:',filename # dbg
834 848 return False
835 849
836 850 if is_extension_module(filename):
@@ -46,7 +46,7 b' def test_deliberately_broken():'
46 46 """A deliberately broken test - we want to skip this one."""
47 47 1/0
48 48
49 @dec.skip('foo')
49 @dec.skip('Testing the skip decorator')
50 50 def test_deliberately_broken2():
51 51 """Another deliberately broken test - we want to skip this one."""
52 52 1/0
@@ -26,15 +26,13 b' Authors'
26 26 # Required modules and packages
27 27 #-----------------------------------------------------------------------------
28 28
29 # Standard Python lib
30 29 import os
31 30 import sys
32 31
33 # Third-party
34 32 import nose.tools as nt
35 33
36 # From this project
37 34 from IPython.tools import utils
35 from IPython.testing import decorators as dec
38 36
39 37 #-----------------------------------------------------------------------------
40 38 # Globals
@@ -55,6 +53,7 b" for _x in [a for a in dir(nt) if a.startswith('assert')]:"
55 53 # Functions and classes
56 54 #-----------------------------------------------------------------------------
57 55
56
58 57 def full_path(startPath,files):
59 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 61 used with a script's __file__ variable as startPath. The base of startPath
63 62 is then prepended to all the listed files, forming the output list.
64 63
65 :Parameters:
64 Parameters
65 ----------
66 66 startPath : string
67 67 Initial path to use as the base for the results. This path is split
68 68 using os.path.split() and only its first component is kept.
@@ -70,7 +70,8 b' def full_path(startPath,files):'
70 70 files : string or list
71 71 One or more files.
72 72
73 :Examples:
73 Examples
74 --------
74 75
75 76 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
76 77 ['/foo/a.txt', '/foo/b.txt']
@@ -210,7 +210,6 b' def test_get_home_dir_7():'
210 210 home_dir = genutils.get_home_dir()
211 211 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
212 212
213
214 213 #
215 214 # Tests for get_ipython_dir
216 215 #
@@ -239,7 +238,6 b' def test_get_ipython_dir_3():'
239 238 ipdir = genutils.get_ipython_dir()
240 239 nt.assert_equal(ipdir, os.path.abspath(os.path.join("someplace", "_ipython")))
241 240
242
243 241 #
244 242 # Tests for get_security_dir
245 243 #
@@ -249,6 +247,14 b' def test_get_security_dir():'
249 247 """Testcase to see if we can call get_security_dir without Exceptions."""
250 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 260 # Tests for popkey
@@ -289,3 +295,12 b' def test_popkey_3():'
289 295 nt.assert_equal(dct, dict(c=3))
290 296 nt.assert_equal(genutils.popkey(dct, "c"), 3)
291 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 13 import nose.tools as nt
14 14
15 15 # our own packages
16 from IPython import iplib
16 from IPython import ipapi, iplib
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Globals
@@ -28,7 +28,15 b' from IPython import iplib'
28 28 # ipapi instance should be read from there, but we also will often need to use
29 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 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 68 # Now repeat the operation with a non-existent directory. Check both that
61 69 # the call succeeds and that the directory is created.
62 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 73 try:
64 74 yield user_setup, (tmpdir,''), kw
65 75 yield os.path.isdir, tmpdir
66 finally:
67 # In this case, clean up the temp dir once done
68 shutil.rmtree(tmpdir)
76 except:
77 pass
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 3 Needs to be run by nose (to make ipython session available).
4 4 """
5 5
6 # Standard library imports
7 6 import os
8 7 import sys
9 8 import tempfile
10 9 import types
11 10
12 # Third-party imports
13 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 14 from IPython.testing import decorators as dec
17 15 from IPython.testing import tools as tt
18 16
@@ -60,12 +58,14 b' def doctest_hist_r():'
60 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 63 def test_obj_del():
65 64 """Test that object's __del__ methods are called on exit."""
66 65 test_dir = os.path.dirname(__file__)
67 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 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 159 tclass.py: deleting object: C-first_pass
160 160 """
161 161
162 @dec.skip_win32
162 163 def doctest_run_builtins():
163 164 """Check that %run doesn't damage __builtins__ via a doctest.
164 165
@@ -204,8 +205,17 b' class TestMagicRun(object):'
204 205 self.tmpfile = f
205 206
206 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 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 219 def test_builtins_id(self):
210 220 """Check that %run doesn't damage __builtins__ """
211 221
@@ -215,6 +225,8 b' class TestMagicRun(object):'
215 225 bid2 = id(_ip.user_ns['__builtins__'])
216 226 tt.assert_equals(bid1, bid2)
217 227
228 # See https://bugs.launchpad.net/bugs/366353
229 @dec.skip_win32
218 230 def test_builtins_type(self):
219 231 """Check that the type of __builtins__ doesn't change with %run.
220 232
@@ -225,6 +237,8 b' class TestMagicRun(object):'
225 237 self.run_tmpfile()
226 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 242 def test_prompts(self):
229 243 """Test that prompts correctly generate after %run"""
230 244 self.run_tmpfile()
@@ -25,6 +25,7 b" if __name__ == '__main__':"
25 25 r'\.cocoa',
26 26 r'\.ipdoctest',
27 27 r'\.Gnuplot',
28 r'\.frontend.process.winprocess',
28 29 ]
29 30 docwriter.write_api_docs(outdir)
30 31 docwriter.write_index(outdir, 'gen',
@@ -302,18 +302,33 b' The ``--furl-file`` flag works like this::'
302 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 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 325 :command:`ipcluster`::
313 326
314 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 333 .. note::
319 334
@@ -2,6 +2,10 b''
2 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 10 Installing and testing IPython at OSC systems
7 11 =============================================
@@ -109,7 +109,7 b' def find_packages():'
109 109 add_package(packages, 'gui')
110 110 add_package(packages, 'gui.wx')
111 111 add_package(packages, 'frontend', tests=True)
112 add_package(packages, 'frontend._process')
112 add_package(packages, 'frontend.process')
113 113 add_package(packages, 'frontend.wx')
114 114 add_package(packages, 'frontend.cocoa', tests=True)
115 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