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 |
|
|
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 |
|
|
|
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 |
|
|
|
273 | completion_string.append(symbol.ljust(max_len)) | |
|
279 | 274 | pos += 1 |
|
280 | 275 | else: |
|
281 |
|
|
|
276 | completion_string.append(symbol.rstrip() + '\n') | |
|
282 | 277 | pos = 1 |
|
283 |
self.write(''.join( |
|
|
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 |
|
|
|
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 |
|
|
|
22 |
|
|
|
23 |
|
|
|
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. |
|
|
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 |
""" |
|
|
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 |
|
|
|
135 | ||
|
136 | # d is now the deferred for the last executed block | |
|
137 |
d.addCallback(self.history |
|
|
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, |
|
|
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( |
|
|
57 |
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( |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
171 |
assert f.input_buffer |
|
|
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. |
|
|
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. |
|
|
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. |
|
|
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 |
|
|
|
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. |
|
|
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_num |
|
|
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_num |
|
|
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_num |
|
|
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_num |
|
|
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 |
# |
|
|
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 |
|
|
|
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, |
|
|
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 |
|
|
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 |
|
|
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 |
# Support |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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 |
|
|
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. |
|
|
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 |
|
|
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 |
|
|
|
31 |
|
|
|
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 |
|
|
|
31 | from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase | |
|
32 |
|
|
|
33 |
|
|
|
34 | ||
|
35 |
|
|
|
36 | IEngineCoreTestCase, \ | |
|
37 | IEngineSerializedTestCase, \ | |
|
38 |
|
|
|
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 |
|
|
|
33 | IEngineCoreTestCase, \ | |
|
34 | IEngineSerializedTestCase, \ | |
|
35 |
|
|
|
36 |
|
|
|
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 |
|
|
|
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 |
|
|
|
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 |
|
|
|
31 |
|
|
|
32 |
|
|
|
33 |
|
|
|
34 |
|
|
|
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 |
|
|
|
127 |
|
|
|
128 |
|
|
|
129 |
|
|
|
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 |
|
|
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 |
|
|
36 | 'IPython/platutils_win32', | |
|
37 |
'IPython |
|
|
38 |
'IPython |
|
|
39 | 'IPython/Gnuplot', | |
|
40 |
'IPython |
|
|
41 | 'IPython/Extensions/clearcmd', | |
|
42 |
'IPython |
|
|
43 |
'IPython |
|
|
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 |
|
|
|
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 | ||
|
271 | print '*'*77 | |
|
272 | print 'Ran %s test sets in %.3fs' % (nrunners, t_tests) | |
|
273 | ||
|
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 | ||
|
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 |
|
|
|
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 |
|
|
|
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. |
|
|
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