##// END OF EJS Templates
Replace links to launchpad bugs in comments/docstrings with equivalent github links.
Thomas Kluyver -
Show More
@@ -1,47 +1,47 b''
1 """Minimal script to reproduce our nasty reference counting bug.
1 """Minimal script to reproduce our nasty reference counting bug.
2
2
3 The problem is related to https://bugs.launchpad.net/ipython/+bug/269966
3 The problem is related to https://github.com/ipython/ipython/issues/141
4
4
5 The original fix for that appeared to work, but John D. Hunter found a
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
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.
7 were references held by open figures to internals of Tkinter.
8
8
9 This code reproduces the problem that John saw, without matplotlib.
9 This code reproduces the problem that John saw, without matplotlib.
10
10
11 This script is meant to be called by other parts of the test suite that call it
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,
12 via %run as if it were executed interactively by the user. As of 2011-05-29,
13 test_magic.py calls it.
13 test_run.py calls it.
14 """
14 """
15
15
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17 # Module imports
17 # Module imports
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 import sys
19 import sys
20
20
21 from IPython.core import ipapi
21 from IPython.core import ipapi
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Globals
24 # Globals
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27 # This needs to be here because nose and other test runners will import
27 # This needs to be here because nose and other test runners will import
28 # this module. Importing this module has potential side effects that we
28 # this module. Importing this module has potential side effects that we
29 # want to prevent.
29 # want to prevent.
30 if __name__ == '__main__':
30 if __name__ == '__main__':
31
31
32 ip = ipapi.get()
32 ip = ipapi.get()
33
33
34 if not '_refbug_cache' in ip.user_ns:
34 if not '_refbug_cache' in ip.user_ns:
35 ip.user_ns['_refbug_cache'] = []
35 ip.user_ns['_refbug_cache'] = []
36
36
37
37
38 aglobal = 'Hello'
38 aglobal = 'Hello'
39 def f():
39 def f():
40 return aglobal
40 return aglobal
41
41
42 cache = ip.user_ns['_refbug_cache']
42 cache = ip.user_ns['_refbug_cache']
43 cache.append(f)
43 cache.append(f)
44
44
45 def call_f():
45 def call_f():
46 for func in cache:
46 for func in cache:
47 print 'lowercased:',func().lower()
47 print 'lowercased:',func().lower()
@@ -1,68 +1,68 b''
1 """Tests for input manipulation machinery."""
1 """Tests for input manipulation machinery."""
2
2
3 #-----------------------------------------------------------------------------
3 #-----------------------------------------------------------------------------
4 # Imports
4 # Imports
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 import nose.tools as nt
6 import nose.tools as nt
7
7
8 from IPython.testing import tools as tt, decorators as dec
8 from IPython.testing import tools as tt, decorators as dec
9 from IPython.testing.globalipapp import get_ipython
9 from IPython.testing.globalipapp import get_ipython
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Tests
12 # Tests
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 ip = get_ipython()
14 ip = get_ipython()
15
15
16 @dec.parametric
16 @dec.parametric
17 def test_prefilter():
17 def test_prefilter():
18 """Test user input conversions"""
18 """Test user input conversions"""
19
19
20 # pairs of (raw, expected correct) input
20 # pairs of (raw, expected correct) input
21 pairs = [ ('2+2','2+2'),
21 pairs = [ ('2+2','2+2'),
22 ('>>> 2+2','2+2'),
22 ('>>> 2+2','2+2'),
23 ('>>> # This is a comment\n'
23 ('>>> # This is a comment\n'
24 '... 2+2',
24 '... 2+2',
25 '# This is a comment\n'
25 '# This is a comment\n'
26 '2+2'),
26 '2+2'),
27 # Some IPython input
27 # Some IPython input
28 ('In [1]: 1', '1'),
28 ('In [1]: 1', '1'),
29 ('In [2]: for i in range(5):\n'
29 ('In [2]: for i in range(5):\n'
30 ' ...: print i,',
30 ' ...: print i,',
31 'for i in range(5):\n'
31 'for i in range(5):\n'
32 ' print i,'),
32 ' print i,'),
33 ]
33 ]
34
34
35 for raw, correct in pairs:
35 for raw, correct in pairs:
36 yield nt.assert_equals(ip.prefilter(raw), correct)
36 yield nt.assert_equals(ip.prefilter(raw), correct)
37
37
38 @dec.parametric
38 @dec.parametric
39 def test_autocall_binops():
39 def test_autocall_binops():
40 """See https://bugs.launchpad.net/ipython/+bug/315706"""
40 """See https://github.com/ipython/ipython/issues/81"""
41 ip.magic('autocall 2')
41 ip.magic('autocall 2')
42 f = lambda x: x
42 f = lambda x: x
43 ip.user_ns['f'] = f
43 ip.user_ns['f'] = f
44 try:
44 try:
45 yield nt.assert_equals(ip.prefilter('f 1'),'f(1)')
45 yield nt.assert_equals(ip.prefilter('f 1'),'f(1)')
46 for t in ['f +1', 'f -1']:
46 for t in ['f +1', 'f -1']:
47 yield nt.assert_equals(ip.prefilter(t), t)
47 yield nt.assert_equals(ip.prefilter(t), t)
48 finally:
48 finally:
49 ip.magic('autocall 0')
49 ip.magic('autocall 0')
50 del ip.user_ns['f']
50 del ip.user_ns['f']
51
51
52 @dec.parametric
52 @dec.parametric
53 def test_issue114():
53 def test_issue114():
54 """Check that multiline string literals don't expand as magic
54 """Check that multiline string literals don't expand as magic
55 see http://github.com/ipython/ipython/issues/#issue/114"""
55 see http://github.com/ipython/ipython/issues/#issue/114"""
56
56
57 template = '"""\n%s\n"""'
57 template = '"""\n%s\n"""'
58 # Store the current value of multi_line_specials and turn it off before
58 # Store the current value of multi_line_specials and turn it off before
59 # running test, since it could be true (case in which the test doesn't make
59 # running test, since it could be true (case in which the test doesn't make
60 # sense, as multiline string literals *will* expand as magic in that case).
60 # sense, as multiline string literals *will* expand as magic in that case).
61 msp = ip.prefilter_manager.multi_line_specials
61 msp = ip.prefilter_manager.multi_line_specials
62 ip.prefilter_manager.multi_line_specials = False
62 ip.prefilter_manager.multi_line_specials = False
63 try:
63 try:
64 for mgk in ip.lsmagic():
64 for mgk in ip.lsmagic():
65 raw = template % mgk
65 raw = template % mgk
66 yield nt.assert_equals(ip.prefilter(raw), raw)
66 yield nt.assert_equals(ip.prefilter(raw), raw)
67 finally:
67 finally:
68 ip.prefilter_manager.multi_line_specials = msp
68 ip.prefilter_manager.multi_line_specials = msp
@@ -1,205 +1,203 b''
1 """Tests for code execution (%run and related), which is particularly tricky.
1 """Tests for code execution (%run and related), which is particularly tricky.
2
2
3 Because of how %run manages namespaces, and the fact that we are trying here to
3 Because of how %run manages namespaces, and the fact that we are trying here to
4 verify subtle object deletion and reference counting issues, the %run tests
4 verify subtle object deletion and reference counting issues, the %run tests
5 will be kept in this separate file. This makes it easier to aggregate in one
5 will be kept in this separate file. This makes it easier to aggregate in one
6 place the tricks needed to handle it; most other magics are much easier to test
6 place the tricks needed to handle it; most other magics are much easier to test
7 and we do so in a common test_magic file.
7 and we do so in a common test_magic file.
8 """
8 """
9 from __future__ import absolute_import
9 from __future__ import absolute_import
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Imports
12 # Imports
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 import os
15 import os
16 import sys
16 import sys
17 import tempfile
17 import tempfile
18
18
19 import nose.tools as nt
19 import nose.tools as nt
20
20
21 from IPython.testing import decorators as dec
21 from IPython.testing import decorators as dec
22 from IPython.testing import tools as tt
22 from IPython.testing import tools as tt
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Test functions begin
25 # Test functions begin
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 def doctest_refbug():
28 def doctest_refbug():
29 """Very nasty problem with references held by multiple runs of a script.
29 """Very nasty problem with references held by multiple runs of a script.
30 See: https://bugs.launchpad.net/ipython/+bug/269966
30 See: https://github.com/ipython/ipython/issues/141
31
31
32 In [1]: _ip.clear_main_mod_cache()
32 In [1]: _ip.clear_main_mod_cache()
33 # random
33 # random
34
34
35 In [2]: %run refbug
35 In [2]: %run refbug
36
36
37 In [3]: call_f()
37 In [3]: call_f()
38 lowercased: hello
38 lowercased: hello
39
39
40 In [4]: %run refbug
40 In [4]: %run refbug
41
41
42 In [5]: call_f()
42 In [5]: call_f()
43 lowercased: hello
43 lowercased: hello
44 lowercased: hello
44 lowercased: hello
45 """
45 """
46
46
47
47
48 def doctest_run_builtins():
48 def doctest_run_builtins():
49 r"""Check that %run doesn't damage __builtins__.
49 r"""Check that %run doesn't damage __builtins__.
50
50
51 In [1]: import tempfile
51 In [1]: import tempfile
52
52
53 In [2]: bid1 = id(__builtins__)
53 In [2]: bid1 = id(__builtins__)
54
54
55 In [3]: fname = tempfile.mkstemp('.py')[1]
55 In [3]: fname = tempfile.mkstemp('.py')[1]
56
56
57 In [3]: f = open(fname,'w')
57 In [3]: f = open(fname,'w')
58
58
59 In [4]: f.write('pass\n')
59 In [4]: f.write('pass\n')
60
60
61 In [5]: f.flush()
61 In [5]: f.flush()
62
62
63 In [6]: t1 = type(__builtins__)
63 In [6]: t1 = type(__builtins__)
64
64
65 In [7]: %run $fname
65 In [7]: %run $fname
66
66
67 In [7]: f.close()
67 In [7]: f.close()
68
68
69 In [8]: bid2 = id(__builtins__)
69 In [8]: bid2 = id(__builtins__)
70
70
71 In [9]: t2 = type(__builtins__)
71 In [9]: t2 = type(__builtins__)
72
72
73 In [10]: t1 == t2
73 In [10]: t1 == t2
74 Out[10]: True
74 Out[10]: True
75
75
76 In [10]: bid1 == bid2
76 In [10]: bid1 == bid2
77 Out[10]: True
77 Out[10]: True
78
78
79 In [12]: try:
79 In [12]: try:
80 ....: os.unlink(fname)
80 ....: os.unlink(fname)
81 ....: except:
81 ....: except:
82 ....: pass
82 ....: pass
83 ....:
83 ....:
84 """
84 """
85
85
86 def doctest_reset_del():
86 def doctest_reset_del():
87 """Test that resetting doesn't cause errors in __del__ methods.
87 """Test that resetting doesn't cause errors in __del__ methods.
88
88
89 In [2]: class A(object):
89 In [2]: class A(object):
90 ...: def __del__(self):
90 ...: def __del__(self):
91 ...: print str("Hi")
91 ...: print str("Hi")
92 ...:
92 ...:
93
93
94 In [3]: a = A()
94 In [3]: a = A()
95
95
96 In [4]: get_ipython().reset()
96 In [4]: get_ipython().reset()
97 Hi
97 Hi
98
98
99 In [5]: 1+1
99 In [5]: 1+1
100 Out[5]: 2
100 Out[5]: 2
101 """
101 """
102
102
103 # For some tests, it will be handy to organize them in a class with a common
103 # For some tests, it will be handy to organize them in a class with a common
104 # setup that makes a temp file
104 # setup that makes a temp file
105
105
106 class TestMagicRunPass(tt.TempFileMixin):
106 class TestMagicRunPass(tt.TempFileMixin):
107
107
108 def setup(self):
108 def setup(self):
109 """Make a valid python temp file."""
109 """Make a valid python temp file."""
110 self.mktmp('pass\n')
110 self.mktmp('pass\n')
111
111
112 def run_tmpfile(self):
112 def run_tmpfile(self):
113 _ip = get_ipython()
113 _ip = get_ipython()
114 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
114 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
115 # See below and ticket https://bugs.launchpad.net/bugs/366353
115 # See below and ticket https://bugs.launchpad.net/bugs/366353
116 _ip.magic('run %s' % self.fname)
116 _ip.magic('run %s' % self.fname)
117
117
118 def test_builtins_id(self):
118 def test_builtins_id(self):
119 """Check that %run doesn't damage __builtins__ """
119 """Check that %run doesn't damage __builtins__ """
120 _ip = get_ipython()
120 _ip = get_ipython()
121 # Test that the id of __builtins__ is not modified by %run
121 # Test that the id of __builtins__ is not modified by %run
122 bid1 = id(_ip.user_ns['__builtins__'])
122 bid1 = id(_ip.user_ns['__builtins__'])
123 self.run_tmpfile()
123 self.run_tmpfile()
124 bid2 = id(_ip.user_ns['__builtins__'])
124 bid2 = id(_ip.user_ns['__builtins__'])
125 tt.assert_equals(bid1, bid2)
125 tt.assert_equals(bid1, bid2)
126
126
127 def test_builtins_type(self):
127 def test_builtins_type(self):
128 """Check that the type of __builtins__ doesn't change with %run.
128 """Check that the type of __builtins__ doesn't change with %run.
129
129
130 However, the above could pass if __builtins__ was already modified to
130 However, the above could pass if __builtins__ was already modified to
131 be a dict (it should be a module) by a previous use of %run. So we
131 be a dict (it should be a module) by a previous use of %run. So we
132 also check explicitly that it really is a module:
132 also check explicitly that it really is a module:
133 """
133 """
134 _ip = get_ipython()
134 _ip = get_ipython()
135 self.run_tmpfile()
135 self.run_tmpfile()
136 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
136 tt.assert_equals(type(_ip.user_ns['__builtins__']),type(sys))
137
137
138 def test_prompts(self):
138 def test_prompts(self):
139 """Test that prompts correctly generate after %run"""
139 """Test that prompts correctly generate after %run"""
140 self.run_tmpfile()
140 self.run_tmpfile()
141 _ip = get_ipython()
141 _ip = get_ipython()
142 p2 = str(_ip.displayhook.prompt2).strip()
142 p2 = str(_ip.displayhook.prompt2).strip()
143 nt.assert_equals(p2[:3], '...')
143 nt.assert_equals(p2[:3], '...')
144
144
145
145
146 class TestMagicRunSimple(tt.TempFileMixin):
146 class TestMagicRunSimple(tt.TempFileMixin):
147
147
148 def test_simpledef(self):
148 def test_simpledef(self):
149 """Test that simple class definitions work."""
149 """Test that simple class definitions work."""
150 src = ("class foo: pass\n"
150 src = ("class foo: pass\n"
151 "def f(): return foo()")
151 "def f(): return foo()")
152 self.mktmp(src)
152 self.mktmp(src)
153 _ip.magic('run %s' % self.fname)
153 _ip.magic('run %s' % self.fname)
154 _ip.run_cell('t = isinstance(f(), foo)')
154 _ip.run_cell('t = isinstance(f(), foo)')
155 nt.assert_true(_ip.user_ns['t'])
155 nt.assert_true(_ip.user_ns['t'])
156
156
157 def test_obj_del(self):
157 def test_obj_del(self):
158 """Test that object's __del__ methods are called on exit."""
158 """Test that object's __del__ methods are called on exit."""
159
159
160 # This test is known to fail on win32.
161 # See ticket https://bugs.launchpad.net/bugs/366334
162 src = ("class A(object):\n"
160 src = ("class A(object):\n"
163 " def __del__(self):\n"
161 " def __del__(self):\n"
164 " print 'object A deleted'\n"
162 " print 'object A deleted'\n"
165 "a = A()\n")
163 "a = A()\n")
166 self.mktmp(src)
164 self.mktmp(src)
167 tt.ipexec_validate(self.fname, 'object A deleted')
165 tt.ipexec_validate(self.fname, 'object A deleted')
168
166
169 @dec.skip_known_failure
167 @dec.skip_known_failure
170 def test_aggressive_namespace_cleanup(self):
168 def test_aggressive_namespace_cleanup(self):
171 """Test that namespace cleanup is not too aggressive GH-238
169 """Test that namespace cleanup is not too aggressive GH-238
172
170
173 Returning from another run magic deletes the namespace"""
171 Returning from another run magic deletes the namespace"""
174 # see ticket https://github.com/ipython/ipython/issues/238
172 # see ticket https://github.com/ipython/ipython/issues/238
175 class secondtmp(tt.TempFileMixin): pass
173 class secondtmp(tt.TempFileMixin): pass
176 empty = secondtmp()
174 empty = secondtmp()
177 empty.mktmp('')
175 empty.mktmp('')
178 src = ("ip = get_ipython()\n"
176 src = ("ip = get_ipython()\n"
179 "for i in range(5):\n"
177 "for i in range(5):\n"
180 " try:\n"
178 " try:\n"
181 " ip.magic('run %s')\n"
179 " ip.magic('run %s')\n"
182 " except NameError, e:\n"
180 " except NameError, e:\n"
183 " print i;break\n" % empty.fname)
181 " print i;break\n" % empty.fname)
184 self.mktmp(src)
182 self.mktmp(src)
185 _ip.magic('run %s' % self.fname)
183 _ip.magic('run %s' % self.fname)
186 _ip.run_cell('ip == get_ipython()')
184 _ip.run_cell('ip == get_ipython()')
187 tt.assert_equals(_ip.user_ns['i'], 5)
185 tt.assert_equals(_ip.user_ns['i'], 5)
188
186
189 @dec.skip_win32
187 @dec.skip_win32
190 def test_tclass(self):
188 def test_tclass(self):
191 mydir = os.path.dirname(__file__)
189 mydir = os.path.dirname(__file__)
192 tc = os.path.join(mydir, 'tclass')
190 tc = os.path.join(mydir, 'tclass')
193 src = ("%%run '%s' C-first\n"
191 src = ("%%run '%s' C-first\n"
194 "%%run '%s' C-second\n"
192 "%%run '%s' C-second\n"
195 "%%run '%s' C-third\n") % (tc, tc, tc)
193 "%%run '%s' C-third\n") % (tc, tc, tc)
196 self.mktmp(src, '.ipy')
194 self.mktmp(src, '.ipy')
197 out = """\
195 out = """\
198 ARGV 1-: [u'C-first']
196 ARGV 1-: [u'C-first']
199 ARGV 1-: [u'C-second']
197 ARGV 1-: [u'C-second']
200 tclass.py: deleting object: C-first
198 tclass.py: deleting object: C-first
201 ARGV 1-: [u'C-third']
199 ARGV 1-: [u'C-third']
202 tclass.py: deleting object: C-second
200 tclass.py: deleting object: C-second
203 tclass.py: deleting object: C-third
201 tclass.py: deleting object: C-third
204 """
202 """
205 tt.ipexec_validate(self.fname, out)
203 tt.ipexec_validate(self.fname, out)
@@ -1,443 +1,443 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
3
4 This module provides a main entry point to a user script to test IPython
4 This module provides a main entry point to a user script to test IPython
5 itself from the command line. There are two ways of running this script:
5 itself from the command line. There are two ways of running this script:
6
6
7 1. With the syntax `iptest all`. This runs our entire test suite by
7 1. With the syntax `iptest all`. This runs our entire test suite by
8 calling this script (with different arguments) recursively. This
8 calling this script (with different arguments) recursively. This
9 causes modules and package to be tested in different processes, using nose
9 causes modules and package to be tested in different processes, using nose
10 or trial where appropriate.
10 or trial where appropriate.
11 2. With the regular nose syntax, like `iptest -vvs IPython`. In this form
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
12 the script simply calls nose, but with special command line flags and
13 plugins loaded.
13 plugins loaded.
14
14
15 """
15 """
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2009 The IPython Development Team
18 # Copyright (C) 2009 The IPython Development Team
19 #
19 #
20 # Distributed under the terms of the BSD License. The full license is in
20 # Distributed under the terms of the BSD License. The full license is in
21 # the file COPYING, distributed as part of this software.
21 # the file COPYING, distributed as part of this software.
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Imports
25 # Imports
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 # Stdlib
28 # Stdlib
29 import os
29 import os
30 import os.path as path
30 import os.path as path
31 import signal
31 import signal
32 import sys
32 import sys
33 import subprocess
33 import subprocess
34 import tempfile
34 import tempfile
35 import time
35 import time
36 import warnings
36 import warnings
37
37
38 # Note: monkeypatch!
38 # Note: monkeypatch!
39 # We need to monkeypatch a small problem in nose itself first, before importing
39 # We need to monkeypatch a small problem in nose itself first, before importing
40 # it for actual use. This should get into nose upstream, but its release cycle
40 # it for actual use. This should get into nose upstream, but its release cycle
41 # is slow and we need it for our parametric tests to work correctly.
41 # is slow and we need it for our parametric tests to work correctly.
42 from IPython.testing import nosepatch
42 from IPython.testing import nosepatch
43 # Now, proceed to import nose itself
43 # Now, proceed to import nose itself
44 import nose.plugins.builtin
44 import nose.plugins.builtin
45 from nose.core import TestProgram
45 from nose.core import TestProgram
46
46
47 # Our own imports
47 # Our own imports
48 from IPython.utils.path import get_ipython_module_path
48 from IPython.utils.path import get_ipython_module_path
49 from IPython.utils.process import find_cmd, pycmd2argv
49 from IPython.utils.process import find_cmd, pycmd2argv
50 from IPython.utils.sysinfo import sys_info
50 from IPython.utils.sysinfo import sys_info
51
51
52 from IPython.testing import globalipapp
52 from IPython.testing import globalipapp
53 from IPython.testing.plugin.ipdoctest import IPythonDoctest
53 from IPython.testing.plugin.ipdoctest import IPythonDoctest
54 from IPython.external.decorators import KnownFailure
54 from IPython.external.decorators import KnownFailure
55
55
56 pjoin = path.join
56 pjoin = path.join
57
57
58
58
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Globals
60 # Globals
61 #-----------------------------------------------------------------------------
61 #-----------------------------------------------------------------------------
62
62
63
63
64 #-----------------------------------------------------------------------------
64 #-----------------------------------------------------------------------------
65 # Warnings control
65 # Warnings control
66 #-----------------------------------------------------------------------------
66 #-----------------------------------------------------------------------------
67
67
68 # Twisted generates annoying warnings with Python 2.6, as will do other code
68 # Twisted generates annoying warnings with Python 2.6, as will do other code
69 # that imports 'sets' as of today
69 # that imports 'sets' as of today
70 warnings.filterwarnings('ignore', 'the sets module is deprecated',
70 warnings.filterwarnings('ignore', 'the sets module is deprecated',
71 DeprecationWarning )
71 DeprecationWarning )
72
72
73 # This one also comes from Twisted
73 # This one also comes from Twisted
74 warnings.filterwarnings('ignore', 'the sha module is deprecated',
74 warnings.filterwarnings('ignore', 'the sha module is deprecated',
75 DeprecationWarning)
75 DeprecationWarning)
76
76
77 # Wx on Fedora11 spits these out
77 # Wx on Fedora11 spits these out
78 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
78 warnings.filterwarnings('ignore', 'wxPython/wxWidgets release number mismatch',
79 UserWarning)
79 UserWarning)
80
80
81 #-----------------------------------------------------------------------------
81 #-----------------------------------------------------------------------------
82 # Logic for skipping doctests
82 # Logic for skipping doctests
83 #-----------------------------------------------------------------------------
83 #-----------------------------------------------------------------------------
84
84
85 def test_for(mod, min_version=None):
85 def test_for(mod, min_version=None):
86 """Test to see if mod is importable."""
86 """Test to see if mod is importable."""
87 try:
87 try:
88 __import__(mod)
88 __import__(mod)
89 except (ImportError, RuntimeError):
89 except (ImportError, RuntimeError):
90 # GTK reports Runtime error if it can't be initialized even if it's
90 # GTK reports Runtime error if it can't be initialized even if it's
91 # importable.
91 # importable.
92 return False
92 return False
93 else:
93 else:
94 if min_version:
94 if min_version:
95 return sys.modules[mod].__version__ >= min_version
95 return sys.modules[mod].__version__ >= min_version
96 else:
96 else:
97 return True
97 return True
98
98
99 # Global dict where we can store information on what we have and what we don't
99 # Global dict where we can store information on what we have and what we don't
100 # have available at test run time
100 # have available at test run time
101 have = {}
101 have = {}
102
102
103 have['curses'] = test_for('_curses')
103 have['curses'] = test_for('_curses')
104 have['matplotlib'] = test_for('matplotlib')
104 have['matplotlib'] = test_for('matplotlib')
105 have['pexpect'] = test_for('pexpect')
105 have['pexpect'] = test_for('pexpect')
106 have['pymongo'] = test_for('pymongo')
106 have['pymongo'] = test_for('pymongo')
107 have['wx'] = test_for('wx')
107 have['wx'] = test_for('wx')
108 have['wx.aui'] = test_for('wx.aui')
108 have['wx.aui'] = test_for('wx.aui')
109 if os.name == 'nt':
109 if os.name == 'nt':
110 have['zmq'] = test_for('zmq', '2.1.7')
110 have['zmq'] = test_for('zmq', '2.1.7')
111 else:
111 else:
112 have['zmq'] = test_for('zmq', '2.1.4')
112 have['zmq'] = test_for('zmq', '2.1.4')
113 have['qt'] = test_for('IPython.external.qt')
113 have['qt'] = test_for('IPython.external.qt')
114
114
115 #-----------------------------------------------------------------------------
115 #-----------------------------------------------------------------------------
116 # Functions and classes
116 # Functions and classes
117 #-----------------------------------------------------------------------------
117 #-----------------------------------------------------------------------------
118
118
119 def report():
119 def report():
120 """Return a string with a summary report of test-related variables."""
120 """Return a string with a summary report of test-related variables."""
121
121
122 out = [ sys_info(), '\n']
122 out = [ sys_info(), '\n']
123
123
124 avail = []
124 avail = []
125 not_avail = []
125 not_avail = []
126
126
127 for k, is_avail in have.items():
127 for k, is_avail in have.items():
128 if is_avail:
128 if is_avail:
129 avail.append(k)
129 avail.append(k)
130 else:
130 else:
131 not_avail.append(k)
131 not_avail.append(k)
132
132
133 if avail:
133 if avail:
134 out.append('\nTools and libraries available at test time:\n')
134 out.append('\nTools and libraries available at test time:\n')
135 avail.sort()
135 avail.sort()
136 out.append(' ' + ' '.join(avail)+'\n')
136 out.append(' ' + ' '.join(avail)+'\n')
137
137
138 if not_avail:
138 if not_avail:
139 out.append('\nTools and libraries NOT available at test time:\n')
139 out.append('\nTools and libraries NOT available at test time:\n')
140 not_avail.sort()
140 not_avail.sort()
141 out.append(' ' + ' '.join(not_avail)+'\n')
141 out.append(' ' + ' '.join(not_avail)+'\n')
142
142
143 return ''.join(out)
143 return ''.join(out)
144
144
145
145
146 def make_exclude():
146 def make_exclude():
147 """Make patterns of modules and packages to exclude from testing.
147 """Make patterns of modules and packages to exclude from testing.
148
148
149 For the IPythonDoctest plugin, we need to exclude certain patterns that
149 For the IPythonDoctest plugin, we need to exclude certain patterns that
150 cause testing problems. We should strive to minimize the number of
150 cause testing problems. We should strive to minimize the number of
151 skipped modules, since this means untested code.
151 skipped modules, since this means untested code.
152
152
153 These modules and packages will NOT get scanned by nose at all for tests.
153 These modules and packages will NOT get scanned by nose at all for tests.
154 """
154 """
155 # Simple utility to make IPython paths more readably, we need a lot of
155 # Simple utility to make IPython paths more readably, we need a lot of
156 # these below
156 # these below
157 ipjoin = lambda *paths: pjoin('IPython', *paths)
157 ipjoin = lambda *paths: pjoin('IPython', *paths)
158
158
159 exclusions = [ipjoin('external'),
159 exclusions = [ipjoin('external'),
160 pjoin('IPython_doctest_plugin'),
160 pjoin('IPython_doctest_plugin'),
161 ipjoin('quarantine'),
161 ipjoin('quarantine'),
162 ipjoin('deathrow'),
162 ipjoin('deathrow'),
163 ipjoin('testing', 'attic'),
163 ipjoin('testing', 'attic'),
164 # This guy is probably attic material
164 # This guy is probably attic material
165 ipjoin('testing', 'mkdoctests'),
165 ipjoin('testing', 'mkdoctests'),
166 # Testing inputhook will need a lot of thought, to figure out
166 # Testing inputhook will need a lot of thought, to figure out
167 # how to have tests that don't lock up with the gui event
167 # how to have tests that don't lock up with the gui event
168 # loops in the picture
168 # loops in the picture
169 ipjoin('lib', 'inputhook'),
169 ipjoin('lib', 'inputhook'),
170 # Config files aren't really importable stand-alone
170 # Config files aren't really importable stand-alone
171 ipjoin('config', 'default'),
171 ipjoin('config', 'default'),
172 ipjoin('config', 'profile'),
172 ipjoin('config', 'profile'),
173 ]
173 ]
174
174
175 if not have['wx']:
175 if not have['wx']:
176 exclusions.append(ipjoin('lib', 'inputhookwx'))
176 exclusions.append(ipjoin('lib', 'inputhookwx'))
177
177
178 # We do this unconditionally, so that the test suite doesn't import
178 # We do this unconditionally, so that the test suite doesn't import
179 # gtk, changing the default encoding and masking some unicode bugs.
179 # gtk, changing the default encoding and masking some unicode bugs.
180 exclusions.append(ipjoin('lib', 'inputhookgtk'))
180 exclusions.append(ipjoin('lib', 'inputhookgtk'))
181
181
182 # These have to be skipped on win32 because the use echo, rm, cd, etc.
182 # These have to be skipped on win32 because the use echo, rm, cd, etc.
183 # See ticket https://bugs.launchpad.net/bugs/366982
183 # See ticket https://github.com/ipython/ipython/issues/87
184 if sys.platform == 'win32':
184 if sys.platform == 'win32':
185 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
185 exclusions.append(ipjoin('testing', 'plugin', 'test_exampleip'))
186 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
186 exclusions.append(ipjoin('testing', 'plugin', 'dtexample'))
187
187
188 if not have['pexpect']:
188 if not have['pexpect']:
189 exclusions.extend([ipjoin('scripts', 'irunner'),
189 exclusions.extend([ipjoin('scripts', 'irunner'),
190 ipjoin('lib', 'irunner'),
190 ipjoin('lib', 'irunner'),
191 ipjoin('lib', 'tests', 'test_irunner')])
191 ipjoin('lib', 'tests', 'test_irunner')])
192
192
193 if not have['zmq']:
193 if not have['zmq']:
194 exclusions.append(ipjoin('zmq'))
194 exclusions.append(ipjoin('zmq'))
195 exclusions.append(ipjoin('frontend', 'qt'))
195 exclusions.append(ipjoin('frontend', 'qt'))
196 exclusions.append(ipjoin('parallel'))
196 exclusions.append(ipjoin('parallel'))
197 elif not have['qt']:
197 elif not have['qt']:
198 exclusions.append(ipjoin('frontend', 'qt'))
198 exclusions.append(ipjoin('frontend', 'qt'))
199
199
200 if not have['pymongo']:
200 if not have['pymongo']:
201 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
201 exclusions.append(ipjoin('parallel', 'controller', 'mongodb'))
202 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
202 exclusions.append(ipjoin('parallel', 'tests', 'test_mongodb'))
203
203
204 if not have['matplotlib']:
204 if not have['matplotlib']:
205 exclusions.extend([ipjoin('lib', 'pylabtools'),
205 exclusions.extend([ipjoin('lib', 'pylabtools'),
206 ipjoin('lib', 'tests', 'test_pylabtools')])
206 ipjoin('lib', 'tests', 'test_pylabtools')])
207
207
208 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
208 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
209 if sys.platform == 'win32':
209 if sys.platform == 'win32':
210 exclusions = [s.replace('\\','\\\\') for s in exclusions]
210 exclusions = [s.replace('\\','\\\\') for s in exclusions]
211
211
212 return exclusions
212 return exclusions
213
213
214
214
215 class IPTester(object):
215 class IPTester(object):
216 """Call that calls iptest or trial in a subprocess.
216 """Call that calls iptest or trial in a subprocess.
217 """
217 """
218 #: string, name of test runner that will be called
218 #: string, name of test runner that will be called
219 runner = None
219 runner = None
220 #: list, parameters for test runner
220 #: list, parameters for test runner
221 params = None
221 params = None
222 #: list, arguments of system call to be made to call test runner
222 #: list, arguments of system call to be made to call test runner
223 call_args = None
223 call_args = None
224 #: list, process ids of subprocesses we start (for cleanup)
224 #: list, process ids of subprocesses we start (for cleanup)
225 pids = None
225 pids = None
226
226
227 def __init__(self, runner='iptest', params=None):
227 def __init__(self, runner='iptest', params=None):
228 """Create new test runner."""
228 """Create new test runner."""
229 p = os.path
229 p = os.path
230 if runner == 'iptest':
230 if runner == 'iptest':
231 iptest_app = get_ipython_module_path('IPython.testing.iptest')
231 iptest_app = get_ipython_module_path('IPython.testing.iptest')
232 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
232 self.runner = pycmd2argv(iptest_app) + sys.argv[1:]
233 else:
233 else:
234 raise Exception('Not a valid test runner: %s' % repr(runner))
234 raise Exception('Not a valid test runner: %s' % repr(runner))
235 if params is None:
235 if params is None:
236 params = []
236 params = []
237 if isinstance(params, str):
237 if isinstance(params, str):
238 params = [params]
238 params = [params]
239 self.params = params
239 self.params = params
240
240
241 # Assemble call
241 # Assemble call
242 self.call_args = self.runner+self.params
242 self.call_args = self.runner+self.params
243
243
244 # Store pids of anything we start to clean up on deletion, if possible
244 # Store pids of anything we start to clean up on deletion, if possible
245 # (on posix only, since win32 has no os.kill)
245 # (on posix only, since win32 has no os.kill)
246 self.pids = []
246 self.pids = []
247
247
248 if sys.platform == 'win32':
248 if sys.platform == 'win32':
249 def _run_cmd(self):
249 def _run_cmd(self):
250 # On Windows, use os.system instead of subprocess.call, because I
250 # On Windows, use os.system instead of subprocess.call, because I
251 # was having problems with subprocess and I just don't know enough
251 # was having problems with subprocess and I just don't know enough
252 # about win32 to debug this reliably. Os.system may be the 'old
252 # about win32 to debug this reliably. Os.system may be the 'old
253 # fashioned' way to do it, but it works just fine. If someone
253 # fashioned' way to do it, but it works just fine. If someone
254 # later can clean this up that's fine, as long as the tests run
254 # later can clean this up that's fine, as long as the tests run
255 # reliably in win32.
255 # reliably in win32.
256 # What types of problems are you having. They may be related to
256 # What types of problems are you having. They may be related to
257 # running Python in unboffered mode. BG.
257 # running Python in unboffered mode. BG.
258 return os.system(' '.join(self.call_args))
258 return os.system(' '.join(self.call_args))
259 else:
259 else:
260 def _run_cmd(self):
260 def _run_cmd(self):
261 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
261 # print >> sys.stderr, '*** CMD:', ' '.join(self.call_args) # dbg
262 subp = subprocess.Popen(self.call_args)
262 subp = subprocess.Popen(self.call_args)
263 self.pids.append(subp.pid)
263 self.pids.append(subp.pid)
264 # If this fails, the pid will be left in self.pids and cleaned up
264 # If this fails, the pid will be left in self.pids and cleaned up
265 # later, but if the wait call succeeds, then we can clear the
265 # later, but if the wait call succeeds, then we can clear the
266 # stored pid.
266 # stored pid.
267 retcode = subp.wait()
267 retcode = subp.wait()
268 self.pids.pop()
268 self.pids.pop()
269 return retcode
269 return retcode
270
270
271 def run(self):
271 def run(self):
272 """Run the stored commands"""
272 """Run the stored commands"""
273 try:
273 try:
274 return self._run_cmd()
274 return self._run_cmd()
275 except:
275 except:
276 import traceback
276 import traceback
277 traceback.print_exc()
277 traceback.print_exc()
278 return 1 # signal failure
278 return 1 # signal failure
279
279
280 def __del__(self):
280 def __del__(self):
281 """Cleanup on exit by killing any leftover processes."""
281 """Cleanup on exit by killing any leftover processes."""
282
282
283 if not hasattr(os, 'kill'):
283 if not hasattr(os, 'kill'):
284 return
284 return
285
285
286 for pid in self.pids:
286 for pid in self.pids:
287 try:
287 try:
288 print 'Cleaning stale PID:', pid
288 print 'Cleaning stale PID:', pid
289 os.kill(pid, signal.SIGKILL)
289 os.kill(pid, signal.SIGKILL)
290 except OSError:
290 except OSError:
291 # This is just a best effort, if we fail or the process was
291 # This is just a best effort, if we fail or the process was
292 # really gone, ignore it.
292 # really gone, ignore it.
293 pass
293 pass
294
294
295
295
296 def make_runners():
296 def make_runners():
297 """Define the top-level packages that need to be tested.
297 """Define the top-level packages that need to be tested.
298 """
298 """
299
299
300 # Packages to be tested via nose, that only depend on the stdlib
300 # Packages to be tested via nose, that only depend on the stdlib
301 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
301 nose_pkg_names = ['config', 'core', 'extensions', 'frontend', 'lib',
302 'scripts', 'testing', 'utils' ]
302 'scripts', 'testing', 'utils' ]
303
303
304 if have['zmq']:
304 if have['zmq']:
305 nose_pkg_names.append('parallel')
305 nose_pkg_names.append('parallel')
306
306
307 # For debugging this code, only load quick stuff
307 # For debugging this code, only load quick stuff
308 #nose_pkg_names = ['core', 'extensions'] # dbg
308 #nose_pkg_names = ['core', 'extensions'] # dbg
309
309
310 # Make fully qualified package names prepending 'IPython.' to our name lists
310 # Make fully qualified package names prepending 'IPython.' to our name lists
311 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
311 nose_packages = ['IPython.%s' % m for m in nose_pkg_names ]
312
312
313 # Make runners
313 # Make runners
314 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
314 runners = [ (v, IPTester('iptest', params=v)) for v in nose_packages ]
315
315
316 return runners
316 return runners
317
317
318
318
319 def run_iptest():
319 def run_iptest():
320 """Run the IPython test suite using nose.
320 """Run the IPython test suite using nose.
321
321
322 This function is called when this script is **not** called with the form
322 This function is called when this script is **not** called with the form
323 `iptest all`. It simply calls nose with appropriate command line flags
323 `iptest all`. It simply calls nose with appropriate command line flags
324 and accepts all of the standard nose arguments.
324 and accepts all of the standard nose arguments.
325 """
325 """
326
326
327 warnings.filterwarnings('ignore',
327 warnings.filterwarnings('ignore',
328 'This will be removed soon. Use IPython.testing.util instead')
328 'This will be removed soon. Use IPython.testing.util instead')
329
329
330 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
330 argv = sys.argv + [ '--detailed-errors', # extra info in tracebacks
331
331
332 # Loading ipdoctest causes problems with Twisted, but
332 # Loading ipdoctest causes problems with Twisted, but
333 # our test suite runner now separates things and runs
333 # our test suite runner now separates things and runs
334 # all Twisted tests with trial.
334 # all Twisted tests with trial.
335 '--with-ipdoctest',
335 '--with-ipdoctest',
336 '--ipdoctest-tests','--ipdoctest-extension=txt',
336 '--ipdoctest-tests','--ipdoctest-extension=txt',
337
337
338 # We add --exe because of setuptools' imbecility (it
338 # We add --exe because of setuptools' imbecility (it
339 # blindly does chmod +x on ALL files). Nose does the
339 # blindly does chmod +x on ALL files). Nose does the
340 # right thing and it tries to avoid executables,
340 # right thing and it tries to avoid executables,
341 # setuptools unfortunately forces our hand here. This
341 # setuptools unfortunately forces our hand here. This
342 # has been discussed on the distutils list and the
342 # has been discussed on the distutils list and the
343 # setuptools devs refuse to fix this problem!
343 # setuptools devs refuse to fix this problem!
344 '--exe',
344 '--exe',
345 ]
345 ]
346
346
347 if nose.__version__ >= '0.11':
347 if nose.__version__ >= '0.11':
348 # I don't fully understand why we need this one, but depending on what
348 # I don't fully understand why we need this one, but depending on what
349 # directory the test suite is run from, if we don't give it, 0 tests
349 # directory the test suite is run from, if we don't give it, 0 tests
350 # get run. Specifically, if the test suite is run from the source dir
350 # get run. Specifically, if the test suite is run from the source dir
351 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
351 # with an argument (like 'iptest.py IPython.core', 0 tests are run,
352 # even if the same call done in this directory works fine). It appears
352 # even if the same call done in this directory works fine). It appears
353 # that if the requested package is in the current dir, nose bails early
353 # that if the requested package is in the current dir, nose bails early
354 # by default. Since it's otherwise harmless, leave it in by default
354 # by default. Since it's otherwise harmless, leave it in by default
355 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
355 # for nose >= 0.11, though unfortunately nose 0.10 doesn't support it.
356 argv.append('--traverse-namespace')
356 argv.append('--traverse-namespace')
357
357
358 # Construct list of plugins, omitting the existing doctest plugin, which
358 # Construct list of plugins, omitting the existing doctest plugin, which
359 # ours replaces (and extends).
359 # ours replaces (and extends).
360 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
360 plugins = [IPythonDoctest(make_exclude()), KnownFailure()]
361 for p in nose.plugins.builtin.plugins:
361 for p in nose.plugins.builtin.plugins:
362 plug = p()
362 plug = p()
363 if plug.name == 'doctest':
363 if plug.name == 'doctest':
364 continue
364 continue
365 plugins.append(plug)
365 plugins.append(plug)
366
366
367 # We need a global ipython running in this process
367 # We need a global ipython running in this process
368 globalipapp.start_ipython()
368 globalipapp.start_ipython()
369 # Now nose can run
369 # Now nose can run
370 TestProgram(argv=argv, plugins=plugins)
370 TestProgram(argv=argv, plugins=plugins)
371
371
372
372
373 def run_iptestall():
373 def run_iptestall():
374 """Run the entire IPython test suite by calling nose and trial.
374 """Run the entire IPython test suite by calling nose and trial.
375
375
376 This function constructs :class:`IPTester` instances for all IPython
376 This function constructs :class:`IPTester` instances for all IPython
377 modules and package and then runs each of them. This causes the modules
377 modules and package and then runs each of them. This causes the modules
378 and packages of IPython to be tested each in their own subprocess using
378 and packages of IPython to be tested each in their own subprocess using
379 nose or twisted.trial appropriately.
379 nose or twisted.trial appropriately.
380 """
380 """
381
381
382 runners = make_runners()
382 runners = make_runners()
383
383
384 # Run the test runners in a temporary dir so we can nuke it when finished
384 # Run the test runners in a temporary dir so we can nuke it when finished
385 # to clean up any junk files left over by accident. This also makes it
385 # to clean up any junk files left over by accident. This also makes it
386 # robust against being run in non-writeable directories by mistake, as the
386 # robust against being run in non-writeable directories by mistake, as the
387 # temp dir will always be user-writeable.
387 # temp dir will always be user-writeable.
388 curdir = os.getcwd()
388 curdir = os.getcwd()
389 testdir = tempfile.gettempdir()
389 testdir = tempfile.gettempdir()
390 os.chdir(testdir)
390 os.chdir(testdir)
391
391
392 # Run all test runners, tracking execution time
392 # Run all test runners, tracking execution time
393 failed = []
393 failed = []
394 t_start = time.time()
394 t_start = time.time()
395 try:
395 try:
396 for (name, runner) in runners:
396 for (name, runner) in runners:
397 print '*'*70
397 print '*'*70
398 print 'IPython test group:',name
398 print 'IPython test group:',name
399 res = runner.run()
399 res = runner.run()
400 if res:
400 if res:
401 failed.append( (name, runner) )
401 failed.append( (name, runner) )
402 finally:
402 finally:
403 os.chdir(curdir)
403 os.chdir(curdir)
404 t_end = time.time()
404 t_end = time.time()
405 t_tests = t_end - t_start
405 t_tests = t_end - t_start
406 nrunners = len(runners)
406 nrunners = len(runners)
407 nfail = len(failed)
407 nfail = len(failed)
408 # summarize results
408 # summarize results
409 print
409 print
410 print '*'*70
410 print '*'*70
411 print 'Test suite completed for system with the following information:'
411 print 'Test suite completed for system with the following information:'
412 print report()
412 print report()
413 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
413 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
414 print
414 print
415 print 'Status:'
415 print 'Status:'
416 if not failed:
416 if not failed:
417 print 'OK'
417 print 'OK'
418 else:
418 else:
419 # If anything went wrong, point out what command to rerun manually to
419 # If anything went wrong, point out what command to rerun manually to
420 # see the actual errors and individual summary
420 # see the actual errors and individual summary
421 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
421 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
422 for name, failed_runner in failed:
422 for name, failed_runner in failed:
423 print '-'*40
423 print '-'*40
424 print 'Runner failed:',name
424 print 'Runner failed:',name
425 print 'You may wish to rerun this one individually, with:'
425 print 'You may wish to rerun this one individually, with:'
426 print ' '.join(failed_runner.call_args)
426 print ' '.join(failed_runner.call_args)
427 print
427 print
428 # Ensure that our exit code indicates failure
428 # Ensure that our exit code indicates failure
429 sys.exit(1)
429 sys.exit(1)
430
430
431
431
432 def main():
432 def main():
433 for arg in sys.argv[1:]:
433 for arg in sys.argv[1:]:
434 if arg.startswith('IPython'):
434 if arg.startswith('IPython'):
435 # This is in-process
435 # This is in-process
436 run_iptest()
436 run_iptest()
437 else:
437 else:
438 # This starts subprocesses
438 # This starts subprocesses
439 run_iptestall()
439 run_iptestall()
440
440
441
441
442 if __name__ == '__main__':
442 if __name__ == '__main__':
443 main()
443 main()
General Comments 0
You need to be logged in to leave comments. Login now