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