##// END OF EJS Templates
Merging upstream
Brian Granger -
r2005:7eb8a846 merge
parent child Browse files
Show More
@@ -0,0 +1,37 b''
1 # encoding: utf-8
2 """
3 Test the LineFrontEnd
4 """
5
6 __docformat__ = "restructuredtext en"
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
10 #
11 # Distributed under the terms of the BSD License. The full license is
12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
14
15 from IPython.frontend.linefrontendbase import LineFrontEndBase
16 from copy import deepcopy
17 import nose.tools as nt
18
19 class ConcreteLineFrontEnd(LineFrontEndBase):
20 """ A concrete class to test the LineFrontEndBase.
21 """
22 def capture_output(self):
23 pass
24
25 def release_output(self):
26 pass
27
28
29 def test_is_complete():
30 """ Tests line completion heuristic.
31 """
32 frontend = ConcreteLineFrontEnd()
33 yield nt.assert_true, not frontend.is_complete('for x in \\')
34 yield nt.assert_true, not frontend.is_complete('for x in (1, ):')
35 yield nt.assert_true, frontend.is_complete('for x in (1, ):\n pass')
36
37
@@ -0,0 +1,51 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3
4 #-----------------------------------------------------------------------------
5 # Copyright (C) 2008 The IPython Development Team
6 #
7 # Distributed under the terms of the BSD License. The full license is in
8 # the file COPYING, distributed as part of this software.
9 #-----------------------------------------------------------------------------
10
11 #-----------------------------------------------------------------------------
12 # Imports
13 #-----------------------------------------------------------------------------
14
15 # Tell nose to skip this module
16 __test__ = {}
17
18 import tempfile
19 import os, sys
20
21 from twisted.internet import reactor
22 from twisted.trial import unittest
23
24 from IPython.kernel.error import FileTimeoutError
25 from IPython.kernel.twistedutil import wait_for_file
26
27 #-----------------------------------------------------------------------------
28 # Tests
29 #-----------------------------------------------------------------------------
30
31 class TestWaitForFile(unittest.TestCase):
32
33 def test_delay(self):
34 filename = tempfile.mktemp()
35 def _create_file():
36 open(filename,'w').write('####')
37 dcall = reactor.callLater(0.5, _create_file)
38 d = wait_for_file(filename,delay=0.1)
39 d.addCallback(lambda r: self.assert_(r))
40 def _cancel_dcall(r):
41 if dcall.active():
42 dcall.cancel()
43 d.addCallback(_cancel_dcall)
44 return d
45
46 def test_timeout(self):
47 filename = tempfile.mktemp()
48 d = wait_for_file(filename,delay=0.1,max_tries=1)
49 d.addErrback(lambda f: self.assertRaises(FileTimeoutError,f.raiseException))
50 return d
51 No newline at end of file
@@ -0,0 +1,132 b''
1 # encoding: utf-8
2 """
3 Testing related decorators for use with twisted.trial.
4
5 The decorators in this files are designed to follow the same API as those
6 in the decorators module (in this same directory). But they can be used
7 with twisted.trial
8 """
9
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2009 The IPython Development Team
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
16
17 #-----------------------------------------------------------------------------
18 # Imports
19 #-----------------------------------------------------------------------------
20
21 import os
22 import sys
23
24 from IPython.testing.decorators import make_label_dec
25
26 #-----------------------------------------------------------------------------
27 # Testing decorators
28 #-----------------------------------------------------------------------------
29
30
31 def skipif(skip_condition, msg=None):
32 """Create a decorator that marks a test function for skipping.
33
34 The is a decorator factory that returns a decorator that will
35 conditionally skip a test based on the value of skip_condition. The
36 skip_condition argument can either be a boolean or a callable that returns
37 a boolean.
38
39 Parameters
40 ----------
41 skip_condition : boolean or callable
42 If this evaluates to True, the test is skipped.
43 msg : str
44 The message to print if the test is skipped.
45
46 Returns
47 -------
48 decorator : function
49 The decorator function that can be applied to the test function.
50 """
51
52 def skip_decorator(f):
53
54 # Allow for both boolean or callable skip conditions.
55 if callable(skip_condition):
56 skip_val = lambda : skip_condition()
57 else:
58 skip_val = lambda : skip_condition
59
60 if msg is None:
61 out = 'Test skipped due to test condition.'
62 else:
63 out = msg
64 final_msg = "Skipping test: %s. %s" % (f.__name__,out)
65
66 if skip_val():
67 f.skip = final_msg
68
69 return f
70 return skip_decorator
71
72
73 def skip(msg=None):
74 """Create a decorator that marks a test function for skipping.
75
76 This is a decorator factory that returns a decorator that will cause
77 tests to be skipped.
78
79 Parameters
80 ----------
81 msg : str
82 Optional message to be added.
83
84 Returns
85 -------
86 decorator : function
87 Decorator, which, when applied to a function, sets the skip
88 attribute of the function causing `twisted.trial` to skip it.
89 """
90
91 return skipif(True,msg)
92
93
94 def numpy_not_available():
95 """Can numpy be imported? Returns true if numpy does NOT import.
96
97 This is used to make a decorator to skip tests that require numpy to be
98 available, but delay the 'import numpy' to test execution time.
99 """
100 try:
101 import numpy
102 np_not_avail = False
103 except ImportError:
104 np_not_avail = True
105
106 return np_not_avail
107
108 #-----------------------------------------------------------------------------
109 # Decorators for public use
110 #-----------------------------------------------------------------------------
111
112 # Decorators to skip certain tests on specific platforms.
113 skip_win32 = skipif(sys.platform == 'win32',
114 "This test does not run under Windows")
115 skip_linux = skipif(sys.platform == 'linux2',
116 "This test does not run under Linux")
117 skip_osx = skipif(sys.platform == 'darwin',"This test does not run under OS X")
118
119 # Decorators to skip tests if not on specific platforms.
120 skip_if_not_win32 = skipif(sys.platform != 'win32',
121 "This test only runs under Windows")
122 skip_if_not_linux = skipif(sys.platform != 'linux2',
123 "This test only runs under Linux")
124 skip_if_not_osx = skipif(sys.platform != 'darwin',
125 "This test only runs under OSX")
126
127 # Other skip decorators
128 skipif_not_numpy = skipif(numpy_not_available,"This test requires numpy")
129
130 skipknownfailure = skip('This test is known to fail')
131
132
@@ -0,0 +1,94 b''
1 """Tests for the ipdoctest machinery itself.
2
3 Note: in a file named test_X, functions whose only test is their docstring (as
4 a doctest) and which have no test functionality of their own, should be called
5 'doctest_foo' instead of 'test_foo', otherwise they get double-counted (the
6 empty function call is counted as a test, which just inflates tests numbers
7 artificially).
8 """
9
10 def doctest_simple():
11 """ipdoctest must handle simple inputs
12
13 In [1]: 1
14 Out[1]: 1
15
16 In [2]: print 1
17 1
18 """
19
20
21 def doctest_run_builtins():
22 """Check that %run doesn't damage __builtins__ via a doctest.
23
24 This is similar to the test_run_builtins, but I want *both* forms of the
25 test to catch any possible glitches in our testing machinery, since that
26 modifies %run somewhat. So for this, we have both a normal test (below)
27 and a doctest (this one).
28
29 In [1]: import tempfile
30
31 In [3]: f = tempfile.NamedTemporaryFile()
32
33 In [4]: f.write('pass\\n')
34
35 In [5]: f.flush()
36
37 In [7]: %run $f.name
38 """
39
40 def doctest_multiline1():
41 """The ipdoctest machinery must handle multiline examples gracefully.
42
43 In [2]: for i in range(10):
44 ...: print i,
45 ...:
46 0 1 2 3 4 5 6 7 8 9
47 """
48
49
50 def doctest_multiline2():
51 """Multiline examples that define functions and print output.
52
53 In [7]: def f(x):
54 ...: return x+1
55 ...:
56
57 In [8]: f(1)
58 Out[8]: 2
59
60 In [9]: def g(x):
61 ...: print 'x is:',x
62 ...:
63
64 In [10]: g(1)
65 x is: 1
66
67 In [11]: g('hello')
68 x is: hello
69 """
70
71
72 def doctest_multiline3():
73 """Multiline examples with blank lines.
74
75 In [12]: def h(x):
76 ....: if x>1:
77 ....: return x**2
78 ....: # To leave a blank line in the input, you must mark it
79 ....: # with a comment character:
80 ....: #
81 ....: # otherwise the doctest parser gets confused.
82 ....: else:
83 ....: return -1
84 ....:
85
86 In [13]: h(5)
87 Out[13]: 25
88
89 In [14]: h(1)
90 Out[14]: -1
91
92 In [15]: h(0)
93 Out[15]: -1
94 """
@@ -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,89 b''
1 """Generic testing tools that do NOT depend on Twisted.
2
3 In particular, this module exposes a set of top-level assert* functions that
4 can be used in place of nose.tools.assert* in method generators (the ones in
5 nose can not, at least as of nose 0.10.4).
6
7 Note: our testing package contains testing.util, which does depend on Twisted
8 and provides utilities for tests that manage Deferreds. All testing support
9 tools that only depend on nose, IPython or the standard library should go here
10 instead.
11
12
13 Authors
14 -------
15 - Fernando Perez <Fernando.Perez@berkeley.edu>
16 """
17
18 #*****************************************************************************
19 # Copyright (C) 2009 The IPython Development Team
20 #
21 # Distributed under the terms of the BSD License. The full license is in
22 # the file COPYING, distributed as part of this software.
23 #*****************************************************************************
24
25 #-----------------------------------------------------------------------------
26 # Required modules and packages
27 #-----------------------------------------------------------------------------
28
29 import os
30 import sys
31
32 import nose.tools as nt
33
34 from IPython.tools import utils
35 from IPython.testing import decorators as dec
36
37 #-----------------------------------------------------------------------------
38 # Globals
39 #-----------------------------------------------------------------------------
40
41 # Make a bunch of nose.tools assert wrappers that can be used in test
42 # generators. This will expose an assert* function for each one in nose.tools.
43
44 _tpl = """
45 def %(name)s(*a,**kw):
46 return nt.%(name)s(*a,**kw)
47 """
48
49 for _x in [a for a in dir(nt) if a.startswith('assert')]:
50 exec _tpl % dict(name=_x)
51
52 #-----------------------------------------------------------------------------
53 # Functions and classes
54 #-----------------------------------------------------------------------------
55
56
57 def full_path(startPath,files):
58 """Make full paths for all the listed files, based on startPath.
59
60 Only the base part of startPath is kept, since this routine is typically
61 used with a script's __file__ variable as startPath. The base of startPath
62 is then prepended to all the listed files, forming the output list.
63
64 Parameters
65 ----------
66 startPath : string
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.
69
70 files : string or list
71 One or more files.
72
73 Examples
74 --------
75
76 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
77 ['/foo/a.txt', '/foo/b.txt']
78
79 >>> full_path('/foo',['a.txt','b.txt'])
80 ['/a.txt', '/b.txt']
81
82 If a single file is given, the output is still a list:
83 >>> full_path('/foo','a.txt')
84 ['/a.txt']
85 """
86
87 files = utils.list_strings(files)
88 base = os.path.split(startPath)[0]
89 return [ os.path.join(base,f) for f in files ]
@@ -0,0 +1,41 b''
1 """Minimal script to reproduce our nasty reference counting bug.
2
3 The problem is related to https://bugs.launchpad.net/ipython/+bug/269966
4
5 The original fix for that appeared to work, but John D. Hunter found a
6 matplotlib example which, when run twice in a row, would break. The problem
7 were references held by open figures to internals of Tkinter.
8
9 This code reproduces the problem that John saw, without matplotlib.
10
11 This script is meant to be called by other parts of the test suite that call it
12 via %run as if it were executed interactively by the user. As of 2009-04-13,
13 test_magic.py calls it.
14 """
15
16 #-----------------------------------------------------------------------------
17 # Module imports
18 #-----------------------------------------------------------------------------
19 import sys
20
21 from IPython import ipapi
22
23 #-----------------------------------------------------------------------------
24 # Globals
25 #-----------------------------------------------------------------------------
26 ip = ipapi.get()
27
28 if not '_refbug_cache' in ip.user_ns:
29 ip.user_ns['_refbug_cache'] = []
30
31
32 aglobal = 'Hello'
33 def f():
34 return aglobal
35
36 cache = ip.user_ns['_refbug_cache']
37 cache.append(f)
38
39 def call_f():
40 for func in cache:
41 print 'lowercased:',func().lower()
@@ -0,0 +1,17 b''
1 """Tests for the FakeModule objects.
2 """
3
4 import nose.tools as nt
5
6 from IPython.FakeModule import FakeModule, init_fakemod_dict
7
8 # Make a fakemod and check a few properties
9 def test_mk_fakemod():
10 fm = FakeModule()
11 yield nt.assert_true,fm
12 yield nt.assert_true,lambda : hasattr(fm,'__file__')
13
14 def test_mk_fakemod_fromdict():
15 """Test making a FakeModule object with initial data"""
16 fm = FakeModule(dict(hello=True))
17 nt.assert_true(fm.hello)
@@ -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
@@ -114,6 +114,11 b' except NameError:'
114 114 items.reverse()
115 115 return items
116 116
117 try: # Python 2.4 compatibility
118 GeneratorExit
119 except NameError:
120 GeneratorExit = SystemExit
121
117 122 try:
118 123 import pwd
119 124 except ImportError:
@@ -666,7 +671,7 b' def xrepr(item, mode="default"):'
666 671 try:
667 672 for x in func(mode):
668 673 yield x
669 except (KeyboardInterrupt, SystemExit):
674 except (KeyboardInterrupt, SystemExit, GeneratorExit):
670 675 raise
671 676 except Exception:
672 677 yield (astyle.style_default, repr(item))
@@ -840,20 +845,20 b' def upgradexattr(attr):'
840 845 """
841 846 Convert an attribute descriptor string to a real descriptor object.
842 847
843 If attr already is a descriptor object return if unmodified. A
848 If attr already is a descriptor object return it unmodified. A
844 849 ``SelfDescriptor`` will be returned if ``attr`` is ``None``. ``"foo"``
845 850 returns an ``AttributeDescriptor`` for the attribute named ``"foo"``.
846 851 ``"foo()"`` returns a ``MethodDescriptor`` for the method named ``"foo"``.
847 852 ``"-foo"`` will return an ``IterAttributeDescriptor`` for the attribute
848 853 named ``"foo"`` and ``"-foo()"`` will return an ``IterMethodDescriptor``
849 for the method named ``"foo"``. Furthermore integer will return the appropriate
854 for the method named ``"foo"``. Furthermore integers will return the appropriate
850 855 ``IndexDescriptor`` and callables will return a ``FunctionDescriptor``.
851 856 """
852 857 if attr is None:
853 858 return selfdescriptor
854 859 elif isinstance(attr, Descriptor):
855 860 return attr
856 elif isinstance(attr, str):
861 elif isinstance(attr, basestring):
857 862 if attr.endswith("()"):
858 863 if attr.startswith("-"):
859 864 return IterMethodDescriptor(attr[1:-2])
@@ -66,7 +66,7 b' def attr_matches(self, text):'
66 66 return res
67 67
68 68 def main():
69 import readline
69 import IPython.rlineimpl as readline
70 70 readline.set_completer_delims(" \n\t")
71 71 # monkeypatch - the code will be folded to normal completer later on
72 72 import IPython.completer
@@ -15,6 +15,37 b' sessions.'
15 15
16 16 import types
17 17
18 def init_fakemod_dict(fm,adict=None):
19 """Initialize a FakeModule instance __dict__.
20
21 Kept as a standalone function and not a method so the FakeModule API can
22 remain basically empty.
23
24 This should be considered for private IPython use, used in managing
25 namespaces for %run.
26
27 Parameters
28 ----------
29
30 fm : FakeModule instance
31
32 adict : dict, optional
33 """
34
35 dct = {}
36 # It seems pydoc (and perhaps others) needs any module instance to
37 # implement a __nonzero__ method, so we add it if missing:
38 dct.setdefault('__nonzero__',lambda : True)
39 dct.setdefault('__file__',__file__)
40
41 if adict is not None:
42 dct.update(adict)
43
44 # Hard assignment of the object's __dict__. This is nasty but deliberate.
45 fm.__dict__.clear()
46 fm.__dict__.update(dct)
47
48
18 49 class FakeModule(types.ModuleType):
19 50 """Simple class with attribute access to fake a module.
20 51
@@ -29,14 +60,7 b' class FakeModule(types.ModuleType):'
29 60
30 61 # tmp to force __dict__ instance creation, else self.__dict__ fails
31 62 self.__iptmp = None
32
33 # It seems pydoc (and perhaps others) needs any module instance to
34 # implement a __nonzero__ method, so we add it if missing:
35 self.__dict__.setdefault('__nonzero__',lambda : True)
36 self.__dict__.setdefault('__file__',__file__)
37
38 63 # cleanup our temp trick
39 64 del self.__iptmp
40
41 if adict is not None:
42 self.__dict__.update(adict)
65 # Now, initialize the actual data in the instance dict.
66 init_fakemod_dict(self,adict)
@@ -1584,23 +1584,17 b' Currently the magic system has the following functions:\\n"""'
1584 1584 prog_ns = self.shell.user_ns
1585 1585 __name__save = self.shell.user_ns['__name__']
1586 1586 prog_ns['__name__'] = '__main__'
1587 main_mod = FakeModule(prog_ns)
1587 main_mod = self.shell.new_main_mod(prog_ns)
1588 1588 else:
1589 1589 # Run in a fresh, empty namespace
1590 1590 if opts.has_key('n'):
1591 1591 name = os.path.splitext(os.path.basename(filename))[0]
1592 1592 else:
1593 1593 name = '__main__'
1594 main_mod = FakeModule()
1594
1595 main_mod = self.shell.new_main_mod()
1595 1596 prog_ns = main_mod.__dict__
1596 1597 prog_ns['__name__'] = name
1597
1598 # The shell MUST hold a reference to main_mod so after %run exits,
1599 # the python deletion mechanism doesn't zero it out (leaving
1600 # dangling references). However, we should drop old versions of
1601 # main_mod. There is now a proper API to manage this caching in
1602 # the main shell object, we use that.
1603 self.shell.cache_main_mod(main_mod)
1604 1598
1605 1599 # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
1606 1600 # set the __file__ global in the script's namespace
@@ -1681,7 +1675,7 b' Currently the magic system has the following functions:\\n"""'
1681 1675 exit_ignore=exit_ignore)
1682 1676 t1 = clock2()
1683 1677 t_usr = t1[0]-t0[0]
1684 t_sys = t1[1]-t1[1]
1678 t_sys = t1[1]-t0[1]
1685 1679 print "\nIPython CPU timings (estimated):"
1686 1680 print " User : %10s s." % t_usr
1687 1681 print " System: %10s s." % t_sys
@@ -1693,7 +1687,7 b' Currently the magic system has the following functions:\\n"""'
1693 1687 exit_ignore=exit_ignore)
1694 1688 t1 = clock2()
1695 1689 t_usr = t1[0]-t0[0]
1696 t_sys = t1[1]-t1[1]
1690 t_sys = t1[1]-t0[1]
1697 1691 print "\nIPython CPU timings (estimated):"
1698 1692 print "Total runs performed:",nruns
1699 1693 print " Times : %10s %10s" % ('Total','Per run')
@@ -1703,13 +1697,28 b' Currently the magic system has the following functions:\\n"""'
1703 1697 else:
1704 1698 # regular execution
1705 1699 runner(filename,prog_ns,prog_ns,exit_ignore=exit_ignore)
1700
1706 1701 if opts.has_key('i'):
1707 1702 self.shell.user_ns['__name__'] = __name__save
1708 1703 else:
1704 # The shell MUST hold a reference to prog_ns so after %run
1705 # exits, the python deletion mechanism doesn't zero it out
1706 # (leaving dangling references).
1707 self.shell.cache_main_mod(prog_ns,filename)
1709 1708 # update IPython interactive namespace
1710 1709 del prog_ns['__name__']
1711 1710 self.shell.user_ns.update(prog_ns)
1712 1711 finally:
1712 # It's a bit of a mystery why, but __builtins__ can change from
1713 # being a module to becoming a dict missing some key data after
1714 # %run. As best I can see, this is NOT something IPython is doing
1715 # at all, and similar problems have been reported before:
1716 # http://coding.derkeiler.com/Archive/Python/comp.lang.python/2004-10/0188.html
1717 # Since this seems to be done by the interpreter itself, the best
1718 # we can do is to at least restore __builtins__ for the user on
1719 # exit.
1720 self.shell.user_ns['__builtins__'] = __builtin__
1721
1713 1722 # Ensure key global structures are restored
1714 1723 sys.argv = save_argv
1715 1724 if restore_main:
@@ -1719,6 +1728,7 b' Currently the magic system has the following functions:\\n"""'
1719 1728 # added. Otherwise it will trap references to objects
1720 1729 # contained therein.
1721 1730 del sys.modules[main_mod_name]
1731
1722 1732 self.shell.reloadhist()
1723 1733
1724 1734 return stats
@@ -1800,7 +1810,28 b' Currently the magic system has the following functions:\\n"""'
1800 1810 import timeit
1801 1811 import math
1802 1812
1803 units = [u"s", u"ms", u"\xb5s", u"ns"]
1813 # XXX: Unfortunately the unicode 'micro' symbol can cause problems in
1814 # certain terminals. Until we figure out a robust way of
1815 # auto-detecting if the terminal can deal with it, use plain 'us' for
1816 # microseconds. I am really NOT happy about disabling the proper
1817 # 'micro' prefix, but crashing is worse... If anyone knows what the
1818 # right solution for this is, I'm all ears...
1819 #
1820 # Note: using
1821 #
1822 # s = u'\xb5'
1823 # s.encode(sys.getdefaultencoding())
1824 #
1825 # is not sufficient, as I've seen terminals where that fails but
1826 # print s
1827 #
1828 # succeeds
1829 #
1830 # See bug: https://bugs.launchpad.net/ipython/+bug/348466
1831
1832 #units = [u"s", u"ms",u'\xb5',"ns"]
1833 units = [u"s", u"ms",u'us',"ns"]
1834
1804 1835 scaling = [1, 1e3, 1e6, 1e9]
1805 1836
1806 1837 opts, stmt = self.parse_options(parameter_s,'n:r:tcp:',
@@ -1839,9 +1870,9 b' Currently the magic system has the following functions:\\n"""'
1839 1870 # determine number so that 0.2 <= total time < 2.0
1840 1871 number = 1
1841 1872 for i in range(1, 10):
1842 number *= 10
1843 1873 if timer.timeit(number) >= 0.2:
1844 1874 break
1875 number *= 10
1845 1876
1846 1877 best = min(timer.repeat(repeat, number)) / number
1847 1878
@@ -126,9 +126,13 b' prompt_specials_color = {'
126 126 # Just the prompt counter number, WITHOUT any coloring wrappers, so users
127 127 # can get numbers displayed in whatever color they want.
128 128 r'\N': '${self.cache.prompt_count}',
129
129 130 # Prompt/history count, with the actual digits replaced by dots. Used
130 131 # mainly in continuation prompts (prompt_in2)
131 r'\D': '${"."*len(str(self.cache.prompt_count))}',
132 #r'\D': '${"."*len(str(self.cache.prompt_count))}',
133 # More robust form of the above expression, that uses __builtins__
134 r'\D': '${"."*__builtins__.len(__builtins__.str(self.cache.prompt_count))}',
135
132 136 # Current working directory
133 137 r'\w': '${os.getcwd()}',
134 138 # Current time
@@ -20,10 +20,10 b" name = 'ipython'"
20 20 # because bdist_rpm does not accept dashes (an RPM) convention, and
21 21 # bdist_deb does not accept underscores (a Debian convention).
22 22
23 development = False # change this to False to do a release
24 version_base = '0.9.1'
23 development = True # change this to False to do a release
24 version_base = '0.10'
25 25 branch = 'ipython'
26 revision = '1143'
26 revision = '1163'
27 27
28 28 if development:
29 29 if branch == 'ipython':
@@ -292,17 +292,19 b' if HAS_CTYPES:'
292 292 """raises the exception, performs cleanup if needed"""
293 293 if not inspect.isclass(exctype):
294 294 raise TypeError("Only types can be raised (not instances)")
295 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(tid,
295 # Explicit cast to c_long is necessary for 64-bit support:
296 # See https://bugs.launchpad.net/ipython/+bug/237073
297 res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid),
296 298 ctypes.py_object(exctype))
297 299 if res == 0:
298 300 raise ValueError("invalid thread id")
299 301 elif res != 1:
300 # """if it returns a number greater than one, you're in trouble,
301 # and you should call it again with exc=NULL to revert the effect"""
302 # If it returns a number greater than one, you're in trouble,
303 # and you should call it again with exc=NULL to revert the effect
302 304 ctypes.pythonapi.PyThreadState_SetAsyncExc(tid, 0)
303 305 raise SystemError("PyThreadState_SetAsyncExc failed")
304 306
305 def sigint_handler (signum,stack_frame):
307 def sigint_handler(signum,stack_frame):
306 308 """Sigint handler for threaded apps.
307 309
308 310 This is a horrible hack to pass information about SIGINT _without_
@@ -321,7 +323,7 b' if HAS_CTYPES:'
321 323 Term.cout.flush()
322 324
323 325 else:
324 def sigint_handler (signum,stack_frame):
326 def sigint_handler(signum,stack_frame):
325 327 """Sigint handler for threaded apps.
326 328
327 329 This is a horrible hack to pass information about SIGINT _without_
@@ -99,16 +99,14 b' def main():'
99 99 #import readline
100 100 #readline.parse_and_bind('set completion-query-items 1000')
101 101 #readline.parse_and_bind('set page-completions no')
102
103
104
105
102
103
106 104 # some config helper functions you can use
107 105 def import_all(modules):
108 106 """ Usage: import_all("os sys") """
109 107 for m in modules.split():
110 108 ip.ex("from %s import *" % m)
111
109
112 110 def execf(fname):
113 111 """ Execute a file in user namespace """
114 112 ip.ex('execfile("%s")' % os.path.expanduser(fname))
@@ -248,7 +248,7 b' class BackgroundJobManager:'
248 248 self._update_status()
249 249 new_comp = self._group_report(self._comp_report,'Completed')
250 250 new_dead = self._group_report(self._dead_report,
251 'Dead, call job.traceback() for details')
251 'Dead, call jobs.traceback() for details')
252 252 self._comp_report[:] = []
253 253 self._dead_report[:] = []
254 254 return new_comp or new_dead
@@ -340,7 +340,7 b' class BackgroundJobBase(threading.Thread):'
340 340 stat_created = 'Created'; stat_created_c = 0
341 341 stat_running = 'Running'; stat_running_c = 1
342 342 stat_completed = 'Completed'; stat_completed_c = 2
343 stat_dead = 'Dead (Exception), call job.traceback() for details'
343 stat_dead = 'Dead (Exception), call jobs.traceback() for details'
344 344 stat_dead_c = -1
345 345
346 346 def __init__(self):
@@ -391,7 +391,7 b' class BackgroundJobBase(threading.Thread):'
391 391 self.status = BackgroundJobBase.stat_dead
392 392 self.stat_code = BackgroundJobBase.stat_dead_c
393 393 self.finished = None
394 self.result = ('<BackgroundJob died, call job.traceback() for details>')
394 self.result = ('<BackgroundJob died, call jobs.traceback() for details>')
395 395 self._tb = self._make_tb()
396 396 else:
397 397 self.status = BackgroundJobBase.stat_completed
@@ -14,12 +14,13 b' __docformat__ = "restructuredtext en"'
14 14 #-------------------------------------------------------------------------------
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 from IPython.external import guid
18 17
18 from IPython.external import guid
19 19
20 20 from zope.interface import Interface, Attribute, implements, classProvides
21 21 from twisted.python.failure import Failure
22 from IPython.frontend.frontendbase import FrontEndBase, IFrontEnd, IFrontEndFactory
22 from IPython.frontend.frontendbase import (
23 FrontEndBase, IFrontEnd, IFrontEndFactory)
23 24 from IPython.kernel.core.history import FrontEndHistory
24 25 from IPython.kernel.engineservice import IEngineCore
25 26
@@ -15,30 +15,38 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #---------------------------------------------------------------------------
17 17
18 # Tell nose to skip this module
19 __test__ = {}
20
21 from twisted.trial import unittest
22 from twisted.internet.defer import succeed
23
24 from IPython.kernel.core.interpreter import Interpreter
25 import IPython.kernel.engineservice as es
26
18 27 try:
19 from IPython.kernel.core.interpreter import Interpreter
20 import IPython.kernel.engineservice as es
21 from IPython.testing.util import DeferredTestCase
22 from twisted.internet.defer import succeed
23 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
28 from IPython.frontend.cocoa.cocoa_frontend import IPythonCocoaController
24 29 from Foundation import NSMakeRect
25 from AppKit import NSTextView, NSScrollView
30 from AppKit import NSTextView, NSScrollView
26 31 except ImportError:
27 import nose
28 raise nose.SkipTest("This test requires zope.interface, Twisted, Foolscap and PyObjC")
32 # This tells twisted.trial to skip this module if PyObjC is not found
33 skip = True
29 34
30 class TestIPythonCocoaControler(DeferredTestCase):
35 #---------------------------------------------------------------------------
36 # Tests
37 #---------------------------------------------------------------------------
38 class TestIPythonCocoaControler(unittest.TestCase):
31 39 """Tests for IPythonCocoaController"""
32
40
33 41 def setUp(self):
34 42 self.controller = IPythonCocoaController.alloc().init()
35 43 self.engine = es.EngineService()
36 44 self.engine.startService()
37
45
38 46 def tearDown(self):
39 47 self.controller = None
40 48 self.engine.stopService()
41
49
42 50 def testControllerExecutesCode(self):
43 51 code ="""5+5"""
44 52 expected = Interpreter().execute(code)
@@ -47,48 +55,46 b' class TestIPythonCocoaControler(DeferredTestCase):'
47 55 del result['number']
48 56 del result['id']
49 57 return result
50 self.assertDeferredEquals(
51 self.controller.execute(code).addCallback(removeNumberAndID),
52 expected)
53
58 d = self.controller.execute(code)
59 d.addCallback(removeNumberAndID)
60 d.addCallback(lambda r: self.assertEquals(r, expected))
61
54 62 def testControllerMirrorsUserNSWithValuesAsStrings(self):
55 63 code = """userns1=1;userns2=2"""
56 64 def testControllerUserNS(result):
57 65 self.assertEquals(self.controller.userNS['userns1'], 1)
58 66 self.assertEquals(self.controller.userNS['userns2'], 2)
59
60 67 self.controller.execute(code).addCallback(testControllerUserNS)
61
62
68
63 69 def testControllerInstantiatesIEngine(self):
64 70 self.assert_(es.IEngineBase.providedBy(self.controller.engine))
65
71
66 72 def testControllerCompletesToken(self):
67 73 code = """longNameVariable=10"""
68 74 def testCompletes(result):
69 75 self.assert_("longNameVariable" in result)
70
76
71 77 def testCompleteToken(result):
72 78 self.controller.complete("longNa").addCallback(testCompletes)
73
79
74 80 self.controller.execute(code).addCallback(testCompletes)
75
76
81
82
77 83 def testCurrentIndent(self):
78 84 """test that current_indent_string returns current indent or None.
79 85 Uses _indent_for_block for direct unit testing.
80 86 """
81
87
82 88 self.controller.tabUsesSpaces = True
83 89 self.assert_(self.controller._indent_for_block("""a=3""") == None)
84 90 self.assert_(self.controller._indent_for_block("") == None)
85 91 block = """def test():\n a=3"""
86 92 self.assert_(self.controller._indent_for_block(block) == \
87 93 ' ' * self.controller.tabSpaces)
88
94
89 95 block = """if(True):\n%sif(False):\n%spass""" % \
90 96 (' '*self.controller.tabSpaces,
91 97 2*' '*self.controller.tabSpaces)
92 98 self.assert_(self.controller._indent_for_block(block) == \
93 99 2*(' '*self.controller.tabSpaces))
94
100
@@ -18,10 +18,8 b' __docformat__ = "restructuredtext en"'
18 18 #-------------------------------------------------------------------------------
19 19 import re
20 20
21 import IPython
22 21 import sys
23 22 import codeop
24 import traceback
25 23
26 24 from frontendbase import FrontEndBase
27 25 from IPython.kernel.core.interpreter import Interpreter
@@ -58,6 +56,9 b' class LineFrontEndBase(FrontEndBase):'
58 56 # programatic control of the frontend.
59 57 last_result = dict(number=0)
60 58
59 # The last prompt displayed. Useful for continuation prompts.
60 last_prompt = ''
61
61 62 # The input buffer being edited
62 63 input_buffer = ''
63 64
@@ -151,8 +152,12 b' class LineFrontEndBase(FrontEndBase):'
151 152 self.capture_output()
152 153 try:
153 154 # Add line returns here, to make sure that the statement is
154 # complete.
155 is_complete = codeop.compile_command(string.rstrip() + '\n\n',
155 # complete (except if '\' was used).
156 # This should probably be done in a different place (like
157 # maybe 'prefilter_input' method? For now, this works.
158 clean_string = string.rstrip('\n')
159 if not clean_string.endswith('\\'): clean_string +='\n\n'
160 is_complete = codeop.compile_command(clean_string,
156 161 "<string>", "exec")
157 162 self.release_output()
158 163 except Exception, e:
@@ -183,16 +188,6 b' class LineFrontEndBase(FrontEndBase):'
183 188 # Create a false result, in case there is an exception
184 189 self.last_result = dict(number=self.prompt_number)
185 190
186 ## try:
187 ## self.history.input_cache[-1] = raw_string.rstrip()
188 ## result = self.shell.execute(python_string)
189 ## self.last_result = result
190 ## self.render_result(result)
191 ## except:
192 ## self.show_traceback()
193 ## finally:
194 ## self.after_execute()
195
196 191 try:
197 192 try:
198 193 self.history.input_cache[-1] = raw_string.rstrip()
@@ -272,15 +267,15 b' class LineFrontEndBase(FrontEndBase):'
272 267 symbols_per_line = max(1, chars_per_line/max_len)
273 268
274 269 pos = 1
275 buf = []
270 completion_string = []
276 271 for symbol in possibilities:
277 272 if pos < symbols_per_line:
278 buf.append(symbol.ljust(max_len))
273 completion_string.append(symbol.ljust(max_len))
279 274 pos += 1
280 275 else:
281 buf.append(symbol.rstrip() + '\n')
276 completion_string.append(symbol.rstrip() + '\n')
282 277 pos = 1
283 self.write(''.join(buf))
278 self.write(''.join(completion_string))
284 279 self.new_prompt(self.input_prompt_template.substitute(
285 280 number=self.last_result['number'] + 1))
286 281 self.input_buffer = new_line
@@ -297,26 +292,70 b' class LineFrontEndBase(FrontEndBase):'
297 292 self.write(prompt)
298 293
299 294
295 def continuation_prompt(self):
296 """Returns the current continuation prompt.
297 """
298 return ("."*(len(self.last_prompt)-2) + ': ')
299
300
301 def execute_command(self, command, hidden=False):
302 """ Execute a command, not only in the model, but also in the
303 view, if any.
304 """
305 return self.shell.execute(command)
306
300 307 #--------------------------------------------------------------------------
301 308 # Private API
302 309 #--------------------------------------------------------------------------
303 310
304 def _on_enter(self):
311 def _on_enter(self, new_line_pos=0):
305 312 """ Called when the return key is pressed in a line editing
306 313 buffer.
314
315 Parameters
316 ----------
317 new_line_pos : integer, optional
318 Position of the new line to add, starting from the
319 end (0 adds a new line after the last line, -1 before
320 the last line...)
321
322 Returns
323 -------
324 True if execution is triggered
307 325 """
308 326 current_buffer = self.input_buffer
309 cleaned_buffer = self.prefilter_input(current_buffer)
327 # XXX: This string replace is ugly, but there should be no way it
328 # fails.
329 prompt_less_buffer = re.sub('^' + self.continuation_prompt(),
330 '', current_buffer).replace('\n' + self.continuation_prompt(),
331 '\n')
332 cleaned_buffer = self.prefilter_input(prompt_less_buffer)
310 333 if self.is_complete(cleaned_buffer):
311 334 self.execute(cleaned_buffer, raw_string=current_buffer)
335 return True
312 336 else:
313 self.input_buffer += self._get_indent_string(
314 current_buffer[:-1])
315 if len(current_buffer.split('\n')) == 2:
316 self.input_buffer += '\t\t'
317 if current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
318 self.input_buffer += '\t'
319
337 # Start a new line.
338 new_line_pos = -new_line_pos
339 lines = current_buffer.split('\n')[:-1]
340 prompt_less_lines = prompt_less_buffer.split('\n')
341 # Create the new line, with the continuation prompt, and the
342 # same amount of indent than the line above it.
343 new_line = self.continuation_prompt() + \
344 self._get_indent_string('\n'.join(
345 prompt_less_lines[:new_line_pos-1]))
346 if len(lines) == 1:
347 # We are starting a first continuation line. Indent it.
348 new_line += '\t'
349 elif current_buffer[:-1].split('\n')[-1].rstrip().endswith(':'):
350 # The last line ends with ":", autoindent the new line.
351 new_line += '\t'
352
353 if new_line_pos == 0:
354 lines.append(new_line)
355 else:
356 lines.insert(new_line_pos, new_line)
357 self.input_buffer = '\n'.join(lines)
358
320 359
321 360 def _get_indent_string(self, string):
322 361 """ Return the string of whitespace that prefixes a line. Used to
@@ -22,9 +22,10 b' __docformat__ = "restructuredtext en"'
22 22 # Imports
23 23 #-------------------------------------------------------------------------------
24 24 import sys
25
26 from linefrontendbase import LineFrontEndBase, common_prefix
27 from frontendbase import FrontEndBase
25 import pydoc
26 import os
27 import re
28 import __builtin__
28 29
29 30 from IPython.ipmaker import make_IPython
30 31 from IPython.ipapi import IPApi
@@ -33,9 +34,8 b' from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap'
33 34 from IPython.kernel.core.sync_traceback_trap import SyncTracebackTrap
34 35
35 36 from IPython.genutils import Term
36 import pydoc
37 import os
38 import sys
37
38 from linefrontendbase import LineFrontEndBase, common_prefix
39 39
40 40
41 41 def mk_system_call(system_call_function, command):
@@ -45,6 +45,8 b' def mk_system_call(system_call_function, command):'
45 45 """
46 46 def my_system_call(args):
47 47 system_call_function("%s %s" % (command, args))
48
49 my_system_call.__doc__ = "Calls %s" % command
48 50 return my_system_call
49 51
50 52 #-------------------------------------------------------------------------------
@@ -62,13 +64,25 b' class PrefilterFrontEnd(LineFrontEndBase):'
62 64
63 65 debug = False
64 66
65 def __init__(self, ipython0=None, *args, **kwargs):
67 def __init__(self, ipython0=None, argv=None, *args, **kwargs):
66 68 """ Parameters:
67 69 -----------
68 70
69 71 ipython0: an optional ipython0 instance to use for command
70 72 prefiltering and completion.
73
74 argv : list, optional
75 Used as the instance's argv value. If not given, [] is used.
71 76 """
77 if argv is None:
78 argv = []
79 # This is a hack to avoid the IPython exception hook to trigger
80 # on exceptions (https://bugs.launchpad.net/bugs/337105)
81 # XXX: This is horrible: module-leve monkey patching -> side
82 # effects.
83 from IPython import iplib
84 iplib.InteractiveShell.isthreaded = True
85
72 86 LineFrontEndBase.__init__(self, *args, **kwargs)
73 87 self.shell.output_trap = RedirectorOutputTrap(
74 88 out_callback=self.write,
@@ -83,10 +97,16 b' class PrefilterFrontEnd(LineFrontEndBase):'
83 97 if ipython0 is None:
84 98 # Instanciate an IPython0 interpreter to be able to use the
85 99 # prefiltering.
100 # Suppress all key input, to avoid waiting
101 def my_rawinput(x=None):
102 return '\n'
103 old_rawinput = __builtin__.raw_input
104 __builtin__.raw_input = my_rawinput
86 105 # XXX: argv=[] is a bit bold.
87 ipython0 = make_IPython(argv=[],
106 ipython0 = make_IPython(argv=argv,
88 107 user_ns=self.shell.user_ns,
89 108 user_global_ns=self.shell.user_global_ns)
109 __builtin__.raw_input = old_rawinput
90 110 self.ipython0 = ipython0
91 111 # Set the pager:
92 112 self.ipython0.set_hook('show_in_pager',
@@ -98,16 +118,17 b' class PrefilterFrontEnd(LineFrontEndBase):'
98 118 self._ip.system = self.system_call
99 119 # XXX: Muck around with magics so that they work better
100 120 # in our environment
101 self.ipython0.magic_ls = mk_system_call(self.system_call,
102 'ls -CF')
121 if not sys.platform.startswith('win'):
122 self.ipython0.magic_ls = mk_system_call(self.system_call,
123 'ls -CF')
103 124 # And now clean up the mess created by ipython0
104 125 self.release_output()
105 126
106 127
107 128 if not 'banner' in kwargs and self.banner is None:
108 self.banner = self.ipython0.BANNER + """
109 This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""
129 self.banner = self.ipython0.BANNER
110 130
131 # FIXME: __init__ and start should be two different steps
111 132 self.start()
112 133
113 134 #--------------------------------------------------------------------------
@@ -117,7 +138,10 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
117 138 def show_traceback(self):
118 139 """ Use ipython0 to capture the last traceback and display it.
119 140 """
120 self.capture_output()
141 # Don't do the capture; the except_hook has already done some
142 # modifications to the IO streams, if we store them, we'll be
143 # storing the wrong ones.
144 #self.capture_output()
121 145 self.ipython0.showtraceback(tb_offset=-1)
122 146 self.release_output()
123 147
@@ -171,7 +195,7 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
171 195 def complete(self, line):
172 196 # FIXME: This should be factored out in the linefrontendbase
173 197 # method.
174 word = line.split('\n')[-1].split(' ')[-1]
198 word = self._get_completion_text(line)
175 199 completions = self.ipython0.complete(word)
176 200 # FIXME: The proper sort should be done in the complete method.
177 201 key = lambda x: x.replace('_', '')
@@ -244,3 +268,18 b' This is the wx frontend, by Gael Varoquaux. This is EXPERIMENTAL code."""'
244 268 """
245 269 self.ipython0.atexit_operations()
246 270
271
272 def _get_completion_text(self, line):
273 """ Returns the text to be completed by breaking the line at specified
274 delimiters.
275 """
276 # Break at: spaces, '=', all parentheses (except if balanced).
277 # FIXME2: In the future, we need to make the implementation similar to
278 # that in the 'pyreadline' module (modes/basemode.py) where we break at
279 # each delimiter and try to complete the residual line, until we get a
280 # successful list of completions.
281 expression = '\s|=|,|:|\((?!.*\))|\[(?!.*\])|\{(?!.*\})'
282 complete_sep = re.compile(expression)
283 text = complete_sep.split(line)[-1]
284 return text
285
1 NO CONTENT: file renamed from IPython/frontend/_process/__init__.py to IPython/frontend/process/__init__.py
@@ -151,7 +151,12 b' else:'
151 151 self._thread = ht
152 152 self.pid = pid
153 153
154 winprocess.AssignProcessToJobObject(self._job, hp)
154 # XXX: A try/except to fix UAC-related problems under
155 # Windows Vista, when reparenting jobs.
156 try:
157 winprocess.AssignProcessToJobObject(self._job, hp)
158 except WindowsError:
159 pass
155 160 winprocess.ResumeThread(ht)
156 161
157 162 if p2cread is not None:
1 NO CONTENT: file renamed from IPython/frontend/_process/pipedprocess.py to IPython/frontend/process/pipedprocess.py
1 NO CONTENT: file renamed from IPython/frontend/_process/winprocess.py to IPython/frontend/process/winprocess.py
@@ -1,6 +1,6 b''
1 1 # encoding: utf-8
2 2
3 """This file contains unittests for the frontendbase module."""
3 """This file contains unittests for the asyncfrontendbase module."""
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
@@ -15,17 +15,15 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #---------------------------------------------------------------------------
17 17
18 import unittest
18 # Tell nose to skip this module
19 __test__ = {}
19 20
20 try:
21 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
22 from IPython.frontend import frontendbase
23 from IPython.kernel.engineservice import EngineService
24 except ImportError:
25 import nose
26 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
21 from twisted.trial import unittest
22 from IPython.frontend.asyncfrontendbase import AsyncFrontEndBase
23 from IPython.frontend import frontendbase
24 from IPython.kernel.engineservice import EngineService
25 from IPython.testing.parametric import Parametric, parametric
27 26
28 from IPython.testing.decorators import skip
29 27
30 28 class FrontEndCallbackChecker(AsyncFrontEndBase):
31 29 """FrontEndBase subclass for checking callbacks"""
@@ -44,14 +42,11 b' class FrontEndCallbackChecker(AsyncFrontEndBase):'
44 42 self.renderResultCalled = True
45 43 return result
46 44
47
48 45 def render_error(self, failure):
49 46 self.renderErrorCalled = True
50 47 return failure
51
52 48
53 49
54
55 50 class TestAsyncFrontendBase(unittest.TestCase):
56 51 def setUp(self):
57 52 """Setup the EngineService and FrontEndBase"""
@@ -59,97 +54,56 b' class TestAsyncFrontendBase(unittest.TestCase):'
59 54 self.fb = FrontEndCallbackChecker(engine=EngineService())
60 55
61 56 def test_implements_IFrontEnd(self):
62 assert(frontendbase.IFrontEnd.implementedBy(
57 self.assert_(frontendbase.IFrontEnd.implementedBy(
63 58 AsyncFrontEndBase))
64 59
65 60 def test_is_complete_returns_False_for_incomplete_block(self):
66 """"""
67
68 61 block = """def test(a):"""
69
70 assert(self.fb.is_complete(block) == False)
62 self.assert_(self.fb.is_complete(block) == False)
71 63
72 64 def test_is_complete_returns_True_for_complete_block(self):
73 """"""
74
75 65 block = """def test(a): pass"""
76
77 assert(self.fb.is_complete(block))
78
66 self.assert_(self.fb.is_complete(block))
79 67 block = """a=3"""
80
81 assert(self.fb.is_complete(block))
68 self.assert_(self.fb.is_complete(block))
82 69
83 70 def test_blockID_added_to_result(self):
84 71 block = """3+3"""
85
86 72 d = self.fb.execute(block, blockID='TEST_ID')
87
88 d.addCallback(self.checkBlockID, expected='TEST_ID')
73 d.addCallback(lambda r: self.assert_(r['blockID']=='TEST_ID'))
74 return d
89 75
90 76 def test_blockID_added_to_failure(self):
91 77 block = "raise Exception()"
92
93 78 d = self.fb.execute(block,blockID='TEST_ID')
94 d.addErrback(self.checkFailureID, expected='TEST_ID')
95
96 def checkBlockID(self, result, expected=""):
97 assert(result['blockID'] == expected)
98
99
100 def checkFailureID(self, failure, expected=""):
101 assert(failure.blockID == expected)
102
79 d.addErrback(lambda f: self.assert_(f.blockID=='TEST_ID'))
80 return d
103 81
104 82 def test_callbacks_added_to_execute(self):
105 """test that
106 update_cell_prompt
107 render_result
108
109 are added to execute request
110 """
111
112 83 d = self.fb.execute("10+10")
113 d.addCallback(self.checkCallbacks)
84 d.addCallback(lambda r: self.assert_(self.fb.updateCalled and self.fb.renderResultCalled))
85 return d
114 86
115 def checkCallbacks(self, result):
116 assert(self.fb.updateCalled)
117 assert(self.fb.renderResultCalled)
118
119 @skip("This test fails and lead to an unhandled error in a Deferred.")
120 87 def test_error_callback_added_to_execute(self):
121 """test that render_error called on execution error"""
88 """Test that render_error called on execution error."""
122 89
123 90 d = self.fb.execute("raise Exception()")
124 d.addCallback(self.checkRenderError)
125
126 def checkRenderError(self, result):
127 assert(self.fb.renderErrorCalled)
91 d.addErrback(lambda f: self.assert_(self.fb.renderErrorCalled))
92 return d
128 93
129 94 def test_history_returns_expected_block(self):
130 """Make sure history browsing doesn't fail"""
95 """Make sure history browsing doesn't fail."""
131 96
132 97 blocks = ["a=1","a=2","a=3"]
133 for b in blocks:
134 d = self.fb.execute(b)
135
136 # d is now the deferred for the last executed block
137 d.addCallback(self.historyTests, blocks)
138
139
140 def historyTests(self, result, blocks):
141 """historyTests"""
142
143 assert(len(blocks) >= 3)
144 assert(self.fb.get_history_previous("") == blocks[-2])
145 assert(self.fb.get_history_previous("") == blocks[-3])
146 assert(self.fb.get_history_next() == blocks[-2])
147
148
149 def test_history_returns_none_at_startup(self):
150 """test_history_returns_none_at_startup"""
151
152 assert(self.fb.get_history_previous("")==None)
153 assert(self.fb.get_history_next()==None)
154
155
98 d = self.fb.execute(blocks[0])
99 d.addCallback(lambda _: self.fb.execute(blocks[1]))
100 d.addCallback(lambda _: self.fb.execute(blocks[2]))
101 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-2]))
102 d.addCallback(lambda _: self.assert_(self.fb.get_history_previous("")==blocks[-3]))
103 d.addCallback(lambda _: self.assert_(self.fb.get_history_next()==blocks[-2]))
104 return d
105
106 def test_history_returns_none_at_startup(self):
107 self.assert_(self.fb.get_history_previous("")==None)
108 self.assert_(self.fb.get_history_next()==None)
109
@@ -12,12 +12,32 b' __docformat__ = "restructuredtext en"'
12 12 # in the file COPYING, distributed as part of this software.
13 13 #-------------------------------------------------------------------------------
14 14
15 from copy import copy, deepcopy
15 16 from cStringIO import StringIO
16 17 import string
17 18
18 from IPython.ipapi import get as get_ipython0
19 from nose.tools import assert_equal
20
19 21 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
20 from copy import deepcopy
22 from IPython.ipapi import get as get_ipython0
23 from IPython.testing.plugin.ipdoctest import default_argv
24
25
26 def safe_deepcopy(d):
27 """ Deep copy every key of the given dict, when possible. Elsewhere
28 do a copy.
29 """
30 copied_d = dict()
31 for key, value in d.iteritems():
32 try:
33 copied_d[key] = deepcopy(value)
34 except:
35 try:
36 copied_d[key] = copy(value)
37 except:
38 copied_d[key] = value
39 return copied_d
40
21 41
22 42 class TestPrefilterFrontEnd(PrefilterFrontEnd):
23 43
@@ -26,16 +46,8 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
26 46 banner = ''
27 47
28 48 def __init__(self):
29 ipython0 = get_ipython0().IP
30 49 self.out = StringIO()
31 PrefilterFrontEnd.__init__(self, ipython0=ipython0)
32 # Clean up the namespace for isolation between tests
33 user_ns = self.ipython0.user_ns
34 # We need to keep references to things so that they don't
35 # get garbage collected (this stinks).
36 self.shadow_ns = dict()
37 for i in self.ipython0.magic_who_ls():
38 self.shadow_ns[i] = user_ns.pop(i)
50 PrefilterFrontEnd.__init__(self,argv=default_argv())
39 51 # Some more code for isolation (yeah, crazy)
40 52 self._on_enter()
41 53 self.out.flush()
@@ -52,17 +64,31 b' class TestPrefilterFrontEnd(PrefilterFrontEnd):'
52 64
53 65 def isolate_ipython0(func):
54 66 """ Decorator to isolate execution that involves an iptyhon0.
67
68 Notes
69 -----
70
71 Apply only to functions with no arguments. Nose skips functions
72 with arguments.
55 73 """
56 def my_func(*args, **kwargs):
57 ipython0 = get_ipython0().IP
58 user_ns = deepcopy(ipython0.user_ns)
59 global_ns = deepcopy(ipython0.global_ns)
74 def my_func():
75 iplib = get_ipython0()
76 if iplib is None:
77 return func()
78 ipython0 = iplib.IP
79 global_ns = safe_deepcopy(ipython0.user_global_ns)
80 user_ns = safe_deepcopy(ipython0.user_ns)
60 81 try:
61 func(*args, **kwargs)
82 out = func()
62 83 finally:
63 84 ipython0.user_ns = user_ns
64 ipython0.global_ns = global_ns
85 ipython0.user_global_ns = global_ns
86 # Undo the hack at creation of PrefilterFrontEnd
87 from IPython import iplib
88 iplib.InteractiveShell.isthreaded = False
89 return out
65 90
91 my_func.__name__ = func.__name__
66 92 return my_func
67 93
68 94
@@ -74,7 +100,7 b' def test_execution():'
74 100 f.input_buffer = 'print 1'
75 101 f._on_enter()
76 102 out_value = f.out.getvalue()
77 assert out_value == '1\n'
103 assert_equal(out_value, '1\n')
78 104
79 105
80 106 @isolate_ipython0
@@ -87,20 +113,20 b' def test_multiline():'
87 113 f.input_buffer += 'print 1'
88 114 f._on_enter()
89 115 out_value = f.out.getvalue()
90 assert out_value == ''
116 yield assert_equal, out_value, ''
91 117 f._on_enter()
92 118 out_value = f.out.getvalue()
93 assert out_value == '1\n'
119 yield assert_equal, out_value, '1\n'
94 120 f = TestPrefilterFrontEnd()
95 121 f.input_buffer='(1 +'
96 122 f._on_enter()
97 123 f.input_buffer += '0)'
98 124 f._on_enter()
99 125 out_value = f.out.getvalue()
100 assert out_value == ''
126 yield assert_equal, out_value, ''
101 127 f._on_enter()
102 128 out_value = f.out.getvalue()
103 assert out_value == '1\n'
129 yield assert_equal, out_value, '1\n'
104 130
105 131
106 132 @isolate_ipython0
@@ -113,13 +139,13 b' def test_capture():'
113 139 'import os; out=os.fdopen(1, "w"); out.write("1") ; out.flush()'
114 140 f._on_enter()
115 141 out_value = f.out.getvalue()
116 assert out_value == '1'
142 yield assert_equal, out_value, '1'
117 143 f = TestPrefilterFrontEnd()
118 144 f.input_buffer = \
119 145 'import os; out=os.fdopen(2, "w"); out.write("1") ; out.flush()'
120 146 f._on_enter()
121 147 out_value = f.out.getvalue()
122 assert out_value == '1'
148 yield assert_equal, out_value, '1'
123 149
124 150
125 151 @isolate_ipython0
@@ -129,10 +155,16 b' def test_magic():'
129 155 This test is fairly fragile and will break when magics change.
130 156 """
131 157 f = TestPrefilterFrontEnd()
158 # Before checking the interactive namespace, make sure it's clear (it can
159 # otherwise pick up things stored in the user's local db)
160 f.input_buffer += '%reset -f'
161 f._on_enter()
162 f.complete_current_input()
163 # Now, run the %who magic and check output
132 164 f.input_buffer += '%who'
133 165 f._on_enter()
134 166 out_value = f.out.getvalue()
135 assert out_value == 'Interactive namespace is empty.\n'
167 assert_equal(out_value, 'Interactive namespace is empty.\n')
136 168
137 169
138 170 @isolate_ipython0
@@ -156,8 +188,8 b' def test_help():'
156 188
157 189
158 190 @isolate_ipython0
159 def test_completion():
160 """ Test command-line completion.
191 def test_completion_simple():
192 """ Test command-line completion on trivial examples.
161 193 """
162 194 f = TestPrefilterFrontEnd()
163 195 f.input_buffer = 'zzza = 1'
@@ -167,8 +199,47 b' def test_completion():'
167 199 f.input_buffer = 'zz'
168 200 f.complete_current_input()
169 201 out_value = f.out.getvalue()
170 assert out_value == '\nzzza zzzb '
171 assert f.input_buffer == 'zzz'
202 yield assert_equal, out_value, '\nzzza zzzb '
203 yield assert_equal, f.input_buffer, 'zzz'
204
205
206 @isolate_ipython0
207 def test_completion_parenthesis():
208 """ Test command-line completion when a parenthesis is open.
209 """
210 f = TestPrefilterFrontEnd()
211 f.input_buffer = 'zzza = 1'
212 f._on_enter()
213 f.input_buffer = 'zzzb = 2'
214 f._on_enter()
215 f.input_buffer = 'map(zz'
216 f.complete_current_input()
217 out_value = f.out.getvalue()
218 yield assert_equal, out_value, '\nzzza zzzb '
219 yield assert_equal, f.input_buffer, 'map(zzz'
220
221
222 @isolate_ipython0
223 def test_completion_indexing():
224 """ Test command-line completion when indexing on objects.
225 """
226 f = TestPrefilterFrontEnd()
227 f.input_buffer = 'a = [0]'
228 f._on_enter()
229 f.input_buffer = 'a[0].'
230 f.complete_current_input()
231 assert_equal(f.input_buffer, 'a[0].__')
232
233
234 @isolate_ipython0
235 def test_completion_equal():
236 """ Test command-line completion when the delimiter is "=", not " ".
237 """
238 f = TestPrefilterFrontEnd()
239 f.input_buffer = 'a=1.'
240 f.complete_current_input()
241 assert_equal(f.input_buffer, 'a=1.__')
242
172 243
173 244
174 245 if __name__ == '__main__':
@@ -177,4 +248,5 b" if __name__ == '__main__':"
177 248 test_execution()
178 249 test_multiline()
179 250 test_capture()
180 test_completion()
251 test_completion_simple()
252 test_completion_complex()
@@ -5,18 +5,18 b' Test process execution and IO redirection.'
5 5
6 6 __docformat__ = "restructuredtext en"
7 7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2009 The IPython Development Team
10 10 #
11 11 # Distributed under the terms of the BSD License. The full license is
12 12 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 14
15 15 from cStringIO import StringIO
16 16 from time import sleep
17 17 import sys
18 18
19 from IPython.frontend._process import PipedProcess
19 from IPython.frontend.process import PipedProcess
20 20 from IPython.testing import decorators as testdec
21 21
22 22
@@ -25,6 +25,8 b' import wx.stc as stc'
25 25 from wx.py import editwindow
26 26 import time
27 27 import sys
28 import string
29
28 30 LINESEP = '\n'
29 31 if sys.platform == 'win32':
30 32 LINESEP = '\n\r'
@@ -33,20 +35,26 b' import re'
33 35
34 36 # FIXME: Need to provide an API for non user-generated display on the
35 37 # screen: this should not be editable by the user.
38 #-------------------------------------------------------------------------------
39 # Constants
40 #-------------------------------------------------------------------------------
41 _COMPLETE_BUFFER_MARKER = 31
42 _ERROR_MARKER = 30
43 _INPUT_MARKER = 29
36 44
37 45 _DEFAULT_SIZE = 10
38 46 if sys.platform == 'darwin':
39 47 _DEFAULT_SIZE = 12
40 48
41 49 _DEFAULT_STYLE = {
42 'stdout' : 'fore:#0000FF',
43 'stderr' : 'fore:#007f00',
44 'trace' : 'fore:#FF0000',
45
50 #background definition
46 51 'default' : 'size:%d' % _DEFAULT_SIZE,
47 52 'bracegood' : 'fore:#00AA00,back:#000000,bold',
48 53 'bracebad' : 'fore:#FF0000,back:#000000,bold',
49 54
55 # Edge column: a number of None
56 'edge_column' : -1,
57
50 58 # properties for the various Python lexer styles
51 59 'comment' : 'fore:#007F00',
52 60 'number' : 'fore:#007F7F',
@@ -57,7 +65,24 b' _DEFAULT_STYLE = {'
57 65 'tripledouble' : 'fore:#7F0000',
58 66 'class' : 'fore:#0000FF,bold,underline',
59 67 'def' : 'fore:#007F7F,bold',
60 'operator' : 'bold'
68 'operator' : 'bold',
69
70 # Default colors
71 'trace' : '#FAFAF1', # Nice green
72 'stdout' : '#FDFFD3', # Nice yellow
73 'stderr' : '#FFF1F1', # Nice red
74
75 # Default scintilla settings
76 'antialiasing' : True,
77 'carret_color' : 'BLACK',
78 'background_color' :'WHITE',
79
80 #prompt definition
81 'prompt_in1' : \
82 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02',
83
84 'prompt_out': \
85 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02',
61 86 }
62 87
63 88 # new style numbers
@@ -69,6 +94,47 b' _TRACE_STYLE = 17'
69 94 # system colors
70 95 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
71 96
97 # Translation table from ANSI escape sequences to color.
98 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
99 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
100 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
101 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
102 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
103 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
104 '1;34': [12, 'LIGHT BLUE'], '1;35':
105 [13, 'MEDIUM VIOLET RED'],
106 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
107
108 # XXX: Maybe one day we should factor this code with ColorANSI. Right now
109 # ColorANSI is hard to reuse and makes our code more complex.
110
111 #we define platform specific fonts
112 if wx.Platform == '__WXMSW__':
113 FACES = { 'times': 'Times New Roman',
114 'mono' : 'Courier New',
115 'helv' : 'Arial',
116 'other': 'Comic Sans MS',
117 'size' : 10,
118 'size2': 8,
119 }
120 elif wx.Platform == '__WXMAC__':
121 FACES = { 'times': 'Times New Roman',
122 'mono' : 'Monaco',
123 'helv' : 'Arial',
124 'other': 'Comic Sans MS',
125 'size' : 10,
126 'size2': 8,
127 }
128 else:
129 FACES = { 'times': 'Times',
130 'mono' : 'Courier',
131 'helv' : 'Helvetica',
132 'other': 'new century schoolbook',
133 'size' : 10,
134 'size2': 8,
135 }
136
137
72 138 #-------------------------------------------------------------------------------
73 139 # The console widget class
74 140 #-------------------------------------------------------------------------------
@@ -83,6 +149,9 b' class ConsoleWidget(editwindow.EditWindow):'
83 149 # stored.
84 150 title = 'Console'
85 151
152 # Last prompt printed
153 last_prompt = ''
154
86 155 # The buffer being edited.
87 156 def _set_input_buffer(self, string):
88 157 self.SetSelection(self.current_prompt_pos, self.GetLength())
@@ -103,19 +172,11 b' class ConsoleWidget(editwindow.EditWindow):'
103 172
104 173 # Translation table from ANSI escape sequences to color. Override
105 174 # this to specify your colors.
106 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
107 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
108 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
109 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
110 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
111 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
112 '1;34': [12, 'LIGHT BLUE'], '1;35':
113 [13, 'MEDIUM VIOLET RED'],
114 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
115
116 # The color of the carret (call _apply_style() after setting)
117 carret_color = 'BLACK'
175 ANSI_STYLES = ANSI_STYLES.copy()
118 176
177 # Font faces
178 faces = FACES.copy()
179
119 180 # Store the last time a refresh was done
120 181 _last_refresh_time = 0
121 182
@@ -126,7 +187,11 b' class ConsoleWidget(editwindow.EditWindow):'
126 187 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
127 188 size=wx.DefaultSize, style=wx.WANTS_CHARS, ):
128 189 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
129 self._configure_scintilla()
190 self.configure_scintilla()
191 # Track if 'enter' key as ever been processed
192 # This variable will only be reallowed until key goes up
193 self.enter_catched = False
194 self.current_prompt_pos = 0
130 195
131 196 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
132 197 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
@@ -193,8 +258,19 b' class ConsoleWidget(editwindow.EditWindow):'
193 258 self.current_prompt_pos = self.GetLength()
194 259 self.current_prompt_line = self.GetCurrentLine()
195 260 self.EnsureCaretVisible()
261 self.last_prompt = prompt
196 262
197 263
264 def continuation_prompt(self):
265 """ Returns the current continuation prompt.
266 We need to implement this method here to deal with the
267 ascii escape sequences cleaning up.
268 """
269 # ASCII-less prompt
270 ascii_less = ''.join(self.color_pat.split(self.last_prompt)[2::2])
271 return "."*(len(ascii_less)-2) + ': '
272
273
198 274 def scroll_to_bottom(self):
199 275 maxrange = self.GetScrollRange(wx.VERTICAL)
200 276 self.ScrollLines(maxrange)
@@ -216,37 +292,24 b' class ConsoleWidget(editwindow.EditWindow):'
216 292 """
217 293 return self.GetSize()[0]/self.GetCharWidth()
218 294
219 #--------------------------------------------------------------------------
220 # EditWindow API
221 #--------------------------------------------------------------------------
222 295
223 def OnUpdateUI(self, event):
224 """ Override the OnUpdateUI of the EditWindow class, to prevent
225 syntax highlighting both for faster redraw, and for more
226 consistent look and feel.
296 def configure_scintilla(self):
297 """ Set up all the styling option of the embedded scintilla
298 widget.
227 299 """
300 p = self.style.copy()
301
302 # Marker for complete buffer.
303 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
304 background=p['trace'])
228 305
229 #--------------------------------------------------------------------------
230 # Private API
231 #--------------------------------------------------------------------------
232
233 def _apply_style(self):
234 """ Applies the colors for the different text elements and the
235 carret.
236 """
237 self.SetCaretForeground(self.carret_color)
238
239 #self.StyleClearAll()
240 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
241 "fore:#FF0000,back:#0000FF,bold")
242 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
243 "fore:#000000,back:#FF0000,bold")
244
245 for style in self.ANSI_STYLES.values():
246 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
306 # Marker for current input buffer.
307 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
308 background=p['stdout'])
309 # Marker for tracebacks.
310 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
311 background=p['stderr'])
247 312
248
249 def _configure_scintilla(self):
250 313 self.SetEOLMode(stc.STC_EOL_LF)
251 314
252 315 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
@@ -268,7 +331,9 b' class ConsoleWidget(editwindow.EditWindow):'
268 331 self.SetWrapMode(stc.STC_WRAP_CHAR)
269 332 self.SetWrapMode(stc.STC_WRAP_WORD)
270 333 self.SetBufferedDraw(True)
271 self.SetUseAntiAliasing(True)
334
335 self.SetUseAntiAliasing(p['antialiasing'])
336
272 337 self.SetLayoutCache(stc.STC_CACHE_PAGE)
273 338 self.SetUndoCollection(False)
274 339 self.SetUseTabs(True)
@@ -289,23 +354,48 b' class ConsoleWidget(editwindow.EditWindow):'
289 354 self.SetMarginWidth(1, 0)
290 355 self.SetMarginWidth(2, 0)
291 356
292 self._apply_style()
293
294 357 # Xterm escape sequences
295 358 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
296 359 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
297 360
298 #self.SetEdgeMode(stc.STC_EDGE_LINE)
299 #self.SetEdgeColumn(80)
300
301 361 # styles
302 p = self.style
303 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
362
363 self.SetCaretForeground(p['carret_color'])
364
365 background_color = p['background_color']
366
367 if 'default' in p:
368 if 'back' not in p['default']:
369 p['default'] += ',back:%s' % background_color
370 if 'size' not in p['default']:
371 p['default'] += ',size:%s' % self.faces['size']
372 if 'face' not in p['default']:
373 p['default'] += ',face:%s' % self.faces['mono']
374
375 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
376 else:
377 self.StyleSetSpec(stc.STC_STYLE_DEFAULT,
378 "fore:%s,back:%s,size:%d,face:%s"
379 % (self.ANSI_STYLES['0;30'][1],
380 background_color,
381 self.faces['size'], self.faces['mono']))
382
304 383 self.StyleClearAll()
384
385 # XXX: two lines below are usefull if not using the lexer
386 #for style in self.ANSI_STYLES.values():
387 # self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
388
389 # prompt definition
390 self.prompt_in1 = p['prompt_in1']
391 self.prompt_out = p['prompt_out']
392
393 self.output_prompt_template = string.Template(self.prompt_out)
394 self.input_prompt_template = string.Template(self.prompt_in1)
395
305 396 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
306 397 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
307 398 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
308
309 399 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
310 400 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
311 401 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
@@ -321,6 +411,28 b' class ConsoleWidget(editwindow.EditWindow):'
321 411 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
322 412 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
323 413
414 edge_column = p['edge_column']
415 if edge_column is not None and edge_column > 0:
416 #we add a vertical line to console widget
417 self.SetEdgeMode(stc.STC_EDGE_LINE)
418 self.SetEdgeColumn(edge_column)
419
420
421 #--------------------------------------------------------------------------
422 # EditWindow API
423 #--------------------------------------------------------------------------
424
425 def OnUpdateUI(self, event):
426 """ Override the OnUpdateUI of the EditWindow class, to prevent
427 syntax highlighting both for faster redraw, and for more
428 consistent look and feel.
429 """
430
431
432 #--------------------------------------------------------------------------
433 # Private API
434 #--------------------------------------------------------------------------
435
324 436 def _on_key_down(self, event, skip=True):
325 437 """ Key press callback used for correcting behavior for
326 438 console-like interfaces: the cursor is constraint to be after
@@ -329,6 +441,11 b' class ConsoleWidget(editwindow.EditWindow):'
329 441 Return True if event as been catched.
330 442 """
331 443 catched = True
444 # XXX: Would the right way to do this be to have a
445 # dictionary at the instance level associating keys with
446 # callbacks? How would we deal with inheritance? And Do the
447 # different callbacks share local variables?
448
332 449 # Intercept some specific keys.
333 450 if event.KeyCode == ord('L') and event.ControlDown() :
334 451 self.scroll_to_bottom()
@@ -346,6 +463,10 b' class ConsoleWidget(editwindow.EditWindow):'
346 463 self.ScrollPages(-1)
347 464 elif event.KeyCode == wx.WXK_PAGEDOWN:
348 465 self.ScrollPages(1)
466 elif event.KeyCode == wx.WXK_HOME:
467 self.GotoPos(self.GetLength())
468 elif event.KeyCode == wx.WXK_END:
469 self.GotoPos(self.GetLength())
349 470 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
350 471 self.ScrollLines(-1)
351 472 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
@@ -357,16 +478,20 b' class ConsoleWidget(editwindow.EditWindow):'
357 478 event.Skip()
358 479 else:
359 480 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
360 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
481 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN,
482 wx.MOD_SHIFT):
361 483 catched = True
362 self.CallTipCancel()
363 self.write('\n', refresh=False)
364 # Under windows scintilla seems to be doing funny stuff to the
365 # line returns here, but the getter for input_buffer filters
366 # this out.
367 if sys.platform == 'win32':
368 self.input_buffer = self.input_buffer
369 self._on_enter()
484 if not self.enter_catched:
485 self.CallTipCancel()
486 if event.Modifiers == wx.MOD_SHIFT:
487 # Try to force execution
488 self.GotoPos(self.GetLength())
489 self.write('\n' + self.continuation_prompt(),
490 refresh=False)
491 self._on_enter()
492 else:
493 self._on_enter()
494 self.enter_catched = True
370 495
371 496 elif event.KeyCode == wx.WXK_HOME:
372 497 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
@@ -391,16 +516,28 b' class ConsoleWidget(editwindow.EditWindow):'
391 516 catched = True
392 517
393 518 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
394 if self.GetCurrentPos() > self.current_prompt_pos:
519 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
520 event.Skip()
521 catched = True
522
523 elif event.KeyCode == wx.WXK_RIGHT:
524 if not self._keep_cursor_in_buffer(self.GetCurrentPos() + 1):
525 event.Skip()
526 catched = True
527
528
529 elif event.KeyCode == wx.WXK_DELETE:
530 if not self._keep_cursor_in_buffer(self.GetCurrentPos() - 1):
395 531 event.Skip()
396 532 catched = True
397 533
398 534 if skip and not catched:
399 535 # Put the cursor back in the edit region
400 if self.GetCurrentPos() < self.current_prompt_pos:
401 self.GotoPos(self.current_prompt_pos)
402 else:
403 event.Skip()
536 if not self._keep_cursor_in_buffer():
537 if not (self.GetCurrentPos() == self.GetLength()
538 and event.KeyCode == wx.WXK_DELETE):
539 event.Skip()
540 catched = True
404 541
405 542 return catched
406 543
@@ -408,17 +545,69 b' class ConsoleWidget(editwindow.EditWindow):'
408 545 def _on_key_up(self, event, skip=True):
409 546 """ If cursor is outside the editing region, put it back.
410 547 """
411 event.Skip()
412 if self.GetCurrentPos() < self.current_prompt_pos:
413 self.GotoPos(self.current_prompt_pos)
548 if skip:
549 event.Skip()
550 self._keep_cursor_in_buffer()
551
552
553 # XXX: I need to avoid the problem of having an empty glass;
554 def _keep_cursor_in_buffer(self, pos=None):
555 """ Checks if the cursor is where it is allowed to be. If not,
556 put it back.
414 557
558 Returns
559 -------
560 cursor_moved: Boolean
561 whether or not the cursor was moved by this routine.
562
563 Notes
564 ------
565 WARNING: This does proper checks only for horizontal
566 movements.
567 """
568 if pos is None:
569 current_pos = self.GetCurrentPos()
570 else:
571 current_pos = pos
572 if current_pos < self.current_prompt_pos:
573 self.GotoPos(self.current_prompt_pos)
574 return True
575 line_num = self.LineFromPosition(current_pos)
576 if not current_pos > self.GetLength():
577 line_pos = self.GetColumn(current_pos)
578 else:
579 line_pos = self.GetColumn(self.GetLength())
580 line = self.GetLine(line_num)
581 # Jump the continuation prompt
582 continuation_prompt = self.continuation_prompt()
583 if ( line.startswith(continuation_prompt)
584 and line_pos < len(continuation_prompt)):
585 if line_pos < 2:
586 # We are at the beginning of the line, trying to move
587 # forward: jump forward.
588 self.GotoPos(current_pos + 1 +
589 len(continuation_prompt) - line_pos)
590 else:
591 # Jump back up
592 self.GotoPos(self.GetLineEndPosition(line_num-1))
593 return True
594 elif ( current_pos > self.GetLineEndPosition(line_num)
595 and not current_pos == self.GetLength()):
596 # Jump to next line
597 self.GotoPos(current_pos + 1 +
598 len(continuation_prompt))
599 return True
600
601 # We re-allow enter event processing
602 self.enter_catched = False
603 return False
415 604
416 605
417 606 if __name__ == '__main__':
418 607 # Some simple code to test the console widget.
419 608 class MainWindow(wx.Frame):
420 609 def __init__(self, parent, id, title):
421 wx.Frame.__init__(self, parent, id, title, size=(300,250))
610 wx.Frame.__init__(self, parent, id, title, size=(300, 250))
422 611 self._sizer = wx.BoxSizer(wx.VERTICAL)
423 612 self.console_widget = ConsoleWidget(self)
424 613 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
@@ -80,6 +80,15 b' class IPythonX(wx.Frame):'
80 80 self.SetSizer(self._sizer)
81 81 self.SetAutoLayout(1)
82 82 self.Show(True)
83 wx.EVT_CLOSE(self, self.on_close)
84
85
86 def on_close(self, event):
87 """ Called on closing the windows.
88
89 Stops the event loop, to close all the child windows.
90 """
91 wx.CallAfter(wx.Exit)
83 92
84 93
85 94 def main():
@@ -25,38 +25,19 b' __docformat__ = "restructuredtext en"'
25 25 # Major library imports
26 26 import re
27 27 import __builtin__
28 from time import sleep
29 28 import sys
30 29 from threading import Lock
31 import string
32 30
33 31 import wx
34 32 from wx import stc
35 33
36 34 # Ipython-specific imports.
37 from IPython.frontend._process import PipedProcess
38 from console_widget import ConsoleWidget
35 from IPython.frontend.process import PipedProcess
36 from console_widget import ConsoleWidget, _COMPLETE_BUFFER_MARKER, \
37 _ERROR_MARKER, _INPUT_MARKER
39 38 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40 39
41 40 #-------------------------------------------------------------------------------
42 # Constants
43 #-------------------------------------------------------------------------------
44
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 _ERROR_BG = '#FFF1F1' # Nice red
48
49 _COMPLETE_BUFFER_MARKER = 31
50 _ERROR_MARKER = 30
51 _INPUT_MARKER = 29
52
53 prompt_in1 = \
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55
56 prompt_out = \
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58
59 #-------------------------------------------------------------------------------
60 41 # Classes to implement the Wx frontend
61 42 #-------------------------------------------------------------------------------
62 43 class WxController(ConsoleWidget, PrefilterFrontEnd):
@@ -66,11 +47,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
66 47 This class inherits from ConsoleWidget, that provides a console-like
67 48 widget to provide a text-rendering widget suitable for a terminal.
68 49 """
69
70 output_prompt_template = string.Template(prompt_out)
71
72 input_prompt_template = string.Template(prompt_in1)
73
50
74 51 # Print debug info on what is happening to the console.
75 52 debug = False
76 53
@@ -138,25 +115,24 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
138 115 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 116 size=wx.DefaultSize,
140 117 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
118 styledef=None,
141 119 *args, **kwds):
142 120 """ Create Shell instance.
121
122 Parameters
123 -----------
124 styledef : dict, optional
125 styledef is the dictionary of options used to define the
126 style.
143 127 """
128 if styledef is not None:
129 self.style = styledef
144 130 ConsoleWidget.__init__(self, parent, id, pos, size, style)
145 131 PrefilterFrontEnd.__init__(self, **kwds)
146 132
147 133 # Stick in our own raw_input:
148 134 self.ipython0.raw_input = self.raw_input
149 135
150 # Marker for complete buffer.
151 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
152 background=_COMPLETE_BUFFER_BG)
153 # Marker for current input buffer.
154 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
155 background=_INPUT_BUFFER_BG)
156 # Marker for tracebacks.
157 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
158 background=_ERROR_BG)
159
160 136 # A time for flushing the write buffer
161 137 BUFFER_FLUSH_TIMER_ID = 100
162 138 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
@@ -171,8 +147,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
171 147 self.shell.user_ns['self'] = self
172 148 # Inject our own raw_input in namespace
173 149 self.shell.user_ns['raw_input'] = self.raw_input
174
175
150
176 151 def raw_input(self, prompt=''):
177 152 """ A replacement from python's raw_input.
178 153 """
@@ -251,11 +226,8 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
251 226 if (self.AutoCompActive() and line and not line[-1] == '.') \
252 227 or create==True:
253 228 suggestion, completions = self.complete(line)
254 offset=0
255 229 if completions:
256 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
257 residual = complete_sep.split(line)[-1]
258 offset = len(residual)
230 offset = len(self._get_completion_text(line))
259 231 self.pop_completion(completions, offset=offset)
260 232 if self.debug:
261 233 print >>sys.__stdout__, completions
@@ -276,6 +248,14 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
276 248 milliseconds=100, oneShot=True)
277 249
278 250
251 def clear_screen(self):
252 """ Empty completely the widget.
253 """
254 self.ClearAll()
255 self.new_prompt(self.input_prompt_template.substitute(
256 number=(self.last_result['number'] + 1)))
257
258
279 259 #--------------------------------------------------------------------------
280 260 # LineFrontEnd interface
281 261 #--------------------------------------------------------------------------
@@ -299,6 +279,41 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
299 279 raw_string=raw_string)
300 280 wx.CallAfter(callback)
301 281
282
283 def execute_command(self, command, hidden=False):
284 """ Execute a command, not only in the model, but also in the
285 view.
286 """
287 # XXX: This method needs to be integrated in the base fronted
288 # interface
289 if hidden:
290 return self.shell.execute(command)
291 else:
292 # XXX: we are not storing the input buffer previous to the
293 # execution, as this forces us to run the execution
294 # input_buffer a yield, which is not good.
295 ##current_buffer = self.shell.control.input_buffer
296 command = command.rstrip()
297 if len(command.split('\n')) > 1:
298 # The input command is several lines long, we need to
299 # force the execution to happen
300 command += '\n'
301 cleaned_command = self.prefilter_input(command)
302 self.input_buffer = command
303 # Do not use wx.Yield() (aka GUI.process_events()) to avoid
304 # recursive yields.
305 self.ProcessEvent(wx.PaintEvent())
306 self.write('\n')
307 if not self.is_complete(cleaned_command + '\n'):
308 self._colorize_input_buffer()
309 self.render_error('Incomplete or invalid input')
310 self.new_prompt(self.input_prompt_template.substitute(
311 number=(self.last_result['number'] + 1)))
312 return False
313 self._on_enter()
314 return True
315
316
302 317 def save_output_hooks(self):
303 318 self.__old_raw_input = __builtin__.raw_input
304 319 PrefilterFrontEnd.save_output_hooks(self)
@@ -356,10 +371,16 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
356 371 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
357 372
358 373
374 def continuation_prompt(self, *args, **kwargs):
375 # Avoid multiple inheritence, be explicit about which
376 # parent method class gets called
377 return ConsoleWidget.continuation_prompt(self, *args, **kwargs)
378
379
359 380 def write(self, *args, **kwargs):
360 381 # Avoid multiple inheritence, be explicit about which
361 382 # parent method class gets called
362 ConsoleWidget.write(self, *args, **kwargs)
383 return ConsoleWidget.write(self, *args, **kwargs)
363 384
364 385
365 386 def _on_key_down(self, event, skip=True):
@@ -367,7 +388,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
367 388 widget handle them, and put our logic afterward.
368 389 """
369 390 # FIXME: This method needs to be broken down in smaller ones.
370 current_line_number = self.GetCurrentLine()
391 current_line_num = self.GetCurrentLine()
371 392 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
372 393 # Capture Control-C
373 394 if self._input_state == 'subprocess':
@@ -413,7 +434,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
413 434 else:
414 435 # Up history
415 436 if event.KeyCode == wx.WXK_UP and (
416 ( current_line_number == self.current_prompt_line and
437 ( current_line_num == self.current_prompt_line and
417 438 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
418 439 or event.ControlDown() ):
419 440 new_buffer = self.get_history_previous(
@@ -425,7 +446,7 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
425 446 self.GotoPos(self.current_prompt_pos)
426 447 # Down history
427 448 elif event.KeyCode == wx.WXK_DOWN and (
428 ( current_line_number == self.LineCount -1 and
449 ( current_line_num == self.LineCount -1 and
429 450 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
430 451 or event.ControlDown() ):
431 452 new_buffer = self.get_history_next()
@@ -433,15 +454,43 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
433 454 self.input_buffer = new_buffer
434 455 # Tab-completion
435 456 elif event.KeyCode == ord('\t'):
436 current_line, current_line_number = self.CurLine
457 current_line, current_line_num = self.CurLine
437 458 if not re.match(r'^\s*$', current_line):
438 459 self.complete_current_input()
439 460 if self.AutoCompActive():
440 461 wx.CallAfter(self._popup_completion, create=True)
441 462 else:
442 463 event.Skip()
464 elif event.KeyCode == wx.WXK_BACK:
465 # If characters where erased, check if we have to
466 # remove a line.
467 # XXX: What about DEL?
468 # FIXME: This logics should be in ConsoleWidget, as it is
469 # independant of IPython
470 current_line, _ = self.CurLine
471 current_pos = self.GetCurrentPos()
472 current_line_num = self.LineFromPosition(current_pos)
473 current_col = self.GetColumn(current_pos)
474 len_prompt = len(self.continuation_prompt())
475 if ( current_line.startswith(self.continuation_prompt())
476 and current_col == len_prompt):
477 new_lines = []
478 for line_num, line in enumerate(
479 self.input_buffer.split('\n')):
480 if (line_num + self.current_prompt_line ==
481 current_line_num):
482 new_lines.append(line[len_prompt:])
483 else:
484 new_lines.append('\n'+line)
485 # The first character is '\n', due to the above
486 # code:
487 self.input_buffer = ''.join(new_lines)[1:]
488 self.GotoPos(current_pos - 1 - len_prompt)
489 else:
490 ConsoleWidget._on_key_down(self, event, skip=skip)
443 491 else:
444 492 ConsoleWidget._on_key_down(self, event, skip=skip)
493
445 494
446 495
447 496 def _on_key_up(self, event, skip=True):
@@ -453,14 +502,40 b' class WxController(ConsoleWidget, PrefilterFrontEnd):'
453 502 wx.CallAfter(self._popup_completion, create=True)
454 503 else:
455 504 ConsoleWidget._on_key_up(self, event, skip=skip)
505 # Make sure the continuation_prompts are always followed by a
506 # whitespace
507 new_lines = []
508 if self._input_state == 'readline':
509 position = self.GetCurrentPos()
510 continuation_prompt = self.continuation_prompt()[:-1]
511 for line in self.input_buffer.split('\n'):
512 if not line == continuation_prompt:
513 new_lines.append(line)
514 self.input_buffer = '\n'.join(new_lines)
515 self.GotoPos(position)
456 516
457 517
458 518 def _on_enter(self):
459 519 """ Called on return key down, in readline input_state.
460 520 """
521 last_line_num = self.LineFromPosition(self.GetLength())
522 current_line_num = self.LineFromPosition(self.GetCurrentPos())
523 new_line_pos = (last_line_num - current_line_num)
461 524 if self.debug:
462 525 print >>sys.__stdout__, repr(self.input_buffer)
463 PrefilterFrontEnd._on_enter(self)
526 self.write('\n', refresh=False)
527 # Under windows scintilla seems to be doing funny
528 # stuff to the line returns here, but the getter for
529 # input_buffer filters this out.
530 if sys.platform == 'win32':
531 self.input_buffer = self.input_buffer
532 old_prompt_num = self.current_prompt_pos
533 has_executed = PrefilterFrontEnd._on_enter(self,
534 new_line_pos=new_line_pos)
535 if old_prompt_num == self.current_prompt_pos:
536 # No execution has happened
537 self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
538 return has_executed
464 539
465 540
466 541 #--------------------------------------------------------------------------
@@ -1007,7 +1007,17 b' def get_security_dir():'
1007 1007 else:
1008 1008 os.chmod(security_dir, 0700)
1009 1009 return security_dir
1010
1010
1011 def get_log_dir():
1012 """Get the IPython log directory.
1013
1014 If the log directory does not exist, it is created.
1015 """
1016 log_dir = os.path.join(get_ipython_dir(), 'log')
1017 if not os.path.isdir(log_dir):
1018 os.mkdir(log_dir, 0777)
1019 return log_dir
1020
1011 1021 #****************************************************************************
1012 1022 # strings and text
1013 1023
@@ -54,7 +54,7 b' from pprint import pprint, pformat'
54 54 from IPython import Debugger,OInspect,PyColorize,ultraTB
55 55 from IPython.ColorANSI import ColorScheme,ColorSchemeTable # too long names
56 56 from IPython.Extensions import pickleshare
57 from IPython.FakeModule import FakeModule
57 from IPython.FakeModule import FakeModule, init_fakemod_dict
58 58 from IPython.Itpl import Itpl,itpl,printpl,ItplNS,itplns
59 59 from IPython.Logger import Logger
60 60 from IPython.Magic import Magic
@@ -108,6 +108,201 b' def softspace(file, newvalue):'
108 108 return oldvalue
109 109
110 110
111 def user_setup(ipythondir,rc_suffix,mode='install',interactive=True):
112 """Install or upgrade the user configuration directory.
113
114 Can be called when running for the first time or to upgrade the user's
115 .ipython/ directory.
116
117 Parameters
118 ----------
119 ipythondir : path
120 The directory to be used for installation/upgrade. In 'install' mode,
121 if this path already exists, the function exits immediately.
122
123 rc_suffix : str
124 Extension for the config files. On *nix platforms it is typically the
125 empty string, while Windows normally uses '.ini'.
126
127 mode : str, optional
128 Valid modes are 'install' and 'upgrade'.
129
130 interactive : bool, optional
131 If False, do not wait for user input on any errors. Normally after
132 printing its status information, this function waits for the user to
133 hit Return before proceeding. This is because the default use case is
134 when first installing the IPython configuration, so we want the user to
135 acknowledge the initial message, which contains some useful
136 information.
137 """
138
139 # For automatic use, deactivate all i/o
140 if interactive:
141 def wait():
142 try:
143 raw_input("Please press <RETURN> to start IPython.")
144 except EOFError:
145 print >> Term.cout
146 print '*'*70
147
148 def printf(s):
149 print s
150 else:
151 wait = lambda : None
152 printf = lambda s : None
153
154 # Install mode should be re-entrant: if the install dir already exists,
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.
160 if mode == 'install' and os.path.isdir(ipythondir):
161 return
162
163 cwd = os.getcwd() # remember where we started
164 glb = glob.glob
165
166 printf('*'*70)
167 if mode == 'install':
168 printf(
169 """Welcome to IPython. I will try to create a personal configuration directory
170 where you can customize many aspects of IPython's functionality in:\n""")
171 else:
172 printf('I am going to upgrade your configuration in:')
173
174 printf(ipythondir)
175
176 rcdirend = os.path.join('IPython','UserConfig')
177 cfg = lambda d: os.path.join(d,rcdirend)
178 try:
179 rcdir = filter(os.path.isdir,map(cfg,sys.path))[0]
180 printf("Initializing from configuration: %s" % rcdir)
181 except IndexError:
182 warning = """
183 Installation error. IPython's directory was not found.
184
185 Check the following:
186
187 The ipython/IPython directory should be in a directory belonging to your
188 PYTHONPATH environment variable (that is, it should be in a directory
189 belonging to sys.path). You can copy it explicitly there or just link to it.
190
191 IPython will create a minimal default configuration for you.
192
193 """
194 warn(warning)
195 wait()
196
197 if sys.platform =='win32':
198 inif = 'ipythonrc.ini'
199 else:
200 inif = 'ipythonrc'
201 minimal_setup = {'ipy_user_conf.py' : 'import ipy_defaults',
202 inif : '# intentionally left blank' }
203 os.makedirs(ipythondir, mode = 0777)
204 for f, cont in minimal_setup.items():
205 # In 2.5, this can be more cleanly done using 'with'
206 fobj = file(ipythondir + '/' + f,'w')
207 fobj.write(cont)
208 fobj.close()
209
210 return
211
212 if mode == 'install':
213 try:
214 shutil.copytree(rcdir,ipythondir)
215 os.chdir(ipythondir)
216 rc_files = glb("ipythonrc*")
217 for rc_file in rc_files:
218 os.rename(rc_file,rc_file+rc_suffix)
219 except:
220 warning = """
221
222 There was a problem with the installation:
223 %s
224 Try to correct it or contact the developers if you think it's a bug.
225 IPython will proceed with builtin defaults.""" % sys.exc_info()[1]
226 warn(warning)
227 wait()
228 return
229
230 elif mode == 'upgrade':
231 try:
232 os.chdir(ipythondir)
233 except:
234 printf("""
235 Can not upgrade: changing to directory %s failed. Details:
236 %s
237 """ % (ipythondir,sys.exc_info()[1]) )
238 wait()
239 return
240 else:
241 sources = glb(os.path.join(rcdir,'[A-Za-z]*'))
242 for new_full_path in sources:
243 new_filename = os.path.basename(new_full_path)
244 if new_filename.startswith('ipythonrc'):
245 new_filename = new_filename + rc_suffix
246 # The config directory should only contain files, skip any
247 # directories which may be there (like CVS)
248 if os.path.isdir(new_full_path):
249 continue
250 if os.path.exists(new_filename):
251 old_file = new_filename+'.old'
252 if os.path.exists(old_file):
253 os.remove(old_file)
254 os.rename(new_filename,old_file)
255 shutil.copy(new_full_path,new_filename)
256 else:
257 raise ValueError('unrecognized mode for install: %r' % mode)
258
259 # Fix line-endings to those native to each platform in the config
260 # directory.
261 try:
262 os.chdir(ipythondir)
263 except:
264 printf("""
265 Problem: changing to directory %s failed.
266 Details:
267 %s
268
269 Some configuration files may have incorrect line endings. This should not
270 cause any problems during execution. """ % (ipythondir,sys.exc_info()[1]) )
271 wait()
272 else:
273 for fname in glb('ipythonrc*'):
274 try:
275 native_line_ends(fname,backup=0)
276 except IOError:
277 pass
278
279 if mode == 'install':
280 printf("""
281 Successful installation!
282
283 Please read the sections 'Initial Configuration' and 'Quick Tips' in the
284 IPython manual (there are both HTML and PDF versions supplied with the
285 distribution) to make sure that your system environment is properly configured
286 to take advantage of IPython's features.
287
288 Important note: the configuration system has changed! The old system is
289 still in place, but its setting may be partly overridden by the settings in
290 "~/.ipython/ipy_user_conf.py" config file. Please take a look at the file
291 if some of the new settings bother you.
292
293 """)
294 else:
295 printf("""
296 Successful upgrade!
297
298 All files in your directory:
299 %(ipythondir)s
300 which would have been overwritten by the upgrade were backed up with a .old
301 extension. If you had made particular customizations in those files you may
302 want to merge them back into the new files.""" % locals() )
303 wait()
304 os.chdir(cwd)
305
111 306 #****************************************************************************
112 307 # Local use exceptions
113 308 class SpaceInInput(exceptions.Exception): pass
@@ -308,13 +503,24 b' class InteractiveShell(object,Magic):'
308 503 # calling functions defined in the script that use other things from
309 504 # the script will fail, because the function's closure had references
310 505 # to the original objects, which are now all None. So we must protect
311 # these modules from deletion by keeping a cache. To avoid keeping
312 # stale modules around (we only need the one from the last run), we use
313 # a dict keyed with the full path to the script, so only the last
314 # version of the module is held in the cache. The %reset command will
315 # flush this cache. See the cache_main_mod() and clear_main_mod_cache()
316 # methods for details on use.
317 self._user_main_modules = {}
506 # these modules from deletion by keeping a cache.
507 #
508 # To avoid keeping stale modules around (we only need the one from the
509 # last run), we use a dict keyed with the full path to the script, so
510 # only the last version of the module is held in the cache. Note,
511 # however, that we must cache the module *namespace contents* (their
512 # __dict__). Because if we try to cache the actual modules, old ones
513 # (uncached) could be destroyed while still holding references (such as
514 # those held by GUI objects that tend to be long-lived)>
515 #
516 # The %reset command will flush this cache. See the cache_main_mod()
517 # and clear_main_mod_cache() methods for details on use.
518
519 # This is the cache used for 'main' namespaces
520 self._main_ns_cache = {}
521 # And this is the single instance of FakeModule whose __dict__ we keep
522 # copying and clearing for reuse on each %run
523 self._user_main_module = FakeModule()
318 524
319 525 # A table holding all the namespaces IPython deals with, so that
320 526 # introspection facilities can search easily.
@@ -330,7 +536,7 b' class InteractiveShell(object,Magic):'
330 536 # a simple list.
331 537 self.ns_refs_table = [ user_ns, user_global_ns, self.user_config_ns,
332 538 self.alias_table, self.internal_ns,
333 self._user_main_modules ]
539 self._main_ns_cache ]
334 540
335 541 # We need to insert into sys.modules something that looks like a
336 542 # module but which accesses the IPython namespace, for shelve and
@@ -1114,156 +1320,11 b' class InteractiveShell(object,Magic):'
1114 1320 def user_setup(self,ipythondir,rc_suffix,mode='install'):
1115 1321 """Install the user configuration directory.
1116 1322
1117 Can be called when running for the first time or to upgrade the user's
1118 .ipython/ directory with the mode parameter. Valid modes are 'install'
1119 and 'upgrade'."""
1120
1121 def wait():
1122 try:
1123 raw_input("Please press <RETURN> to start IPython.")
1124 except EOFError:
1125 print >> Term.cout
1126 print '*'*70
1127
1128 cwd = os.getcwd() # remember where we started
1129 glb = glob.glob
1130 print '*'*70
1131 if mode == 'install':
1132 print \
1133 """Welcome to IPython. I will try to create a personal configuration directory
1134 where you can customize many aspects of IPython's functionality in:\n"""
1135 else:
1136 print 'I am going to upgrade your configuration in:'
1137
1138 print ipythondir
1139
1140 rcdirend = os.path.join('IPython','UserConfig')
1141 cfg = lambda d: os.path.join(d,rcdirend)
1142 try:
1143 rcdir = filter(os.path.isdir,map(cfg,sys.path))[0]
1144 print "Initializing from configuration",rcdir
1145 except IndexError:
1146 warning = """
1147 Installation error. IPython's directory was not found.
1148
1149 Check the following:
1150
1151 The ipython/IPython directory should be in a directory belonging to your
1152 PYTHONPATH environment variable (that is, it should be in a directory
1153 belonging to sys.path). You can copy it explicitly there or just link to it.
1154
1155 IPython will create a minimal default configuration for you.
1156
1157 """
1158 warn(warning)
1159 wait()
1160
1161 if sys.platform =='win32':
1162 inif = 'ipythonrc.ini'
1163 else:
1164 inif = 'ipythonrc'
1165 minimal_setup = {'ipy_user_conf.py' : 'import ipy_defaults',
1166 inif : '# intentionally left blank' }
1167 os.makedirs(ipythondir, mode = 0777)
1168 for f, cont in minimal_setup.items():
1169 open(ipythondir + '/' + f,'w').write(cont)
1170
1171 return
1172
1173 if mode == 'install':
1174 try:
1175 shutil.copytree(rcdir,ipythondir)
1176 os.chdir(ipythondir)
1177 rc_files = glb("ipythonrc*")
1178 for rc_file in rc_files:
1179 os.rename(rc_file,rc_file+rc_suffix)
1180 except:
1181 warning = """
1182
1183 There was a problem with the installation:
1184 %s
1185 Try to correct it or contact the developers if you think it's a bug.
1186 IPython will proceed with builtin defaults.""" % sys.exc_info()[1]
1187 warn(warning)
1188 wait()
1189 return
1190
1191 elif mode == 'upgrade':
1192 try:
1193 os.chdir(ipythondir)
1194 except:
1195 print """
1196 Can not upgrade: changing to directory %s failed. Details:
1197 %s
1198 """ % (ipythondir,sys.exc_info()[1])
1199 wait()
1200 return
1201 else:
1202 sources = glb(os.path.join(rcdir,'[A-Za-z]*'))
1203 for new_full_path in sources:
1204 new_filename = os.path.basename(new_full_path)
1205 if new_filename.startswith('ipythonrc'):
1206 new_filename = new_filename + rc_suffix
1207 # The config directory should only contain files, skip any
1208 # directories which may be there (like CVS)
1209 if os.path.isdir(new_full_path):
1210 continue
1211 if os.path.exists(new_filename):
1212 old_file = new_filename+'.old'
1213 if os.path.exists(old_file):
1214 os.remove(old_file)
1215 os.rename(new_filename,old_file)
1216 shutil.copy(new_full_path,new_filename)
1217 else:
1218 raise ValueError,'unrecognized mode for install:',`mode`
1219
1220 # Fix line-endings to those native to each platform in the config
1221 # directory.
1222 try:
1223 os.chdir(ipythondir)
1224 except:
1225 print """
1226 Problem: changing to directory %s failed.
1227 Details:
1228 %s
1229
1230 Some configuration files may have incorrect line endings. This should not
1231 cause any problems during execution. """ % (ipythondir,sys.exc_info()[1])
1232 wait()
1233 else:
1234 for fname in glb('ipythonrc*'):
1235 try:
1236 native_line_ends(fname,backup=0)
1237 except IOError:
1238 pass
1239
1240 if mode == 'install':
1241 print """
1242 Successful installation!
1243
1244 Please read the sections 'Initial Configuration' and 'Quick Tips' in the
1245 IPython manual (there are both HTML and PDF versions supplied with the
1246 distribution) to make sure that your system environment is properly configured
1247 to take advantage of IPython's features.
1248
1249 Important note: the configuration system has changed! The old system is
1250 still in place, but its setting may be partly overridden by the settings in
1251 "~/.ipython/ipy_user_conf.py" config file. Please take a look at the file
1252 if some of the new settings bother you.
1253
1254 """
1255 else:
1256 print """
1257 Successful upgrade!
1258
1259 All files in your directory:
1260 %(ipythondir)s
1261 which would have been overwritten by the upgrade were backed up with a .old
1262 extension. If you had made particular customizations in those files you may
1263 want to merge them back into the new files.""" % locals()
1264 wait()
1265 os.chdir(cwd)
1266 # end user_setup()
1323 Note
1324 ----
1325 DEPRECATED: use the top-level user_setup() function instead.
1326 """
1327 return user_setup(ipythondir,rc_suffix,mode)
1267 1328
1268 1329 def atexit_operations(self):
1269 1330 """This will be executed at the time of exit.
@@ -1417,8 +1478,9 b' want to merge them back into the new files.""" % locals()'
1417 1478 #print "loading rl:",rlcommand # dbg
1418 1479 readline.parse_and_bind(rlcommand)
1419 1480
1420 # remove some chars from the delimiters list
1421 delims = readline.get_completer_delims()
1481 # Remove some chars from the delimiters list. If we encounter
1482 # unicode chars, discard them.
1483 delims = readline.get_completer_delims().encode("ascii", "ignore")
1422 1484 delims = delims.translate(string._idmap,
1423 1485 self.rc.readline_remove_delims)
1424 1486 readline.set_completer_delims(delims)
@@ -1441,35 +1503,53 b' want to merge them back into the new files.""" % locals()'
1441 1503 return True
1442 1504 return ask_yes_no(prompt,default)
1443 1505
1444 def cache_main_mod(self,mod):
1445 """Cache a main module.
1506 def new_main_mod(self,ns=None):
1507 """Return a new 'main' module object for user code execution.
1508 """
1509 main_mod = self._user_main_module
1510 init_fakemod_dict(main_mod,ns)
1511 return main_mod
1512
1513 def cache_main_mod(self,ns,fname):
1514 """Cache a main module's namespace.
1446 1515
1447 When scripts are executed via %run, we must keep a reference to their
1448 __main__ module (a FakeModule instance) around so that Python doesn't
1449 clear it, rendering objects defined therein useless.
1516 When scripts are executed via %run, we must keep a reference to the
1517 namespace of their __main__ module (a FakeModule instance) around so
1518 that Python doesn't clear it, rendering objects defined therein
1519 useless.
1450 1520
1451 1521 This method keeps said reference in a private dict, keyed by the
1452 1522 absolute path of the module object (which corresponds to the script
1453 1523 path). This way, for multiple executions of the same script we only
1454 keep one copy of __main__ (the last one), thus preventing memory leaks
1455 from old references while allowing the objects from the last execution
1456 to be accessible.
1524 keep one copy of the namespace (the last one), thus preventing memory
1525 leaks from old references while allowing the objects from the last
1526 execution to be accessible.
1527
1528 Note: we can not allow the actual FakeModule instances to be deleted,
1529 because of how Python tears down modules (it hard-sets all their
1530 references to None without regard for reference counts). This method
1531 must therefore make a *copy* of the given namespace, to allow the
1532 original module's __dict__ to be cleared and reused.
1457 1533
1534
1458 1535 Parameters
1459 1536 ----------
1460 mod : a module object
1537 ns : a namespace (a dict, typically)
1538
1539 fname : str
1540 Filename associated with the namespace.
1461 1541
1462 1542 Examples
1463 1543 --------
1464 1544
1465 1545 In [10]: import IPython
1466 1546
1467 In [11]: _ip.IP.cache_main_mod(IPython)
1547 In [11]: _ip.IP.cache_main_mod(IPython.__dict__,IPython.__file__)
1468 1548
1469 In [12]: IPython.__file__ in _ip.IP._user_main_modules
1549 In [12]: IPython.__file__ in _ip.IP._main_ns_cache
1470 1550 Out[12]: True
1471 1551 """
1472 self._user_main_modules[os.path.abspath(mod.__file__) ] = mod
1552 self._main_ns_cache[os.path.abspath(fname)] = ns.copy()
1473 1553
1474 1554 def clear_main_mod_cache(self):
1475 1555 """Clear the cache of main modules.
@@ -1481,17 +1561,17 b' want to merge them back into the new files.""" % locals()'
1481 1561
1482 1562 In [15]: import IPython
1483 1563
1484 In [16]: _ip.IP.cache_main_mod(IPython)
1564 In [16]: _ip.IP.cache_main_mod(IPython.__dict__,IPython.__file__)
1485 1565
1486 In [17]: len(_ip.IP._user_main_modules) > 0
1566 In [17]: len(_ip.IP._main_ns_cache) > 0
1487 1567 Out[17]: True
1488 1568
1489 1569 In [18]: _ip.IP.clear_main_mod_cache()
1490 1570
1491 In [19]: len(_ip.IP._user_main_modules) == 0
1571 In [19]: len(_ip.IP._main_ns_cache) == 0
1492 1572 Out[19]: True
1493 1573 """
1494 self._user_main_modules.clear()
1574 self._main_ns_cache.clear()
1495 1575
1496 1576 def _should_recompile(self,e):
1497 1577 """Utility routine for edit_syntax_error"""
@@ -2129,7 +2209,7 b' want to merge them back into the new files.""" % locals()'
2129 2209
2130 2210 try:
2131 2211 code = self.compile(source,filename,symbol)
2132 except (OverflowError, SyntaxError, ValueError, TypeError):
2212 except (OverflowError, SyntaxError, ValueError, TypeError, MemoryError):
2133 2213 # Case 1
2134 2214 self.showsyntaxerror(filename)
2135 2215 return None
@@ -55,9 +55,9 b' from IPython.iplib import InteractiveShell'
55 55 from IPython.usage import cmd_line_usage,interactive_usage
56 56 from IPython.genutils import *
57 57
58 def force_import(modname):
59 if modname in sys.modules:
60 print "reload",modname
58 def force_import(modname,force_reload=False):
59 if modname in sys.modules and force_reload:
60 info("reloading: %s" % modname)
61 61 reload(sys.modules[modname])
62 62 else:
63 63 __import__(modname)
@@ -625,25 +625,25 b" object? -> Details about 'object'. ?object also works, ?? prints more."
625 625 except:
626 626 IP.InteractiveTB()
627 627 import_fail_info('ipy_system_conf')
628
628
629 629 # only import prof module if ipythonrc-PROF was not found
630 630 if opts_all.profile and not profile_handled_by_legacy:
631 631 profmodname = 'ipy_profile_' + opts_all.profile
632 632 try:
633
634 633 force_import(profmodname)
635 634 except:
636 635 IP.InteractiveTB()
637 print "Error importing",profmodname,"- perhaps you should run %upgrade?"
636 print "Error importing",profmodname,\
637 "- perhaps you should run %upgrade?"
638 638 import_fail_info(profmodname)
639 639 else:
640 640 opts.profile = opts_all.profile
641 641 else:
642 642 force_import('ipy_profile_none')
643 # XXX - this is wrong: ipy_user_conf should not be loaded unconditionally,
644 # since the user could have specified a config file path by hand.
643 645 try:
644
645 646 force_import('ipy_user_conf')
646
647 647 except:
648 648 conf = opts_all.ipythondir + "/ipy_user_conf.py"
649 649 IP.InteractiveTB()
@@ -15,10 +15,11 b' if they need blocking clients or in `asyncclient.py` if they want asynchronous,'
15 15 deferred/Twisted using clients.
16 16 """
17 17 __docformat__ = "restructuredtext en"
18 #-------------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 19 # Copyright (C) 2008 The IPython Development Team
20 20 #
21 21 # Distributed under the terms of the BSD License. The full license is in
22 22 # the file COPYING, distributed as part of this software.
23 #-------------------------------------------------------------------------------
24 No newline at end of file
23 #-----------------------------------------------------------------------------
24
25 from IPython.kernel.error import TaskRejectError No newline at end of file
@@ -15,6 +15,7 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 import os, sys
18 19 from os.path import join as pjoin
19 20
20 21 from IPython.external.configobj import ConfigObj
@@ -23,6 +24,7 b' from IPython.genutils import get_ipython_dir, get_security_dir'
23 24
24 25 default_kernel_config = ConfigObj()
25 26
27 # This will raise OSError if ipythondir doesn't exist.
26 28 security_dir = get_security_dir()
27 29
28 30 #-------------------------------------------------------------------------------
@@ -679,21 +679,22 b' class Interpreter(object):'
679 679 # to exec will fail however. There seems to be some inconsistency in
680 680 # how trailing whitespace is handled, but this seems to work.
681 681 python = python.strip()
682
682
683 683 # The compiler module does not like unicode. We need to convert
684 684 # it encode it:
685 685 if isinstance(python, unicode):
686 686 # Use the utf-8-sig BOM so the compiler detects this a UTF-8
687 687 # encode string.
688 688 python = '\xef\xbb\xbf' + python.encode('utf-8')
689
689
690 690 # The compiler module will parse the code into an abstract syntax tree.
691 # This has a bug with str("a\nb"), but not str("""a\nb""")!!!
691 692 ast = compiler.parse(python)
692
693
693 694 # Uncomment to help debug the ast tree
694 695 # for n in ast.node:
695 696 # print n.lineno,'->',n
696
697
697 698 # Each separate command is available by iterating over ast.node. The
698 699 # lineno attribute is the line number (1-indexed) beginning the commands
699 700 # suite.
@@ -703,20 +704,26 b' class Interpreter(object):'
703 704 # We might eventually discover other cases where lineno is None and have
704 705 # to put in a more sophisticated test.
705 706 linenos = [x.lineno-1 for x in ast.node if x.lineno is not None]
706
707
707 708 # When we finally get the slices, we will need to slice all the way to
708 709 # the end even though we don't have a line number for it. Fortunately,
709 710 # None does the job nicely.
710 711 linenos.append(None)
712
713 # Same problem at the other end: sometimes the ast tree has its
714 # first complete statement not starting on line 0. In this case
715 # we might miss part of it. This fixes ticket 266993. Thanks Gael!
716 linenos[0] = 0
717
711 718 lines = python.splitlines()
712
719
713 720 # Create a list of atomic commands.
714 721 cmds = []
715 722 for i, j in zip(linenos[:-1], linenos[1:]):
716 723 cmd = lines[i:j]
717 724 if cmd:
718 725 cmds.append('\n'.join(cmd)+'\n')
719
726
720 727 return cmds
721 728
722 729 def error(self, text):
@@ -15,6 +15,8 b' __docformat__ = "restructuredtext en"'
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 # Tell nose to skip the testing of this module
19 __test__ = {}
18 20
19 21 class NotificationCenter(object):
20 22 """Synchronous notification center
@@ -2,25 +2,61 b''
2 2
3 3 """This file contains unittests for the interpreter.py module."""
4 4
5 __docformat__ = "restructuredtext en"
6
7 5 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
6 # Copyright (C) 2008-2009 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is
9 # in the file COPYING, distributed as part of this software.
12 10 #-----------------------------------------------------------------------------
13
11
14 12 #-----------------------------------------------------------------------------
15 # Imports
13 # Imports
16 14 #-----------------------------------------------------------------------------
17 15
16 # Tell nose to skip this module
17 __test__ = {}
18
19 from twisted.trial import unittest
18 20 from IPython.kernel.core.interpreter import Interpreter
19 21
20 def test_unicode():
21 """ Test unicode handling with the interpreter.
22 """
23 i = Interpreter()
24 i.execute_python(u'print "ù"')
25 i.execute_python('print "ù"')
22 #-----------------------------------------------------------------------------
23 # Tests
24 #-----------------------------------------------------------------------------
25
26 class TestInterpreter(unittest.TestCase):
27
28 def test_unicode(self):
29 """ Test unicode handling with the interpreter."""
30 i = Interpreter()
31 i.execute_python(u'print "ù"')
32 i.execute_python('print "ù"')
33
34 def test_ticket266993(self):
35 """ Test for ticket 266993."""
36 i = Interpreter()
37 i.execute('str("""a\nb""")')
38
39 def test_ticket364347(self):
40 """Test for ticket 364347."""
41 i = Interpreter()
42 i.split_commands('str("a\\nb")')
43
44 def test_split_commands(self):
45 """ Test that commands are indeed individually split."""
46 i = Interpreter()
47 test_atoms = [('(1\n + 1)', ),
48 ('1', '1', ),
49 ]
50 for atoms in test_atoms:
51 atoms = [atom.rstrip() + '\n' for atom in atoms]
52 self.assertEquals(i.split_commands(''.join(atoms)),atoms)
53
54 def test_long_lines(self):
55 """ Test for spurious syntax error created by the interpreter."""
56 test_strings = [u'( 1 +\n 1\n )\n\n',
57 u'(1 \n + 1\n )\n\n',
58 ]
59 i = Interpreter()
60 for s in test_strings:
61 i.execute(s)
26 62
@@ -2,26 +2,26 b''
2 2
3 3 """This file contains unittests for the notification.py module."""
4 4
5 __docformat__ = "restructuredtext en"
6
7 5 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
9 #
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
6 # Copyright (C) 2008-2009 The IPython Development Team
7 #
8 # Distributed under the terms of the BSD License. The full license is
9 # in the file COPYING, distributed as part of this software.
12 10 #-----------------------------------------------------------------------------
13
11
14 12 #-----------------------------------------------------------------------------
15 # Imports
13 # Imports
16 14 #-----------------------------------------------------------------------------
17 15
18 import unittest
16 # Tell nose to skip this module
17 __test__ = {}
18
19 from twisted.trial import unittest
19 20 import IPython.kernel.core.notification as notification
20 from nose.tools import timed
21 21
22 #
23 # Supporting test classes
24 #
22 #-----------------------------------------------------------------------------
23 # Support Classes
24 #-----------------------------------------------------------------------------
25 25
26 26 class Observer(object):
27 27 """docstring for Observer"""
@@ -36,7 +36,6 b' class Observer(object):'
36 36 self.expectedType,
37 37 self.expectedSender)
38 38
39
40 39 def callback(self, theType, sender, args={}):
41 40 """callback"""
42 41
@@ -47,7 +46,6 b' class Observer(object):'
47 46 assert(args == self.expectedKwArgs)
48 47 self.recieved = True
49 48
50
51 49 def verify(self):
52 50 """verify"""
53 51
@@ -57,7 +55,6 b' class Observer(object):'
57 55 """reset"""
58 56
59 57 self.recieved = False
60
61 58
62 59
63 60 class Notifier(object):
@@ -72,11 +69,10 b' class Notifier(object):'
72 69
73 70 center.post_notification(self.theType, self,
74 71 **self.kwargs)
75
76 72
77 #
78 # Test Cases
79 #
73 #-----------------------------------------------------------------------------
74 # Tests
75 #-----------------------------------------------------------------------------
80 76
81 77 class NotificationTests(unittest.TestCase):
82 78 """docstring for NotificationTests"""
@@ -94,7 +90,6 b' class NotificationTests(unittest.TestCase):'
94 90
95 91 observer.verify()
96 92
97
98 93 def test_type_specificity(self):
99 94 """Test that observers are registered by type"""
100 95
@@ -109,7 +104,6 b' class NotificationTests(unittest.TestCase):'
109 104
110 105 observer.verify()
111 106
112
113 107 def test_sender_specificity(self):
114 108 """Test that observers are registered by sender"""
115 109
@@ -123,7 +117,6 b' class NotificationTests(unittest.TestCase):'
123 117
124 118 observer.verify()
125 119
126
127 120 def test_remove_all_observers(self):
128 121 """White-box test for remove_all_observers"""
129 122
@@ -136,8 +129,7 b' class NotificationTests(unittest.TestCase):'
136 129 notification.sharedCenter.remove_all_observers()
137 130
138 131 self.assert_(len(notification.sharedCenter.observers) == 0, "observers removed")
139
140
132
141 133 def test_any_sender(self):
142 134 """test_any_sender"""
143 135
@@ -153,9 +145,7 b' class NotificationTests(unittest.TestCase):'
153 145 observer.reset()
154 146 sender2.post()
155 147 observer.verify()
156
157
158 @timed(.01)
148
159 149 def test_post_performance(self):
160 150 """Test that post_notification, even with many registered irrelevant
161 151 observers is fast"""
@@ -168,4 +158,4 b' class NotificationTests(unittest.TestCase):'
168 158 notification.sharedCenter.post_notification('EXPECTED_TYPE', self)
169 159
170 160 o.verify()
171
161
@@ -2,69 +2,77 b''
2 2 """
3 3 Test the output capture at the OS level, using file descriptors.
4 4 """
5
6 __docformat__ = "restructuredtext en"
7
8 #-------------------------------------------------------------------------------
9 # Copyright (C) 2008 The IPython Development Team
5 #-----------------------------------------------------------------------------
6 # Copyright (C) 2008-2009 The IPython Development Team
10 7 #
11 8 # Distributed under the terms of the BSD License. The full license is
12 9 # in the file COPYING, distributed as part of this software.
13 #-------------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
14 11
12 #-----------------------------------------------------------------------------
13 # Imports
14 #-----------------------------------------------------------------------------
15
16 # Tell nose to skip this module
17 __test__ = {}
15 18
16 # Stdlib imports
17 import os
18 19 from cStringIO import StringIO
20 import os
21
22 from twisted.trial import unittest
19 23
20 # Our own imports
21 from IPython.testing import decorators as dec
24 from IPython.testing import decorators_trial as dec
22 25
23 26 #-----------------------------------------------------------------------------
24 # Test functions
27 # Tests
28 #-----------------------------------------------------------------------------
25 29
26 @dec.skip_win32
27 def test_redirector():
28 """ Checks that the redirector can be used to do synchronous capture.
29 """
30 from IPython.kernel.core.fd_redirector import FDRedirector
31 r = FDRedirector()
32 out = StringIO()
33 try:
34 r.start()
35 for i in range(10):
36 os.system('echo %ic' % i)
37 print >>out, r.getvalue(),
38 print >>out, i
39 except:
40 r.stop()
41 raise
42 r.stop()
43 result1 = out.getvalue()
44 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
45 assert result1 == result2
46 30
31 class TestRedirector(unittest.TestCase):
47 32
48 @dec.skip_win32
49 def test_redirector_output_trap():
50 """ This test check not only that the redirector_output_trap does
33 @dec.skip_win32
34 def test_redirector(self):
35 """Checks that the redirector can be used to do synchronous capture.
36 """
37 from IPython.kernel.core.fd_redirector import FDRedirector
38 r = FDRedirector()
39 out = StringIO()
40 try:
41 r.start()
42 for i in range(10):
43 os.system('echo %ic' % i)
44 print >>out, r.getvalue(),
45 print >>out, i
46 except:
47 r.stop()
48 raise
49 r.stop()
50 result1 = out.getvalue()
51 result2 = "".join("%ic\n%i\n" %(i, i) for i in range(10))
52 self.assertEquals(result1, result2)
53
54 @dec.skip_win32
55 def test_redirector_output_trap(self):
56 """Check the greedy trapping behavior of the traps.
57
58 This test check not only that the redirector_output_trap does
51 59 trap the output, but also that it does it in a gready way, that
52 60 is by calling the callback ASAP.
53 """
54 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
55 out = StringIO()
56 trap = RedirectorOutputTrap(out.write, out.write)
57 try:
58 trap.set()
59 for i in range(10):
60 os.system('echo %ic' % i)
61 print "%ip" % i
62 print >>out, i
63 except:
61 """
62 from IPython.kernel.core.redirector_output_trap import RedirectorOutputTrap
63 out = StringIO()
64 trap = RedirectorOutputTrap(out.write, out.write)
65 try:
66 trap.set()
67 for i in range(10):
68 os.system('echo %ic' % i)
69 print "%ip" % i
70 print >>out, i
71 except:
72 trap.unset()
73 raise
64 74 trap.unset()
65 raise
66 trap.unset()
67 result1 = out.getvalue()
68 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
69 assert result1 == result2
70
75 result1 = out.getvalue()
76 result2 = "".join("%ic\n%ip\n%i\n" %(i, i, i) for i in range(10))
77 self.assertEquals(result1, result2)
78
@@ -268,6 +268,8 b' def _formatTracebackLines(lnum, index, lines, Colors, lvals=None,scheme=None):'
268 268 # This lets us get fully syntax-highlighted tracebacks.
269 269 if scheme is None:
270 270 try:
271 # Again, reference to a global __IPYTHON__ that doesn't exist.
272 # XXX
271 273 scheme = __IPYTHON__.rc.colors
272 274 except:
273 275 scheme = DEFAULT_SCHEME
@@ -487,10 +489,14 b' class ListTB(TBTools):'
487 489 else:
488 490 list.append('%s\n' % str(stype))
489 491
490 # vds:>>
491 if have_filedata:
492 __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
493 # vds:<<
492 # This is being commented out for now as the __IPYTHON__ variable
493 # referenced here is not resolved and causes massive test failures
494 # and errors. B. Granger, 04/2009. XXX
495 # See https://bugs.launchpad.net/bugs/362137
496 # # vds:>>
497 # if have_filedata:
498 # __IPYTHON__.hooks.synchronize_with_editor(filename, lineno, 0)
499 # # vds:<<
494 500
495 501 return list
496 502
@@ -804,13 +810,17 b' class VerboseTB(TBTools):'
804 810 value = text_repr(getattr(evalue, name))
805 811 exception.append('\n%s%s = %s' % (indent, name, value))
806 812
807 # vds: >>
808 if records:
809 filepath, lnum = records[-1][1:3]
810 #print "file:", str(file), "linenb", str(lnum) # dbg
811 filepath = os.path.abspath(filepath)
812 __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
813 # vds: <<
813 # This is being commented out for now as the __IPYTHON__ variable
814 # referenced here is not resolved and causes massive test failures
815 # and errors. B. Granger, 04/2009. XXX
816 # See https://bugs.launchpad.net/bugs/362137
817 # # vds: >>
818 # if records:
819 # filepath, lnum = records[-1][1:3]
820 # #print "file:", str(file), "linenb", str(lnum) # dbg
821 # filepath = os.path.abspath(filepath)
822 # __IPYTHON__.hooks.synchronize_with_editor(filepath, lnum, 0)
823 # # vds: <<
814 824
815 825 # return all our info assembled as a single string
816 826 return '%s\n\n%s\n%s' % (head,'\n'.join(frames),''.join(exception[0]) )
@@ -67,10 +67,10 b' class EngineConnector(object):'
67 67 self.furl = find_furl(furl_or_file)
68 68 except ValueError:
69 69 return defer.fail(failure.Failure())
70 # return defer.fail(failure.Failure(ValueError('not a valid furl or furl file: %r' % furl_or_file)))
71 d = self.tub.getReference(self.furl)
72 d.addCallbacks(self._register, self._log_failure)
73 return d
70 else:
71 d = self.tub.getReference(self.furl)
72 d.addCallbacks(self._register, self._log_failure)
73 return d
74 74
75 75 def _log_failure(self, reason):
76 76 log.err('EngineConnector: engine registration failed:')
@@ -34,6 +34,9 b' __docformat__ = "restructuredtext en"'
34 34 # Imports
35 35 #-------------------------------------------------------------------------------
36 36
37 # Tell nose to skip the testing of this module
38 __test__ = {}
39
37 40 import os, sys, copy
38 41 import cPickle as pickle
39 42 from new import instancemethod
@@ -266,8 +269,8 b' class StrictDict(dict):'
266 269 pickle.dumps(key, 2)
267 270 pickle.dumps(value, 2)
268 271 newvalue = copy.deepcopy(value)
269 except:
270 raise error.InvalidProperty(value)
272 except Exception, e:
273 raise error.InvalidProperty("can't be a value: %r" % value)
271 274 dict.__setitem__(self, key, newvalue)
272 275 self.modified = True
273 276
@@ -104,6 +104,23 b' class StopLocalExecution(KernelError):'
104 104 class SecurityError(KernelError):
105 105 pass
106 106
107 class FileTimeoutError(KernelError):
108 pass
109
110 class TaskRejectError(KernelError):
111 """Exception to raise when a task should be rejected by an engine.
112
113 This exception can be used to allow a task running on an engine to test
114 if the engine (or the user's namespace on the engine) has the needed
115 task dependencies. If not, the task should raise this exception. For
116 the task to be retried on another engine, the task should be created
117 with the `retries` argument > 1.
118
119 The advantage of this approach over our older properties system is that
120 tasks have full access to the user's namespace on the engines and the
121 properties don't have to be managed or tested by the controller.
122 """
123
107 124 class CompositeError(KernelError):
108 125 def __init__(self, message, elist):
109 126 Exception.__init__(self, *(message, elist))
@@ -20,6 +20,7 b' import sys'
20 20 import cPickle as pickle
21 21 from types import FunctionType
22 22 import linecache
23 import warnings
23 24
24 25 from twisted.internet import reactor
25 26 from twisted.python import components, log
@@ -389,6 +390,14 b' def strip_whitespace(source):'
389 390 #-------------------------------------------------------------------------------
390 391
391 392
393 _prop_warn = """\
394
395 We are currently refactoring the task dependency system. This might
396 involve the removal of this method and other methods related to engine
397 properties. Please see the docstrings for IPython.kernel.TaskRejectError
398 for more information."""
399
400
392 401 class IFullBlockingMultiEngineClient(Interface):
393 402 pass
394 403
@@ -730,22 +739,27 b' class FullBlockingMultiEngineClient(InteractiveMultiEngineClient):'
730 739 return self._blockFromThread(self.smultiengine.queue_status, targets=targets, block=block)
731 740
732 741 def set_properties(self, properties, targets=None, block=None):
742 warnings.warn(_prop_warn)
733 743 targets, block = self._findTargetsAndBlock(targets, block)
734 744 return self._blockFromThread(self.smultiengine.set_properties, properties, targets=targets, block=block)
735 745
736 746 def get_properties(self, keys=None, targets=None, block=None):
747 warnings.warn(_prop_warn)
737 748 targets, block = self._findTargetsAndBlock(targets, block)
738 749 return self._blockFromThread(self.smultiengine.get_properties, keys, targets=targets, block=block)
739 750
740 751 def has_properties(self, keys, targets=None, block=None):
752 warnings.warn(_prop_warn)
741 753 targets, block = self._findTargetsAndBlock(targets, block)
742 754 return self._blockFromThread(self.smultiengine.has_properties, keys, targets=targets, block=block)
743 755
744 756 def del_properties(self, keys, targets=None, block=None):
757 warnings.warn(_prop_warn)
745 758 targets, block = self._findTargetsAndBlock(targets, block)
746 759 return self._blockFromThread(self.smultiengine.del_properties, keys, targets=targets, block=block)
747 760
748 761 def clear_properties(self, targets=None, block=None):
762 warnings.warn(_prop_warn)
749 763 targets, block = self._findTargetsAndBlock(targets, block)
750 764 return self._blockFromThread(self.smultiengine.clear_properties, targets=targets, block=block)
751 765
@@ -1,4 +1,4 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3
4 4 """Start an IPython cluster = (controller + engines)."""
@@ -29,29 +29,35 b' from twisted.python import failure, log'
29 29
30 30 from IPython.external import argparse
31 31 from IPython.external import Itpl
32 from IPython.genutils import get_ipython_dir, num_cpus
32 from IPython.genutils import (
33 get_ipython_dir,
34 get_log_dir,
35 get_security_dir,
36 num_cpus
37 )
33 38 from IPython.kernel.fcutil import have_crypto
34 from IPython.kernel.error import SecurityError
39
40 # Create various ipython directories if they don't exist.
41 # This must be done before IPython.kernel.config is imported.
42 from IPython.iplib import user_setup
43 if os.name == 'posix':
44 rc_suffix = ''
45 else:
46 rc_suffix = '.ini'
47 user_setup(get_ipython_dir(), rc_suffix, mode='install', interactive=False)
48 get_log_dir()
49 get_security_dir()
50
51 from IPython.kernel.config import config_manager as kernel_config_manager
52 from IPython.kernel.error import SecurityError, FileTimeoutError
35 53 from IPython.kernel.fcutil import have_crypto
36 from IPython.kernel.twistedutil import gatherBoth
54 from IPython.kernel.twistedutil import gatherBoth, wait_for_file
37 55 from IPython.kernel.util import printer
38 56
39
40 57 #-----------------------------------------------------------------------------
41 58 # General process handling code
42 59 #-----------------------------------------------------------------------------
43 60
44 def find_exe(cmd):
45 try:
46 import win32api
47 except ImportError:
48 raise ImportError('you need to have pywin32 installed for this to work')
49 else:
50 try:
51 (path, offest) = win32api.SearchPath(os.environ['PATH'],cmd + '.exe')
52 except:
53 (path, offset) = win32api.SearchPath(os.environ['PATH'],cmd + '.bat')
54 return path
55 61
56 62 class ProcessStateError(Exception):
57 63 pass
@@ -184,8 +190,10 b' class ControllerLauncher(ProcessLauncher):'
184 190 from IPython.kernel.scripts import ipcontroller
185 191 script_location = ipcontroller.__file__.replace('.pyc', '.py')
186 192 # The -u option here turns on unbuffered output, which is required
187 # on Win32 to prevent wierd conflict and problems with Twisted
188 args = [find_exe('python'), '-u', script_location]
193 # on Win32 to prevent wierd conflict and problems with Twisted.
194 # Also, use sys.executable to make sure we are picking up the
195 # right python exe.
196 args = [sys.executable, '-u', script_location]
189 197 else:
190 198 args = ['ipcontroller']
191 199 self.extra_args = extra_args
@@ -204,8 +212,10 b' class EngineLauncher(ProcessLauncher):'
204 212 from IPython.kernel.scripts import ipengine
205 213 script_location = ipengine.__file__.replace('.pyc', '.py')
206 214 # The -u option here turns on unbuffered output, which is required
207 # on Win32 to prevent wierd conflict and problems with Twisted
208 args = [find_exe('python'), '-u', script_location]
215 # on Win32 to prevent wierd conflict and problems with Twisted.
216 # Also, use sys.executable to make sure we are picking up the
217 # right python exe.
218 args = [sys.executable, '-u', script_location]
209 219 else:
210 220 args = ['ipengine']
211 221 self.extra_args = extra_args
@@ -465,7 +475,9 b' class SSHEngineSet(object):'
465 475 # The main functions should then just parse the command line arguments, create
466 476 # the appropriate class and call a 'start' method.
467 477
478
468 479 def check_security(args, cont_args):
480 """Check to see if we should run with SSL support."""
469 481 if (not args.x or not args.y) and not have_crypto:
470 482 log.err("""
471 483 OpenSSL/pyOpenSSL is not available, so we can't run in secure mode.
@@ -478,7 +490,9 b' Try running ipcluster with the -xy flags: ipcluster local -xy -n 4""")'
478 490 cont_args.append('-y')
479 491 return True
480 492
493
481 494 def check_reuse(args, cont_args):
495 """Check to see if we should try to resuse FURL files."""
482 496 if args.r:
483 497 cont_args.append('-r')
484 498 if args.client_port == 0 or args.engine_port == 0:
@@ -491,6 +505,25 b' the --client-port and --engine-port options.""")'
491 505 cont_args.append('--engine-port=%i' % args.engine_port)
492 506 return True
493 507
508
509 def _err_and_stop(f):
510 """Errback to log a failure and halt the reactor on a fatal error."""
511 log.err(f)
512 reactor.stop()
513
514
515 def _delay_start(cont_pid, start_engines, furl_file, reuse):
516 """Wait for controller to create FURL files and the start the engines."""
517 if not reuse:
518 if os.path.isfile(furl_file):
519 os.unlink(furl_file)
520 log.msg('Waiting for controller to finish starting...')
521 d = wait_for_file(furl_file, delay=0.2, max_tries=50)
522 d.addCallback(lambda _: log.msg('Controller started'))
523 d.addCallback(lambda _: start_engines(cont_pid))
524 return d
525
526
494 527 def main_local(args):
495 528 cont_args = []
496 529 cont_args.append('--logfile=%s' % pjoin(args.logdir,'ipcontroller'))
@@ -520,13 +553,10 b' def main_local(args):'
520 553 signal.signal(signal.SIGINT,shutdown)
521 554 d = eset.start(args.n)
522 555 return d
523 def delay_start(cont_pid):
524 # This is needed because the controller doesn't start listening
525 # right when it starts and the controller needs to write
526 # furl files for the engine to pick up
527 reactor.callLater(1.0, start_engines, cont_pid)
528 dstart.addCallback(delay_start)
529 dstart.addErrback(lambda f: f.raiseException())
556 config = kernel_config_manager.get_config_obj()
557 furl_file = config['controller']['engine_furl_file']
558 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
559 dstart.addErrback(_err_and_stop)
530 560
531 561
532 562 def main_mpi(args):
@@ -562,13 +592,10 b' def main_mpi(args):'
562 592 signal.signal(signal.SIGINT,shutdown)
563 593 d = eset.start()
564 594 return d
565 def delay_start(cont_pid):
566 # This is needed because the controller doesn't start listening
567 # right when it starts and the controller needs to write
568 # furl files for the engine to pick up
569 reactor.callLater(1.0, start_engines, cont_pid)
570 dstart.addCallback(delay_start)
571 dstart.addErrback(lambda f: f.raiseException())
595 config = kernel_config_manager.get_config_obj()
596 furl_file = config['controller']['engine_furl_file']
597 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
598 dstart.addErrback(_err_and_stop)
572 599
573 600
574 601 def main_pbs(args):
@@ -595,8 +622,10 b' def main_pbs(args):'
595 622 signal.signal(signal.SIGINT,shutdown)
596 623 d = pbs_set.start(args.n)
597 624 return d
598 dstart.addCallback(start_engines)
599 dstart.addErrback(lambda f: f.raiseException())
625 config = kernel_config_manager.get_config_obj()
626 furl_file = config['controller']['engine_furl_file']
627 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
628 dstart.addErrback(_err_and_stop)
600 629
601 630
602 631 def main_ssh(args):
@@ -637,12 +666,10 b' def main_ssh(args):'
637 666 signal.signal(signal.SIGINT,shutdown)
638 667 d = ssh_set.start(clusterfile['send_furl'])
639 668 return d
640
641 def delay_start(cont_pid):
642 reactor.callLater(1.0, start_engines, cont_pid)
643
644 dstart.addCallback(delay_start)
645 dstart.addErrback(lambda f: f.raiseException())
669 config = kernel_config_manager.get_config_obj()
670 furl_file = config['controller']['engine_furl_file']
671 dstart.addCallback(_delay_start, start_engines, furl_file, args.r)
672 dstart.addErrback(_err_and_stop)
646 673
647 674
648 675 def get_args():
@@ -697,8 +724,11 b' def get_args():'
697 724
698 725 parser = argparse.ArgumentParser(
699 726 description='IPython cluster startup. This starts a controller and\
700 engines using various approaches. THIS IS A TECHNOLOGY PREVIEW AND\
701 THE API WILL CHANGE SIGNIFICANTLY BEFORE THE FINAL RELEASE.'
727 engines using various approaches. Use the IPYTHONDIR environment\
728 variable to change your IPython directory from the default of\
729 .ipython or _ipython. The log and security subdirectories of your\
730 IPython directory will be used by this script for log files and\
731 security files.'
702 732 )
703 733 subparsers = parser.add_subparsers(
704 734 help='available cluster types. For help, do "ipcluster TYPE --help"')
@@ -21,8 +21,10 b' __docformat__ = "restructuredtext en"'
21 21 import sys
22 22 sys.path.insert(0, '')
23 23
24 import sys, time, os
25 24 from optparse import OptionParser
25 import os
26 import time
27 import tempfile
26 28
27 29 from twisted.application import internet, service
28 30 from twisted.internet import reactor, error, defer
@@ -37,6 +39,18 b' from IPython.kernel.error import SecurityError'
37 39 from IPython.kernel import controllerservice
38 40 from IPython.kernel.fcutil import check_furl_file_security
39 41
42 # Create various ipython directories if they don't exist.
43 # This must be done before IPython.kernel.config is imported.
44 from IPython.iplib import user_setup
45 from IPython.genutils import get_ipython_dir, get_log_dir, get_security_dir
46 if os.name == 'posix':
47 rc_suffix = ''
48 else:
49 rc_suffix = '.ini'
50 user_setup(get_ipython_dir(), rc_suffix, mode='install', interactive=False)
51 get_log_dir()
52 get_security_dir()
53
40 54 from IPython.kernel.config import config_manager as kernel_config_manager
41 55 from IPython.config.cutils import import_item
42 56
@@ -45,6 +59,10 b' from IPython.config.cutils import import_item'
45 59 # Code
46 60 #-------------------------------------------------------------------------------
47 61
62 def get_temp_furlfile(filename):
63 return tempfile.mktemp(dir=os.path.dirname(filename),
64 prefix=os.path.basename(filename))
65
48 66 def make_tub(ip, port, secure, cert_file):
49 67 """
50 68 Create a listening tub given an ip, port, and cert_file location.
@@ -107,13 +125,18 b' def make_client_service(controller_service, config):'
107 125 """Set the location for the tub and return a deferred."""
108 126
109 127 def register(empty, ref, furl_file):
110 client_tub.registerReference(ref, furlFile=furl_file)
128 # We create and then move to make sure that when the file
129 # appears to other processes, the buffer has the flushed
130 # and the file has been closed
131 temp_furl_file = get_temp_furlfile(furl_file)
132 client_tub.registerReference(ref, furlFile=temp_furl_file)
133 os.rename(temp_furl_file, furl_file)
111 134
112 135 if location == '':
113 136 d = client_tub.setLocationAutomatically()
114 137 else:
115 138 d = defer.maybeDeferred(client_tub.setLocation, "%s:%i" % (location, client_listener.getPortnum()))
116
139
117 140 for ciname, ci in config['controller']['controller_interfaces'].iteritems():
118 141 log.msg("Adapting Controller to interface: %s" % ciname)
119 142 furl_file = ci['furl_file']
@@ -154,7 +177,12 b' def make_engine_service(controller_service, config):'
154 177 """Set the location for the tub and return a deferred."""
155 178
156 179 def register(empty, ref, furl_file):
157 engine_tub.registerReference(ref, furlFile=furl_file)
180 # We create and then move to make sure that when the file
181 # appears to other processes, the buffer has the flushed
182 # and the file has been closed
183 temp_furl_file = get_temp_furlfile(furl_file)
184 engine_tub.registerReference(ref, furlFile=temp_furl_file)
185 os.rename(temp_furl_file, furl_file)
158 186
159 187 if location == '':
160 188 d = engine_tub.setLocationAutomatically()
@@ -236,7 +264,14 b' def init_config():'
236 264 Initialize the configuration using default and command line options.
237 265 """
238 266
239 parser = OptionParser()
267 parser = OptionParser("""ipcontroller [options]
268
269 Start an IPython controller.
270
271 Use the IPYTHONDIR environment variable to change your IPython directory
272 from the default of .ipython or _ipython. The log and security
273 subdirectories of your IPython directory will be used by this script
274 for log files and security files.""")
240 275
241 276 # Client related options
242 277 parser.add_option(
@@ -325,12 +360,6 b' def init_config():'
325 360 help="log file name (default is stdout)"
326 361 )
327 362 parser.add_option(
328 "--ipythondir",
329 type="string",
330 dest="ipythondir",
331 help="look for config files and profiles in this directory"
332 )
333 parser.add_option(
334 363 "-r",
335 364 action="store_true",
336 365 dest="reuse_furls",
@@ -339,7 +368,6 b' def init_config():'
339 368
340 369 (options, args) = parser.parse_args()
341 370
342 kernel_config_manager.update_config_obj_from_default_file(options.ipythondir)
343 371 config = kernel_config_manager.get_config_obj()
344 372
345 373 # Update with command line options
@@ -21,8 +21,8 b' __docformat__ = "restructuredtext en"'
21 21 import sys
22 22 sys.path.insert(0, '')
23 23
24 import sys, os
25 24 from optparse import OptionParser
25 import os
26 26
27 27 from twisted.application import service
28 28 from twisted.internet import reactor
@@ -33,6 +33,19 b' from IPython.kernel.fcutil import Tub, UnauthenticatedTub'
33 33 from IPython.kernel.core.config import config_manager as core_config_manager
34 34 from IPython.config.cutils import import_item
35 35 from IPython.kernel.engineservice import EngineService
36
37 # Create various ipython directories if they don't exist.
38 # This must be done before IPython.kernel.config is imported.
39 from IPython.iplib import user_setup
40 from IPython.genutils import get_ipython_dir, get_log_dir, get_security_dir
41 if os.name == 'posix':
42 rc_suffix = ''
43 else:
44 rc_suffix = '.ini'
45 user_setup(get_ipython_dir(), rc_suffix, mode='install', interactive=False)
46 get_log_dir()
47 get_security_dir()
48
36 49 from IPython.kernel.config import config_manager as kernel_config_manager
37 50 from IPython.kernel.engineconnector import EngineConnector
38 51
@@ -106,13 +119,19 b' def start_engine():'
106 119 engine_connector = EngineConnector(tub_service)
107 120 furl_file = kernel_config['engine']['furl_file']
108 121 log.msg("Using furl file: %s" % furl_file)
109 d = engine_connector.connect_to_controller(engine_service, furl_file)
110 def handle_error(f):
111 log.err(f)
112 if reactor.running:
113 reactor.stop()
114 d.addErrback(handle_error)
115 122
123 def call_connect(engine_service, furl_file):
124 d = engine_connector.connect_to_controller(engine_service, furl_file)
125 def handle_error(f):
126 # If this print statement is replaced by a log.err(f) I get
127 # an unhandled error, which makes no sense. I shouldn't have
128 # to use a print statement here. My only thought is that
129 # at the beginning of the process the logging is still starting up
130 print "error connecting to controller:", f.getErrorMessage()
131 reactor.callLater(0.1, reactor.stop)
132 d.addErrback(handle_error)
133
134 reactor.callWhenRunning(call_connect, engine_service, furl_file)
116 135 reactor.run()
117 136
118 137
@@ -121,7 +140,14 b' def init_config():'
121 140 Initialize the configuration using default and command line options.
122 141 """
123 142
124 parser = OptionParser()
143 parser = OptionParser("""ipengine [options]
144
145 Start an IPython engine.
146
147 Use the IPYTHONDIR environment variable to change your IPython directory
148 from the default of .ipython or _ipython. The log and security
149 subdirectories of your IPython directory will be used by this script
150 for log files and security files.""")
125 151
126 152 parser.add_option(
127 153 "--furl-file",
@@ -142,18 +168,9 b' def init_config():'
142 168 dest="logfile",
143 169 help="log file name (default is stdout)"
144 170 )
145 parser.add_option(
146 "--ipythondir",
147 type="string",
148 dest="ipythondir",
149 help="look for config files and profiles in this directory"
150 )
151 171
152 172 (options, args) = parser.parse_args()
153 173
154 kernel_config_manager.update_config_obj_from_default_file(options.ipythondir)
155 core_config_manager.update_config_obj_from_default_file(options.ipythondir)
156
157 174 kernel_config = kernel_config_manager.get_config_obj()
158 175 # Now override with command line options
159 176 if options.furl_file is not None:
@@ -16,6 +16,9 b' __docformat__ = "restructuredtext en"'
16 16 # Imports
17 17 #-----------------------------------------------------------------------------
18 18
19 # Tell nose to skip the testing of this module
20 __test__ = {}
21
19 22 import copy, time
20 23 from types import FunctionType
21 24
@@ -49,7 +49,7 b' class BlockingTaskClient(object):'
49 49 """
50 50
51 51 implements(
52 IBlockingTaskClient,
52 IBlockingTaskClient,
53 53 ITaskMapperFactory,
54 54 IMapper,
55 55 ITaskParallelDecorator
@@ -62,7 +62,7 b' class BlockingTaskClient(object):'
62 62 def run(self, task, block=False):
63 63 """Run a task on the `TaskController`.
64 64
65 See the documentation of the `MapTask` and `StringTask` classes for
65 See the documentation of the `MapTask` and `StringTask` classes for
66 66 details on how to build a task of different types.
67 67
68 68 :Parameters:
@@ -363,7 +363,8 b' class IEnginePropertiesTestCase(object):'
363 363 p = get_engine(%s).properties"""%self.engine.id
364 364 d = self.engine.execute(s)
365 365 d.addCallback(lambda r: self.engine.execute("p['a'] = lambda _:None"))
366 d = self.assertDeferredRaises(d, error.InvalidProperty)
366 d.addErrback(lambda f: self.assertRaises(error.InvalidProperty,
367 f.raiseException))
367 368 d.addCallback(lambda r: self.engine.execute("p['a'] = range(5)"))
368 369 d.addCallback(lambda r: self.engine.execute("p['a'].append(5)"))
369 370 d.addCallback(lambda r: self.engine.get_properties('a'))
@@ -1,3 +1,6 b''
1 # Tell nose to skip this module
2 __test__ = {}
3
1 4 #from __future__ import with_statement
2 5
3 6 # XXX This file is currently disabled to preserve 2.4 compatibility.
@@ -23,15 +23,15 b' __docformat__ = "restructuredtext en"'
23 23 # Imports
24 24 #-------------------------------------------------------------------------------
25 25
26 try:
27 from twisted.application.service import IService
28 from IPython.kernel.controllerservice import ControllerService
29 from IPython.kernel.tests import multienginetest as met
30 from controllertest import IControllerCoreTestCase
31 from IPython.testing.util import DeferredTestCase
32 except ImportError:
33 import nose
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
26 # Tell nose to skip this module
27 __test__ = {}
28
29 from twisted.application.service import IService
30 from IPython.kernel.controllerservice import ControllerService
31 from IPython.kernel.tests import multienginetest as met
32 from controllertest import IControllerCoreTestCase
33 from IPython.testing.util import DeferredTestCase
34
35 35
36 36 class BasicControllerServiceTest(DeferredTestCase,
37 37 IControllerCoreTestCase):
@@ -15,30 +15,29 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 try:
19 from twisted.python import components
20 from twisted.internet import reactor, defer
21 from twisted.spread import pb
22 from twisted.internet.base import DelayedCall
23 DelayedCall.debug = True
18 # Tell nose to skip this module
19 __test__ = {}
24 20
25 import zope.interface as zi
21 from twisted.python import components
22 from twisted.internet import reactor, defer
23 from twisted.spread import pb
24 from twisted.internet.base import DelayedCall
25 DelayedCall.debug = True
26 26
27 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
28 from IPython.kernel import engineservice as es
29 from IPython.testing.util import DeferredTestCase
30 from IPython.kernel.controllerservice import IControllerBase
31 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
32 from IPython.kernel.engineservice import IEngineQueued
33 from IPython.kernel.engineconnector import EngineConnector
34
35 from IPython.kernel.tests.engineservicetest import \
36 IEngineCoreTestCase, \
37 IEngineSerializedTestCase, \
38 IEngineQueuedTestCase
39 except ImportError:
40 import nose
41 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
27 import zope.interface as zi
28
29 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
30 from IPython.kernel import engineservice as es
31 from IPython.testing.util import DeferredTestCase
32 from IPython.kernel.controllerservice import IControllerBase
33 from IPython.kernel.enginefc import FCRemoteEngineRefFromService, IEngineBase
34 from IPython.kernel.engineservice import IEngineQueued
35 from IPython.kernel.engineconnector import EngineConnector
36
37 from IPython.kernel.tests.engineservicetest import \
38 IEngineCoreTestCase, \
39 IEngineSerializedTestCase, \
40 IEngineQueuedTestCase
42 41
43 42
44 43 class EngineFCTest(DeferredTestCase,
@@ -23,20 +23,19 b' __docformat__ = "restructuredtext en"'
23 23 # Imports
24 24 #-------------------------------------------------------------------------------
25 25
26 try:
27 from twisted.internet import defer
28 from twisted.application.service import IService
29
30 from IPython.kernel import engineservice as es
31 from IPython.testing.util import DeferredTestCase
32 from IPython.kernel.tests.engineservicetest import \
33 IEngineCoreTestCase, \
34 IEngineSerializedTestCase, \
35 IEngineQueuedTestCase, \
36 IEnginePropertiesTestCase
37 except ImportError:
38 import nose
39 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
26 # Tell nose to skip this module
27 __test__ = {}
28
29 from twisted.internet import defer
30 from twisted.application.service import IService
31
32 from IPython.kernel import engineservice as es
33 from IPython.testing.util import DeferredTestCase
34 from IPython.kernel.tests.engineservicetest import \
35 IEngineCoreTestCase, \
36 IEngineSerializedTestCase, \
37 IEngineQueuedTestCase, \
38 IEnginePropertiesTestCase
40 39
41 40
42 41 class BasicEngineServiceTest(DeferredTestCase,
@@ -4,29 +4,28 b''
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 #-------------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 13
14 #-------------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 15 # Imports
16 #-------------------------------------------------------------------------------
17
18 try:
19 from twisted.internet import defer
20 from IPython.testing.util import DeferredTestCase
21 from IPython.kernel.controllerservice import ControllerService
22 from IPython.kernel import multiengine as me
23 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
24 ISynchronousMultiEngineTestCase)
25 except ImportError:
26 import nose
27 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
28
29
16 #-----------------------------------------------------------------------------
17
18 # Tell nose to skip this module
19 __test__ = {}
20
21 from twisted.internet import defer
22 from IPython.testing.util import DeferredTestCase
23 from IPython.kernel.controllerservice import ControllerService
24 from IPython.kernel import multiengine as me
25 from IPython.kernel.tests.multienginetest import (IMultiEngineTestCase,
26 ISynchronousMultiEngineTestCase)
27
28
30 29 class BasicMultiEngineTestCase(DeferredTestCase, IMultiEngineTestCase):
31 30
32 31 def setUp(self):
@@ -14,25 +14,25 b' __docformat__ = "restructuredtext en"'
14 14 # Imports
15 15 #-------------------------------------------------------------------------------
16 16
17 try:
18 from twisted.internet import defer, reactor
17 # Tell nose to skip this module
18 __test__ = {}
19
20 from twisted.internet import defer, reactor
21
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
23
24 from IPython.testing.util import DeferredTestCase
25 from IPython.kernel.controllerservice import ControllerService
26 from IPython.kernel.multiengine import IMultiEngine
27 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 from IPython.kernel import multiengine as me
30 from IPython.kernel.clientconnector import ClientConnector
31 from IPython.kernel.parallelfunction import ParallelFunction
32 from IPython.kernel.error import CompositeError
33 from IPython.kernel.util import printer
19 34
20 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
21 35
22 from IPython.testing.util import DeferredTestCase
23 from IPython.kernel.controllerservice import ControllerService
24 from IPython.kernel.multiengine import IMultiEngine
25 from IPython.kernel.tests.multienginetest import IFullSynchronousMultiEngineTestCase
26 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
27 from IPython.kernel import multiengine as me
28 from IPython.kernel.clientconnector import ClientConnector
29 from IPython.kernel.parallelfunction import ParallelFunction
30 from IPython.kernel.error import CompositeError
31 from IPython.kernel.util import printer
32 except ImportError:
33 import nose
34 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
35
36 36 def _raise_it(f):
37 37 try:
38 38 f.raiseException()
@@ -4,36 +4,36 b''
4 4
5 5 __docformat__ = "restructuredtext en"
6 6
7 #-------------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 8 # Copyright (C) 2008 The IPython Development Team
9 9 #
10 10 # Distributed under the terms of the BSD License. The full license is in
11 11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 13
14 #-------------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 15 # Imports
16 #-------------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 17
18 try:
19 import zope.interface as zi
20 from twisted.trial import unittest
21 from IPython.testing.util import DeferredTestCase
18 # Tell nose to skip this module
19 __test__ = {}
22 20
23 from IPython.kernel.newserialized import \
24 ISerialized, \
25 IUnSerialized, \
26 Serialized, \
27 UnSerialized, \
28 SerializeIt, \
29 UnSerializeIt
30 except ImportError:
31 import nose
32 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
21 import zope.interface as zi
22 from twisted.trial import unittest
23 from IPython.testing.util import DeferredTestCase
33 24
34 #-------------------------------------------------------------------------------
25 from IPython.kernel.newserialized import \
26 ISerialized, \
27 IUnSerialized, \
28 Serialized, \
29 UnSerialized, \
30 SerializeIt, \
31 UnSerializeIt
32
33
34 #-----------------------------------------------------------------------------
35 35 # Tests
36 #-------------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 37
38 38 class SerializedTestCase(unittest.TestCase):
39 39
@@ -16,18 +16,18 b' __docformat__ = "restructuredtext en"'
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18
19 try:
20 from twisted.internet import defer
21 from twisted.python import failure
22
23 from IPython.testing.util import DeferredTestCase
24 import IPython.kernel.pendingdeferred as pd
25 from IPython.kernel import error
26 from IPython.kernel.util import printer
27 except ImportError:
28 import nose
29 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
30
19 # Tell nose to skip this module
20 __test__ = {}
21
22 from twisted.internet import defer
23 from twisted.python import failure
24
25 from IPython.testing.util import DeferredTestCase
26 import IPython.kernel.pendingdeferred as pd
27 from IPython.kernel import error
28 from IPython.kernel.util import printer
29
30
31 31 class Foo(object):
32 32
33 33 def bar(self, bahz):
@@ -15,19 +15,19 b' __docformat__ = "restructuredtext en"'
15 15 # Imports
16 16 #-------------------------------------------------------------------------------
17 17
18 try:
19 import time
20
21 from twisted.internet import defer
22 from twisted.trial import unittest
23
24 from IPython.kernel import task, controllerservice as cs, engineservice as es
25 from IPython.kernel.multiengine import IMultiEngine
26 from IPython.testing.util import DeferredTestCase
27 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
28 except ImportError:
29 import nose
30 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
18 # Tell nose to skip this module
19 __test__ = {}
20
21 import time
22
23 from twisted.internet import defer
24 from twisted.trial import unittest
25
26 from IPython.kernel import task, controllerservice as cs, engineservice as es
27 from IPython.kernel.multiengine import IMultiEngine
28 from IPython.testing.util import DeferredTestCase
29 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
30
31 31
32 32 #-------------------------------------------------------------------------------
33 33 # Tests
@@ -14,27 +14,26 b' __docformat__ = "restructuredtext en"'
14 14 # Imports
15 15 #-------------------------------------------------------------------------------
16 16
17 try:
18 import time
17 # Tell nose to skip this module
18 __test__ = {}
19 19
20 from twisted.internet import defer, reactor
20 import time
21 21
22 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
22 from twisted.internet import defer, reactor
23 23
24 from IPython.kernel import task as taskmodule
25 from IPython.kernel import controllerservice as cs
26 import IPython.kernel.multiengine as me
27 from IPython.testing.util import DeferredTestCase
28 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
29 from IPython.kernel.taskfc import IFCTaskController
30 from IPython.kernel.util import printer
31 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
32 from IPython.kernel.clientconnector import ClientConnector
33 from IPython.kernel.error import CompositeError
34 from IPython.kernel.parallelfunction import ParallelFunction
35 except ImportError:
36 import nose
37 raise nose.SkipTest("This test requires zope.interface, Twisted and Foolscap")
24 from IPython.kernel.fcutil import Tub, UnauthenticatedTub
25
26 from IPython.kernel import task as taskmodule
27 from IPython.kernel import controllerservice as cs
28 import IPython.kernel.multiengine as me
29 from IPython.testing.util import DeferredTestCase
30 from IPython.kernel.multienginefc import IFCSynchronousMultiEngine
31 from IPython.kernel.taskfc import IFCTaskController
32 from IPython.kernel.util import printer
33 from IPython.kernel.tests.tasktest import ITaskControllerTestCase
34 from IPython.kernel.clientconnector import ClientConnector
35 from IPython.kernel.error import CompositeError
36 from IPython.kernel.parallelfunction import ParallelFunction
38 37
39 38
40 39 #-------------------------------------------------------------------------------
@@ -16,12 +16,15 b' __docformat__ = "restructuredtext en"'
16 16 # Imports
17 17 #-------------------------------------------------------------------------------
18 18
19 import os, sys
19 20 import threading, Queue, atexit
20 import twisted
21 21
22 import twisted
22 23 from twisted.internet import defer, reactor
23 24 from twisted.python import log, failure
24 25
26 from IPython.kernel.error import FileTimeoutError
27
25 28 #-------------------------------------------------------------------------------
26 29 # Classes related to twisted and threads
27 30 #-------------------------------------------------------------------------------
@@ -204,3 +207,43 b' class DeferredList(defer.Deferred):'
204 207 result = None
205 208
206 209 return result
210
211
212 def wait_for_file(filename, delay=0.1, max_tries=10):
213 """Wait (poll) for a file to be created.
214
215 This method returns a Deferred that will fire when a file exists. It
216 works by polling os.path.isfile in time intervals specified by the
217 delay argument. If `max_tries` is reached, it will errback with a
218 `FileTimeoutError`.
219
220 Parameters
221 ----------
222 filename : str
223 The name of the file to wait for.
224 delay : float
225 The time to wait between polls.
226 max_tries : int
227 The max number of attempts before raising `FileTimeoutError`
228
229 Returns
230 -------
231 d : Deferred
232 A Deferred instance that will fire when the file exists.
233 """
234
235 d = defer.Deferred()
236
237 def _test_for_file(filename, attempt=0):
238 if attempt >= max_tries:
239 d.errback(FileTimeoutError(
240 'timeout waiting for file to be created: %s' % filename
241 ))
242 else:
243 if os.path.isfile(filename):
244 d.callback(True)
245 else:
246 reactor.callLater(delay, _test_for_file, filename, attempt+1)
247
248 _test_for_file(filename)
249 return d
@@ -14,6 +14,7 b' for your operation system, from platutils_PLATFORMNAME module.'
14 14
15 15 import os
16 16 import sys
17 import warnings
17 18
18 19 # Import the platform-specific implementations
19 20 if os.name == 'posix':
@@ -61,6 +62,41 b' def set_term_title(title):'
61 62 _platutils.set_term_title(title)
62 63
63 64
65 class FindCmdError(Exception):
66 pass
67
68 def find_cmd(cmd):
69 """Find full path to executable cmd in a cross platform manner.
70
71 This function tries to determine the full path to a command line program
72 using `which` on Unix/Linux/OS X and `win32api` on Windows. Most of the
73 time it will use the version that is first on the users `PATH`. If
74 cmd is `python` return `sys.executable`.
75
76 Parameters
77 ----------
78 cmd : str
79 The command line program to look for.
80 """
81 if cmd == 'python':
82 return sys.executable
83 try:
84 path = _platutils.find_cmd(cmd)
85 except:
86 raise FindCmdError('command could not be found: %s' % cmd)
87 # which returns empty if not found
88 if path == '':
89 raise FindCmdError('command could not be found: %s' % cmd)
90 return path
91
92 def get_long_path_name(path):
93 """Expand a path into its long form.
94
95 On Windows this expands any ~ in the paths. On other platforms, it is
96 a null operation.
97 """
98 return _platutils.get_long_path_name(path)
99
64 100 #-----------------------------------------------------------------------------
65 101 # Deprecated functions
66 102 #-----------------------------------------------------------------------------
@@ -23,3 +23,11 b' ignore_termtitle = True'
23 23 def set_term_title(*args,**kw):
24 24 """Dummy no-op."""
25 25 pass
26
27 def find_cmd(cmd):
28 """Find the full path to a command using which."""
29 return os.popen('which %s' % cmd).read().strip()
30
31 def get_long_path_name(path):
32 """Dummy no-op."""
33 return path
@@ -30,3 +30,11 b" if os.environ.get('TERM','') == 'xterm':"
30 30 set_term_title = _set_term_title_xterm
31 31 else:
32 32 set_term_title = _dummy_op
33
34 def find_cmd(cmd):
35 """Find the full path to a command using which."""
36 return os.popen('which %s' % cmd).read().strip()
37
38 def get_long_path_name(path):
39 """Dummy no-op."""
40 return path
@@ -41,3 +41,42 b' except ImportError:'
41 41 if ret:
42 42 # non-zero return code signals error, don't try again
43 43 ignore_termtitle = True
44
45 def find_cmd(cmd):
46 """Find the full path to a .bat or .exe using the win32api module."""
47 try:
48 import win32api
49 except ImportError:
50 raise ImportError('you need to have pywin32 installed for this to work')
51 else:
52 try:
53 (path, offest) = win32api.SearchPath(os.environ['PATH'],cmd + '.exe')
54 except:
55 (path, offset) = win32api.SearchPath(os.environ['PATH'],cmd + '.bat')
56 return path
57
58
59 def get_long_path_name(path):
60 """Get a long path name (expand ~) on Windows using ctypes.
61
62 Examples
63 --------
64
65 >>> get_long_path_name('c:\\docume~1')
66 u'c:\\\\Documents and Settings'
67
68 """
69 try:
70 import ctypes
71 except ImportError:
72 raise ImportError('you need to have ctypes installed for this to work')
73 _GetLongPathName = ctypes.windll.kernel32.GetLongPathNameW
74 _GetLongPathName.argtypes = [ctypes.c_wchar_p, ctypes.c_wchar_p,
75 ctypes.c_uint ]
76
77 buf = ctypes.create_unicode_buffer(260)
78 rv = _GetLongPathName(path, buf, 260)
79 if rv == 0 or rv > 260:
80 return path
81 else:
82 return buf.value
@@ -123,10 +123,10 b' def skipif(skip_condition, msg=None):'
123 123 Parameters
124 124 ----------
125 125 skip_condition : bool or callable.
126 Flag to determine whether to skip test. If the condition is a
127 callable, it is used at runtime to dynamically make the decision. This
128 is useful for tests that may require costly imports, to delay the cost
129 until the test suite is actually executed.
126 Flag to determine whether to skip test. If the condition is a
127 callable, it is used at runtime to dynamically make the decision. This
128 is useful for tests that may require costly imports, to delay the cost
129 until the test suite is actually executed.
130 130 msg : string
131 131 Message to give on raising a SkipTest exception
132 132
@@ -29,13 +29,6 b' def setastest(tf=True):'
29 29 tf : bool
30 30 If True specifies this is a test, not a test otherwise
31 31
32 e.g
33 >>> from numpy.testing.decorators import setastest
34 >>> @setastest(False)
35 ... def func_with_test_in_name(arg1, arg2): pass
36 ...
37 >>>
38
39 32 This decorator cannot use the nose namespace, because it can be
40 33 called from a non-test module. See also istest and nottest in
41 34 nose.tools
@@ -1,54 +1,121 b''
1 1 # -*- coding: utf-8 -*-
2 2 """IPython Test Suite Runner.
3 3
4 This module provides a main entry point to a user script to test IPython itself
5 from the command line. The main() routine can be used in a similar manner to
6 the ``nosetests`` script, and it takes similar arguments, but if no arguments
7 are given it defaults to testing all of IPython. This should be preferred to
8 using plain ``nosetests`` because a number of nose plugins necessary to test
9 IPython correctly are automatically configured by this code.
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) or trial recursively. This
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
14
15 For now, this script requires that both nose and twisted are installed. This
16 will change in the future.
10 17 """
11 18
12 19 #-----------------------------------------------------------------------------
13 20 # Module imports
14 21 #-----------------------------------------------------------------------------
15 22
16 # stdlib
23 import os
24 import os.path as path
17 25 import sys
26 import subprocess
27 import time
18 28 import warnings
19 29
20 # third-party
21 30 import nose.plugins.builtin
22 31 from nose.core import TestProgram
23 32
24 # Our own imports
33 from IPython.platutils import find_cmd
25 34 from IPython.testing.plugin.ipdoctest import IPythonDoctest
26 35
36 pjoin = path.join
37
27 38 #-----------------------------------------------------------------------------
28 # Constants and globals
39 # Logic for skipping doctests
29 40 #-----------------------------------------------------------------------------
30 41
42 def test_for(mod):
43 """Test to see if mod is importable."""
44 try:
45 __import__(mod)
46 except ImportError:
47 return False
48 else:
49 return True
50
51 have_curses = test_for('_curses')
52 have_wx = test_for('wx')
53 have_zi = test_for('zope.interface')
54 have_twisted = test_for('twisted')
55 have_foolscap = test_for('foolscap')
56 have_objc = test_for('objc')
57 have_pexpect = test_for('pexpect')
58
31 59 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
32 60 # testing problems. We should strive to minimize the number of skipped
33 61 # modules, since this means untested code. As the testing machinery
34 62 # solidifies, this list should eventually become empty.
35 EXCLUDE = ['IPython/external/',
36 'IPython/platutils_win32',
37 'IPython/frontend/cocoa',
38 'IPython_doctest_plugin',
39 'IPython/Gnuplot',
40 'IPython/Extensions/ipy_',
41 'IPython/Extensions/clearcmd',
42 'IPython/Extensions/PhysicalQIn',
43 'IPython/Extensions/scitedirector',
63 EXCLUDE = [pjoin('IPython', 'external'),
64 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
65 pjoin('IPython_doctest_plugin'),
66 pjoin('IPython', 'Gnuplot'),
67 pjoin('IPython', 'Extensions', 'ipy_'),
68 pjoin('IPython', 'Extensions', 'clearcmd'),
69 pjoin('IPython', 'Extensions', 'PhysicalQInteractive'),
70 pjoin('IPython', 'Extensions', 'scitedirector'),
71 pjoin('IPython', 'Extensions', 'numeric_formats'),
72 pjoin('IPython', 'testing', 'attic'),
73 pjoin('IPython', 'testing', 'tutils'),
74 pjoin('IPython', 'testing', 'tools'),
75 pjoin('IPython', 'testing', 'mkdoctests')
44 76 ]
45 77
78 if not have_wx:
79 EXCLUDE.append(pjoin('IPython', 'Extensions', 'igrid'))
80 EXCLUDE.append(pjoin('IPython', 'gui'))
81 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
82
83 if not have_objc:
84 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
85
86 if not have_curses:
87 EXCLUDE.append(pjoin('IPython', 'Extensions', 'ibrowse'))
88
89 if not sys.platform == 'win32':
90 EXCLUDE.append(pjoin('IPython', 'platutils_win32'))
91
92 # These have to be skipped on win32 because the use echo, rm, cd, etc.
93 # See ticket https://bugs.launchpad.net/bugs/366982
94 if sys.platform == 'win32':
95 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
96 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
97
98 if not os.name == 'posix':
99 EXCLUDE.append(pjoin('IPython', 'platutils_posix'))
100
101 if not have_pexpect:
102 EXCLUDE.append(pjoin('IPython', 'irunner'))
103
104 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
105 if sys.platform == 'win32':
106 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
107
108
46 109 #-----------------------------------------------------------------------------
47 110 # Functions and classes
48 111 #-----------------------------------------------------------------------------
49 112
50 def main():
51 """Run the IPython test suite.
113 def run_iptest():
114 """Run the IPython test suite using nose.
115
116 This function is called when this script is **not** called with the form
117 `iptest all`. It simply calls nose with appropriate command line flags
118 and accepts all of the standard nose arguments.
52 119 """
53 120
54 121 warnings.filterwarnings('ignore',
@@ -60,8 +127,8 b' def main():'
60 127 # test suite back into working shape. Our nose
61 128 # plugin needs to be gone through with a fine
62 129 # toothed comb to find what is causing the problem.
63 # '--with-ipdoctest',
64 '--doctest-tests','--doctest-extension=txt',
130 '--with-ipdoctest',
131 '--ipdoctest-tests','--ipdoctest-extension=txt',
65 132 '--detailed-errors',
66 133
67 134 # We add --exe because of setuptools' imbecility (it
@@ -81,11 +148,13 b' def main():'
81 148 (':' in arg and '.py' in arg):
82 149 has_tests = True
83 150 break
151
84 152 # If nothing was specifically requested, test full IPython
85 153 if not has_tests:
86 154 argv.append('IPython')
87 155
88 # Construct list of plugins, omitting the existing doctest plugin.
156 # Construct list of plugins, omitting the existing doctest plugin, which
157 # ours replaces (and extends).
89 158 plugins = [IPythonDoctest(EXCLUDE)]
90 159 for p in nose.plugins.builtin.plugins:
91 160 plug = p()
@@ -96,3 +165,136 b' def main():'
96 165 plugins.append(plug)
97 166
98 167 TestProgram(argv=argv,plugins=plugins)
168
169
170 class IPTester(object):
171 """Call that calls iptest or trial in a subprocess.
172 """
173 def __init__(self,runner='iptest',params=None):
174 """ """
175 if runner == 'iptest':
176 self.runner = ['iptest','-v']
177 else:
178 self.runner = [find_cmd('trial')]
179 if params is None:
180 params = []
181 if isinstance(params,str):
182 params = [params]
183 self.params = params
184
185 # Assemble call
186 self.call_args = self.runner+self.params
187
188 def run(self):
189 """Run the stored commands"""
190 return subprocess.call(self.call_args)
191
192
193 def make_runners():
194 """Define the modules and packages that need to be tested.
195 """
196
197 # This omits additional top-level modules that should not be doctested.
198 # XXX: Shell.py is also ommited because of a bug in the skip_doctest
199 # decorator. See ticket https://bugs.launchpad.net/bugs/366209
200 top_mod = \
201 ['background_jobs.py', 'ColorANSI.py', 'completer.py', 'ConfigLoader.py',
202 'CrashHandler.py', 'Debugger.py', 'deep_reload.py', 'demo.py',
203 'DPyGetOpt.py', 'dtutils.py', 'excolors.py', 'FakeModule.py',
204 'generics.py', 'genutils.py', 'history.py', 'hooks.py', 'ipapi.py',
205 'iplib.py', 'ipmaker.py', 'ipstruct.py', 'Itpl.py',
206 'Logger.py', 'macro.py', 'Magic.py', 'OInspect.py',
207 'OutputTrap.py', 'platutils.py', 'prefilter.py', 'Prompts.py',
208 'PyColorize.py', 'Release.py', 'rlineimpl.py', 'shadowns.py',
209 'shellglobals.py', 'strdispatch.py', 'twshell.py',
210 'ultraTB.py', 'upgrade_dir.py', 'usage.py', 'wildcard.py',
211 # See note above for why this is skipped
212 # 'Shell.py',
213 'winconsole.py']
214
215 if have_pexpect:
216 top_mod.append('irunner.py')
217
218 if sys.platform == 'win32':
219 top_mod.append('platutils_win32.py')
220 elif os.name == 'posix':
221 top_mod.append('platutils_posix.py')
222 else:
223 top_mod.append('platutils_dummy.py')
224
225 # These are tested by nose, so skip IPython.kernel
226 top_pack = ['config','Extensions','frontend',
227 'testing','tests','tools','UserConfig']
228
229 if have_wx:
230 top_pack.append('gui')
231
232 modules = ['IPython.%s' % m[:-3] for m in top_mod ]
233 packages = ['IPython.%s' % m for m in top_pack ]
234
235 # Make runners
236 runners = dict(zip(top_pack, [IPTester(params=v) for v in packages]))
237
238 # Test IPython.kernel using trial if twisted is installed
239 if have_zi and have_twisted and have_foolscap:
240 runners['trial'] = IPTester('trial',['IPython'])
241
242 runners['modules'] = IPTester(params=modules)
243
244 return runners
245
246
247 def run_iptestall():
248 """Run the entire IPython test suite by calling nose and trial.
249
250 This function constructs :class:`IPTester` instances for all IPython
251 modules and package and then runs each of them. This causes the modules
252 and packages of IPython to be tested each in their own subprocess using
253 nose or twisted.trial appropriately.
254 """
255 runners = make_runners()
256 # Run all test runners, tracking execution time
257 failed = {}
258 t_start = time.time()
259 for name,runner in runners.iteritems():
260 print '*'*77
261 print 'IPython test set:',name
262 res = runner.run()
263 if res:
264 failed[name] = res
265 t_end = time.time()
266 t_tests = t_end - t_start
267 nrunners = len(runners)
268 nfail = len(failed)
269 # summarize results
270 print
271 print '*'*77
272 print 'Ran %s test sets in %.3fs' % (nrunners, t_tests)
273 print
274 if not failed:
275 print 'OK'
276 else:
277 # If anything went wrong, point out what command to rerun manually to
278 # see the actual errors and individual summary
279 print 'ERROR - %s out of %s test sets failed.' % (nfail, nrunners)
280 for name in failed:
281 failed_runner = runners[name]
282 print '-'*40
283 print 'Runner failed:',name
284 print 'You may wish to rerun this one individually, with:'
285 print ' '.join(failed_runner.call_args)
286 print
287
288
289 def main():
290 if len(sys.argv) == 1:
291 run_iptestall()
292 else:
293 if sys.argv[1] == 'all':
294 run_iptestall()
295 else:
296 run_iptest()
297
298
299 if __name__ == '__main__':
300 main() No newline at end of file
@@ -15,7 +15,6 b' Limitations:'
15 15 won't even have these special _NN variables set at all.
16 16 """
17 17
18
19 18 #-----------------------------------------------------------------------------
20 19 # Module imports
21 20
@@ -60,6 +59,19 b' log = logging.getLogger(__name__)'
60 59 # machinery into a fit. This code should be considered a gross hack, but it
61 60 # gets the job done.
62 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
63 75
64 76 # Hack to modify the %run command so we can sync the user's namespace with the
65 77 # test globals. Once we move over to a clean magic system, this will be done
@@ -85,9 +97,19 b' def _run_ns_sync(self,arg_s,runner=None):'
85 97 This is strictly needed for running doctests that call %run.
86 98 """
87 99
88 finder = py_file_finder(_run_ns_sync.test_filename)
100 # When tests call %run directly (not via doctest) these function attributes
101 # are not set
102 try:
103 fname = _run_ns_sync.test_filename
104 except AttributeError:
105 fname = arg_s
106
107 finder = py_file_finder(fname)
89 108 out = _ip.IP.magic_run_ori(arg_s,runner,finder)
90 _run_ns_sync.test_globs.update(_ip.user_ns)
109
110 # Simliarly, there is no test_globs when a test is NOT a doctest
111 if hasattr(_run_ns_sync,'test_globs'):
112 _run_ns_sync.test_globs.update(_ip.user_ns)
91 113 return out
92 114
93 115
@@ -114,15 +136,31 b' class ipnsdict(dict):'
114 136 def update(self,other):
115 137 self._checkpoint()
116 138 dict.update(self,other)
139
117 140 # If '_' is in the namespace, python won't set it when executing code,
118 141 # and we have examples that test it. So we ensure that the namespace
119 142 # is always 'clean' of it before it's used for test code execution.
120 143 self.pop('_',None)
144
145 # The builtins namespace must *always* be the real __builtin__ module,
146 # else weird stuff happens. The main ipython code does have provisions
147 # to ensure this after %run, but since in this class we do some
148 # aggressive low-level cleaning of the execution namespace, we need to
149 # correct for that ourselves, to ensure consitency with the 'real'
150 # ipython.
151 self['__builtins__'] = __builtin__
121 152
122 153
123 154 def start_ipython():
124 155 """Start a global IPython shell, which we need for IPython-specific syntax.
125 156 """
157
158 # This function should only ever run once!
159 if hasattr(start_ipython,'already_called'):
160 return
161 start_ipython.already_called = True
162
163 # Ok, first time we're called, go ahead
126 164 import new
127 165
128 166 import IPython
@@ -142,10 +180,11 b' def start_ipython():'
142 180 _excepthook = sys.excepthook
143 181 _main = sys.modules.get('__main__')
144 182
183 argv = default_argv()
184
145 185 # Start IPython instance. We customize it to start with minimal frills.
146 186 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
147 IPython.Shell.IPShell(['--colors=NoColor','--noterm_title'],
148 user_ns,global_ns)
187 IPython.Shell.IPShell(argv,user_ns,global_ns)
149 188
150 189 # Deactivate the various python system hooks added by ipython for
151 190 # interactive convenience so we don't confuse the doctest system
@@ -691,6 +730,7 b' class ExtensionDoctest(doctests.Doctest):'
691 730 to exclude any filename which matches them from inclusion in the test
692 731 suite (using pattern.search(), NOT pattern.match() ).
693 732 """
733
694 734 if exclude_patterns is None:
695 735 exclude_patterns = []
696 736 self.exclude_patterns = map(re.compile,exclude_patterns)
@@ -800,11 +840,11 b' class ExtensionDoctest(doctests.Doctest):'
800 840 Modified version that accepts extension modules as valid containers for
801 841 doctests.
802 842 """
803 #print '*** ipdoctest- wantFile:',filename # dbg
843 # print '*** ipdoctest- wantFile:',filename # dbg
804 844
805 845 for pat in self.exclude_patterns:
806 846 if pat.search(filename):
807 #print '###>>> SKIP:',filename # dbg
847 # print '###>>> SKIP:',filename # dbg
808 848 return False
809 849
810 850 if is_extension_module(filename):
@@ -836,15 +876,33 b' class IPythonDoctest(ExtensionDoctest):'
836 876 optionflags=optionflags,
837 877 checker=self.checker)
838 878
839 def configure(self, options, config):
879 def options(self, parser, env=os.environ):
880 Plugin.options(self, parser, env)
881 parser.add_option('--ipdoctest-tests', action='store_true',
882 dest='ipdoctest_tests',
883 default=env.get('NOSE_IPDOCTEST_TESTS',True),
884 help="Also look for doctests in test modules. "
885 "Note that classes, methods and functions should "
886 "have either doctests or non-doctest tests, "
887 "not both. [NOSE_IPDOCTEST_TESTS]")
888 parser.add_option('--ipdoctest-extension', action="append",
889 dest="ipdoctest_extension",
890 help="Also look for doctests in files with "
891 "this extension [NOSE_IPDOCTEST_EXTENSION]")
892 # Set the default as a list, if given in env; otherwise
893 # an additional value set on the command line will cause
894 # an error.
895 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
896 if env_setting is not None:
897 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
840 898
899 def configure(self, options, config):
841 900 Plugin.configure(self, options, config)
842 self.doctest_tests = options.doctest_tests
843 self.extension = tolist(options.doctestExtension)
901 self.doctest_tests = options.ipdoctest_tests
902 self.extension = tolist(options.ipdoctest_extension)
844 903
845 904 self.parser = IPDocTestParser()
846 905 self.finder = DocTestFinder(parser=self.parser)
847 906 self.checker = IPDoctestOutputChecker()
848 907 self.globs = None
849 908 self.extraglobs = None
850
@@ -6,8 +6,9 b' This is used by a companion test case.'
6 6 import gc
7 7
8 8 class C(object):
9 def __del__(self):
10 print 'deleting object...'
9 def __del__(self):
10 pass
11 #print 'deleting object...' # dbg
11 12
12 13 c = C()
13 14
@@ -39,13 +39,10 b' def doctest_ivars():'
39 39 Out[6]: 1
40 40 """
41 41
42 @dec.skip_doctest
42 #@dec.skip_doctest
43 43 def doctest_refs():
44 44 """DocTest reference holding issues when running scripts.
45 45
46 46 In [32]: run show_refs.py
47 47 c referrers: [<type 'dict'>]
48
49 In [33]: map(type,gc.get_referrers(c))
50 Out[33]: [<type 'dict'>]
51 48 """
@@ -46,7 +46,7 b' def test_deliberately_broken():'
46 46 """A deliberately broken test - we want to skip this one."""
47 47 1/0
48 48
49 @dec.skip('foo')
49 @dec.skip('Testing the skip decorator')
50 50 def test_deliberately_broken2():
51 51 """Another deliberately broken test - we want to skip this one."""
52 52 1/0
@@ -26,7 +26,7 b' import sys'
26 26
27 27 class A(object):
28 28 def __del__(self):
29 print 'object A deleted'
29 print 'obj_del.py: object A deleted'
30 30
31 31 a = A()
32 32
@@ -16,11 +16,12 b' class C(object):'
16 16 self.name = name
17 17
18 18 def __del__(self):
19 print 'Deleting object:',self.name
19 print 'tclass.py: deleting object:',self.name
20 20
21 21 try:
22 22 name = sys.argv[1]
23 23 except IndexError:
24 24 pass
25 25 else:
26 c = C(name)
26 if name.startswith('C'):
27 c = C(name)
@@ -210,7 +210,6 b' def test_get_home_dir_7():'
210 210 home_dir = genutils.get_home_dir()
211 211 nt.assert_equal(home_dir, abspath(HOME_TEST_DIR))
212 212
213
214 213 #
215 214 # Tests for get_ipython_dir
216 215 #
@@ -239,7 +238,6 b' def test_get_ipython_dir_3():'
239 238 ipdir = genutils.get_ipython_dir()
240 239 nt.assert_equal(ipdir, os.path.abspath(os.path.join("someplace", "_ipython")))
241 240
242
243 241 #
244 242 # Tests for get_security_dir
245 243 #
@@ -249,6 +247,14 b' def test_get_security_dir():'
249 247 """Testcase to see if we can call get_security_dir without Exceptions."""
250 248 sdir = genutils.get_security_dir()
251 249
250 #
251 # Tests for get_log_dir
252 #
253
254 @with_enivronment
255 def test_get_log_dir():
256 """Testcase to see if we can call get_log_dir without Exceptions."""
257 sdir = genutils.get_log_dir()
252 258
253 259 #
254 260 # Tests for popkey
@@ -289,3 +295,12 b' def test_popkey_3():'
289 295 nt.assert_equal(dct, dict(c=3))
290 296 nt.assert_equal(genutils.popkey(dct, "c"), 3)
291 297 nt.assert_equal(dct, dict())
298
299
300 def test_filefind():
301 """Various tests for filefind"""
302 f = tempfile.NamedTemporaryFile()
303 print 'fname:',f.name
304 alt_dirs = genutils.get_ipython_dir()
305 t = genutils.filefind(f.name,alt_dirs)
306 print 'found:',t
@@ -1,17 +1,80 b''
1 1 """Tests for the key iplib module, where the main ipython class is defined.
2 2 """
3 #-----------------------------------------------------------------------------
4 # Module imports
5 #-----------------------------------------------------------------------------
3 6
7 # stdlib
8 import os
9 import shutil
10 import tempfile
11
12 # third party
4 13 import nose.tools as nt
5 14
15 # our own packages
16 from IPython import ipapi, iplib
17
18 #-----------------------------------------------------------------------------
19 # Globals
20 #-----------------------------------------------------------------------------
21
22 # Useful global ipapi object and main IPython one. Unfortunately we have a
23 # long precedent of carrying the 'ipapi' global object which is injected into
24 # the system namespace as _ip, but that keeps a pointer to the actual IPython
25 # InteractiveShell instance, which is named IP. Since in testing we do need
26 # access to the real thing (we want to probe beyond what ipapi exposes), make
27 # here a global reference to each. In general, things that are exposed by the
28 # ipapi instance should be read from there, but we also will often need to use
29 # the actual IPython one.
30
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
40 IP = ip.IP # This is the actual IPython shell 'raw' object.
41
42 #-----------------------------------------------------------------------------
43 # Test functions
44 #-----------------------------------------------------------------------------
6 45
7 46 def test_reset():
8 47 """reset must clear most namespaces."""
9 ip = _ip.IP
10 ip.reset() # first, it should run without error
48 IP.reset() # first, it should run without error
11 49 # Then, check that most namespaces end up empty
12 for ns in ip.ns_refs_table:
13 if ns is ip.user_ns:
50 for ns in IP.ns_refs_table:
51 if ns is IP.user_ns:
14 52 # The user namespace is reset with some data, so we can't check for
15 53 # it being empty
16 54 continue
17 55 nt.assert_equals(len(ns),0)
56
57
58 # make sure that user_setup can be run re-entrantly in 'install' mode.
59 def test_user_setup():
60 # use a lambda to pass kwargs to the generator
61 user_setup = lambda a,k: iplib.user_setup(*a,**k)
62 kw = dict(mode='install', interactive=False)
63
64 # Call the user setup and verify that the directory exists
65 yield user_setup, (ip.options.ipythondir,''), kw
66 yield os.path.isdir, ip.options.ipythondir
67
68 # Now repeat the operation with a non-existent directory. Check both that
69 # the call succeeds and that the directory is created.
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.
73 try:
74 yield user_setup, (tmpdir,''), kw
75 yield os.path.isdir, tmpdir
76 except:
77 pass
78 # Clean up the temp dir once done
79 shutil.rmtree(tmpdir)
80 No newline at end of file
@@ -3,15 +3,16 b''
3 3 Needs to be run by nose (to make ipython session available).
4 4 """
5 5
6 # Standard library imports
7 6 import os
8 7 import sys
8 import tempfile
9 import types
9 10
10 # Third-party imports
11 11 import nose.tools as nt
12 12
13 # From our own code
13 from IPython.platutils import find_cmd, get_long_path_name
14 14 from IPython.testing import decorators as dec
15 from IPython.testing import tools as tt
15 16
16 17 #-----------------------------------------------------------------------------
17 18 # Test functions begin
@@ -34,28 +35,6 b' def test_rehashx():'
34 35 assert len(scoms) > 10
35 36
36 37
37 def doctest_run_ns():
38 """Classes declared %run scripts must be instantiable afterwards.
39
40 In [11]: run tclass
41
42 In [12]: isinstance(f(),foo)
43 Out[12]: True
44 """
45
46
47 def doctest_run_ns2():
48 """Classes declared %run scripts must be instantiable afterwards.
49
50 In [3]: run tclass.py
51
52 In [4]: run tclass first_pass
53
54 In [5]: run tclass second_pass
55 Deleting object: first_pass
56 """
57
58
59 38 def doctest_hist_f():
60 39 """Test %hist -f with temporary filename.
61 40
@@ -79,13 +58,15 b' def doctest_hist_r():'
79 58 hist -n -r 2 # random
80 59 """
81 60
82
61 # This test is known to fail on win32.
62 # See ticket https://bugs.launchpad.net/bugs/366334
83 63 def test_obj_del():
84 64 """Test that object's __del__ methods are called on exit."""
85 65 test_dir = os.path.dirname(__file__)
86 66 del_file = os.path.join(test_dir,'obj_del.py')
87 out = _ip.IP.getoutput('ipython %s' % del_file)
88 nt.assert_equals(out,'object A deleted')
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')
89 70
90 71
91 72 def test_shist():
@@ -133,3 +114,136 b' def test_fail_dec2(*a,**k):'
133 114 def test_fail_dec3(*a,**k):
134 115 yield nt.assert_true, False
135 116
117
118 def doctest_refbug():
119 """Very nasty problem with references held by multiple runs of a script.
120 See: https://bugs.launchpad.net/ipython/+bug/269966
121
122 In [1]: _ip.IP.clear_main_mod_cache()
123
124 In [2]: run refbug
125
126 In [3]: call_f()
127 lowercased: hello
128
129 In [4]: run refbug
130
131 In [5]: call_f()
132 lowercased: hello
133 lowercased: hello
134 """
135
136 #-----------------------------------------------------------------------------
137 # Tests for %run
138 #-----------------------------------------------------------------------------
139
140 # %run is critical enough that it's a good idea to have a solid collection of
141 # tests for it, some as doctests and some as normal tests.
142
143 def doctest_run_ns():
144 """Classes declared %run scripts must be instantiable afterwards.
145
146 In [11]: run tclass foo
147
148 In [12]: isinstance(f(),foo)
149 Out[12]: True
150 """
151
152
153 def doctest_run_ns2():
154 """Classes declared %run scripts must be instantiable afterwards.
155
156 In [4]: run tclass C-first_pass
157
158 In [5]: run tclass C-second_pass
159 tclass.py: deleting object: C-first_pass
160 """
161
162 @dec.skip_win32
163 def doctest_run_builtins():
164 """Check that %run doesn't damage __builtins__ via a doctest.
165
166 This is similar to the test_run_builtins, but I want *both* forms of the
167 test to catch any possible glitches in our testing machinery, since that
168 modifies %run somewhat. So for this, we have both a normal test (below)
169 and a doctest (this one).
170
171 In [1]: import tempfile
172
173 In [2]: bid1 = id(__builtins__)
174
175 In [3]: f = tempfile.NamedTemporaryFile()
176
177 In [4]: f.write('pass\\n')
178
179 In [5]: f.flush()
180
181 In [6]: print 'B1:',type(__builtins__)
182 B1: <type 'module'>
183
184 In [7]: %run $f.name
185
186 In [8]: bid2 = id(__builtins__)
187
188 In [9]: print 'B2:',type(__builtins__)
189 B2: <type 'module'>
190
191 In [10]: bid1 == bid2
192 Out[10]: True
193 """
194
195 # For some tests, it will be handy to organize them in a class with a common
196 # setup that makes a temp file
197
198 class TestMagicRun(object):
199
200 def setup(self):
201 """Make a valid python temp file."""
202 f = tempfile.NamedTemporaryFile()
203 f.write('pass\n')
204 f.flush()
205 self.tmpfile = f
206
207 def run_tmpfile(self):
208 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
209 # See below and ticket https://bugs.launchpad.net/bugs/366353
210 _ip.magic('run %s' % self.tmpfile.name)
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
219 def test_builtins_id(self):
220 """Check that %run doesn't damage __builtins__ """
221
222 # Test that the id of __builtins__ is not modified by %run
223 bid1 = id(_ip.user_ns['__builtins__'])
224 self.run_tmpfile()
225 bid2 = id(_ip.user_ns['__builtins__'])
226 tt.assert_equals(bid1, bid2)
227
228 # See https://bugs.launchpad.net/bugs/366353
229 @dec.skip_win32
230 def test_builtins_type(self):
231 """Check that the type of __builtins__ doesn't change with %run.
232
233 However, the above could pass if __builtins__ was already modified to
234 be a dict (it should be a module) by a previous use of %run. So we
235 also check explicitly that it really is a module:
236 """
237 self.run_tmpfile()
238 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
239
240 # See https://bugs.launchpad.net/bugs/366353
241 @dec.skip_win32
242 def test_prompts(self):
243 """Test that prompts correctly generate after %run"""
244 self.run_tmpfile()
245 p2 = str(_ip.IP.outputcache.prompt2).strip()
246 nt.assert_equals(p2[:3], '...')
247
248 def teardown(self):
249 self.tmpfile.close()
@@ -25,6 +25,7 b" if __name__ == '__main__':"
25 25 r'\.cocoa',
26 26 r'\.ipdoctest',
27 27 r'\.Gnuplot',
28 r'\.frontend.process.winprocess',
28 29 ]
29 30 docwriter.write_api_docs(outdir)
30 31 docwriter.write_index(outdir, 'gen',
@@ -75,6 +75,9 b' Bug fixes'
75 75 The block is assigned to pasted_block even if code
76 76 raises exception.
77 77
78 * Bug #274067 'The code in get_home_dir is broken for py2exe' was
79 fixed.
80
78 81 Backwards incompatible changes
79 82 ------------------------------
80 83
@@ -345,6 +345,37 b' nosetests option. For example, you can use ``--pdb`` or ``--pdb-failures`` to'
345 345 automatically activate the interactive Pdb debugger on errors or failures. See
346 346 the nosetests documentation for further details.
347 347
348 .. warning::
349
350 Note that right now we have a nasty interaction between ipdoctest and
351 twisted. Until we figure this out, please use the following instructions to
352 ensure that at least you run all the tests.
353
354 Right now, if you now run::
355
356 $ iptest [any options] [any submodules]
357
358 it will NOT load ipdoctest but won't cause any Twisted problems.
359
360 Once you're happy that you didn't break Twisted, run::
361
362 $ iptest --with-ipdoctest [any options] [any submodules]
363
364 This MAY give a Twisted AlreadyCalledError exception at the end, but it will
365 also correctly load up all of the ipython-specific tests and doctests.
366
367 The above can be made easier with a trivial shell alias::
368
369 $ alias iptest2='iptest --with-ipdoctest'
370
371 So that you can run::
372
373 $ iptest ...
374 # Twisted happy
375 # iptest2 ...
376 # ignore possible Twisted error, this checks all the rest.
377
378
348 379 A few tips for writing tests
349 380 ----------------------------
350 381
@@ -302,18 +302,33 b' The ``--furl-file`` flag works like this::'
302 302 Make FURL files persistent
303 303 ---------------------------
304 304
305 At fist glance it may seem that that managing the FURL files is a bit annoying. Going back to the house and key analogy, copying the FURL around each time you start the controller is like having to make a new key every time you want to unlock the door and enter your house. As with your house, you want to be able to create the key (or FURL file) once, and then simply use it at any point in the future.
305 At fist glance it may seem that that managing the FURL files is a bit
306 annoying. Going back to the house and key analogy, copying the FURL around
307 each time you start the controller is like having to make a new key every time
308 you want to unlock the door and enter your house. As with your house, you want
309 to be able to create the key (or FURL file) once, and then simply use it at
310 any point in the future.
306 311
307 This is possible. The only thing you have to do is decide what ports the controller will listen on for the engines and clients. This is done as follows::
312 This is possible, but before you do this, you **must** remove any old FURL
313 files in the :file:`~/.ipython/security` directory.
314
315 .. warning::
316
317 You **must** remove old FURL files before using persistent FURL files.
318
319 Then, The only thing you have to do is decide what ports the controller will
320 listen on for the engines and clients. This is done as follows::
308 321
309 322 $ ipcontroller -r --client-port=10101 --engine-port=10102
310 323
311 These options also work with all of the various modes of
324 These options also work with all of the various modes of
312 325 :command:`ipcluster`::
313 326
314 327 $ ipcluster local -n 2 -r --client-port=10101 --engine-port=10102
315 328
316 Then, just copy the furl files over the first time and you are set. You can start and stop the controller and engines any many times as you want in the future, just make sure to tell the controller to use the *same* ports.
329 Then, just copy the furl files over the first time and you are set. You can
330 start and stop the controller and engines any many times as you want in the
331 future, just make sure to tell the controller to use the *same* ports.
317 332
318 333 .. note::
319 334
@@ -2,6 +2,10 b''
2 2 IPython/Vision Beam Pattern Demo
3 3 ==================================
4 4
5 .. note::
6
7 This page has not been updated to reflect the recent work on ipcluster.
8 This work makes it much easier to use IPython on a cluster.
5 9
6 10 Installing and testing IPython at OSC systems
7 11 =============================================
@@ -5,9 +5,15 b''
5 5
6 6 from docutils.nodes import Body, Element
7 7 from docutils.writers.html4css1 import HTMLTranslator
8 from sphinx.latexwriter import LaTeXTranslator
9 8 from docutils.parsers.rst import directives
10 9
10 # The sphinx API has changed, so we try both the old and new import forms
11 try:
12 from sphinx.latexwriter import LaTeXTranslator
13 except ImportError:
14 from sphinx.writers.latex import LaTeXTranslator
15
16
11 17 class html_only(Body, Element):
12 18 pass
13 19
@@ -109,7 +109,7 b' def find_packages():'
109 109 add_package(packages, 'gui')
110 110 add_package(packages, 'gui.wx')
111 111 add_package(packages, 'frontend', tests=True)
112 add_package(packages, 'frontend._process')
112 add_package(packages, 'frontend.process')
113 113 add_package(packages, 'frontend.wx')
114 114 add_package(packages, 'frontend.cocoa', tests=True)
115 115 add_package(packages, 'kernel', config=True, tests=True, scripts=True)
General Comments 0
You need to be logged in to leave comments. Login now