##// END OF EJS Templates
First failed attempt to get the test suite to run.
Brian Granger -
Show More
@@ -1,7 +1,5 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 def test_import_userconfig():
5 from IPython.config import userconfig
6
4
7
5
@@ -1,65 +1,62 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3
3
4 def test_import_completer():
4 def test_import_completer():
5 from IPython.core import completer
5 from IPython.core import completer
6
6
7 def test_import_crashhandler():
7 def test_import_crashhandler():
8 from IPython.core import crashhandler
8 from IPython.core import crashhandler
9
9
10 def test_import_debugger():
10 def test_import_debugger():
11 from IPython.core import debugger
11 from IPython.core import debugger
12
12
13 def test_import_fakemodule():
13 def test_import_fakemodule():
14 from IPython.core import fakemodule
14 from IPython.core import fakemodule
15
15
16 def test_import_excolors():
16 def test_import_excolors():
17 from IPython.core import excolors
17 from IPython.core import excolors
18
18
19 def test_import_history():
19 def test_import_history():
20 from IPython.core import history
20 from IPython.core import history
21
21
22 def test_import_hooks():
22 def test_import_hooks():
23 from IPython.core import hooks
23 from IPython.core import hooks
24
24
25 def test_import_ipapi():
25 def test_import_ipapi():
26 from IPython.core import ipapi
26 from IPython.core import ipapi
27
27
28 def test_import_iplib():
28 def test_import_iplib():
29 from IPython.core import iplib
29 from IPython.core import iplib
30
30
31 def test_import_logger():
31 def test_import_logger():
32 from IPython.core import logger
32 from IPython.core import logger
33
33
34 def test_import_macro():
34 def test_import_macro():
35 from IPython.core import macro
35 from IPython.core import macro
36
36
37 def test_import_magic():
37 def test_import_magic():
38 from IPython.core import magic
38 from IPython.core import magic
39
39
40 def test_import_oinspect():
40 def test_import_oinspect():
41 from IPython.core import oinspect
41 from IPython.core import oinspect
42
42
43 def test_import_outputtrap():
43 def test_import_outputtrap():
44 from IPython.core import outputtrap
44 from IPython.core import outputtrap
45
45
46 def test_import_prefilter():
46 def test_import_prefilter():
47 from IPython.core import prefilter
47 from IPython.core import prefilter
48
48
49 def test_import_prompts():
49 def test_import_prompts():
50 from IPython.core import prompts
50 from IPython.core import prompts
51
51
52 def test_import_release():
52 def test_import_release():
53 from IPython.core import release
53 from IPython.core import release
54
54
55 def test_import_shadowns():
55 def test_import_shadowns():
56 from IPython.core import shadowns
56 from IPython.core import shadowns
57
57
58 def test_import_shell():
59 from IPython.core import shell
60
61 def test_import_ultratb():
58 def test_import_ultratb():
62 from IPython.core import ultratb
59 from IPython.core import ultratb
63
60
64 def test_import_usage():
61 def test_import_usage():
65 from IPython.core import usage
62 from IPython.core import usage
@@ -1,441 +1,441 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Module for interactively running scripts.
2 """Module for interactively running scripts.
3
3
4 This module implements classes for interactively running scripts written for
4 This module implements classes for interactively running scripts written for
5 any system with a prompt which can be matched by a regexp suitable for
5 any system with a prompt which can be matched by a regexp suitable for
6 pexpect. It can be used to run as if they had been typed up interactively, an
6 pexpect. It can be used to run as if they had been typed up interactively, an
7 arbitrary series of commands for the target system.
7 arbitrary series of commands for the target system.
8
8
9 The module includes classes ready for IPython (with the default prompts),
9 The module includes classes ready for IPython (with the default prompts),
10 plain Python and SAGE, but making a new one is trivial. To see how to use it,
10 plain Python and SAGE, but making a new one is trivial. To see how to use it,
11 simply run the module as a script:
11 simply run the module as a script:
12
12
13 ./irunner.py --help
13 ./irunner.py --help
14
14
15
15
16 This is an extension of Ken Schutte <kschutte-AT-csail.mit.edu>'s script
16 This is an extension of Ken Schutte <kschutte-AT-csail.mit.edu>'s script
17 contributed on the ipython-user list:
17 contributed on the ipython-user list:
18
18
19 http://scipy.net/pipermail/ipython-user/2006-May/001705.html
19 http://scipy.net/pipermail/ipython-user/2006-May/001705.html
20
20
21
21
22 NOTES:
22 NOTES:
23
23
24 - This module requires pexpect, available in most linux distros, or which can
24 - This module requires pexpect, available in most linux distros, or which can
25 be downloaded from
25 be downloaded from
26
26
27 http://pexpect.sourceforge.net
27 http://pexpect.sourceforge.net
28
28
29 - Because pexpect only works under Unix or Windows-Cygwin, this has the same
29 - Because pexpect only works under Unix or Windows-Cygwin, this has the same
30 limitations. This means that it will NOT work under native windows Python.
30 limitations. This means that it will NOT work under native windows Python.
31 """
31 """
32
32
33 # Stdlib imports
33 # Stdlib imports
34 import optparse
34 import optparse
35 import os
35 import os
36 import sys
36 import sys
37
37
38 # Third-party modules.
38 # Third-party modules.
39 import pexpect
39 import pexpect
40
40
41 # Global usage strings, to avoid indentation issues when typing it below.
41 # Global usage strings, to avoid indentation issues when typing it below.
42 USAGE = """
42 USAGE = """
43 Interactive script runner, type: %s
43 Interactive script runner, type: %s
44
44
45 runner [opts] script_name
45 runner [opts] script_name
46 """
46 """
47
47
48 def pexpect_monkeypatch():
48 def pexpect_monkeypatch():
49 """Patch pexpect to prevent unhandled exceptions at VM teardown.
49 """Patch pexpect to prevent unhandled exceptions at VM teardown.
50
50
51 Calling this function will monkeypatch the pexpect.spawn class and modify
51 Calling this function will monkeypatch the pexpect.spawn class and modify
52 its __del__ method to make it more robust in the face of failures that can
52 its __del__ method to make it more robust in the face of failures that can
53 occur if it is called when the Python VM is shutting down.
53 occur if it is called when the Python VM is shutting down.
54
54
55 Since Python may fire __del__ methods arbitrarily late, it's possible for
55 Since Python may fire __del__ methods arbitrarily late, it's possible for
56 them to execute during the teardown of the Python VM itself. At this
56 them to execute during the teardown of the Python VM itself. At this
57 point, various builtin modules have been reset to None. Thus, the call to
57 point, various builtin modules have been reset to None. Thus, the call to
58 self.close() will trigger an exception because it tries to call os.close(),
58 self.close() will trigger an exception because it tries to call os.close(),
59 and os is now None.
59 and os is now None.
60 """
60 """
61
61
62 if pexpect.__version__[:3] >= '2.2':
62 if pexpect.__version__[:3] >= '2.2':
63 # No need to patch, fix is already the upstream version.
63 # No need to patch, fix is already the upstream version.
64 return
64 return
65
65
66 def __del__(self):
66 def __del__(self):
67 """This makes sure that no system resources are left open.
67 """This makes sure that no system resources are left open.
68 Python only garbage collects Python objects. OS file descriptors
68 Python only garbage collects Python objects. OS file descriptors
69 are not Python objects, so they must be handled explicitly.
69 are not Python objects, so they must be handled explicitly.
70 If the child file descriptor was opened outside of this class
70 If the child file descriptor was opened outside of this class
71 (passed to the constructor) then this does not close it.
71 (passed to the constructor) then this does not close it.
72 """
72 """
73 if not self.closed:
73 if not self.closed:
74 try:
74 try:
75 self.close()
75 self.close()
76 except AttributeError:
76 except AttributeError:
77 pass
77 pass
78
78
79 pexpect.spawn.__del__ = __del__
79 pexpect.spawn.__del__ = __del__
80
80
81 pexpect_monkeypatch()
81 pexpect_monkeypatch()
82
82
83 # The generic runner class
83 # The generic runner class
84 class InteractiveRunner(object):
84 class InteractiveRunner(object):
85 """Class to run a sequence of commands through an interactive program."""
85 """Class to run a sequence of commands through an interactive program."""
86
86
87 def __init__(self,program,prompts,args=None,out=sys.stdout,echo=True):
87 def __init__(self,program,prompts,args=None,out=sys.stdout,echo=True):
88 """Construct a runner.
88 """Construct a runner.
89
89
90 Inputs:
90 Inputs:
91
91
92 - program: command to execute the given program.
92 - program: command to execute the given program.
93
93
94 - prompts: a list of patterns to match as valid prompts, in the
94 - prompts: a list of patterns to match as valid prompts, in the
95 format used by pexpect. This basically means that it can be either
95 format used by pexpect. This basically means that it can be either
96 a string (to be compiled as a regular expression) or a list of such
96 a string (to be compiled as a regular expression) or a list of such
97 (it must be a true list, as pexpect does type checks).
97 (it must be a true list, as pexpect does type checks).
98
98
99 If more than one prompt is given, the first is treated as the main
99 If more than one prompt is given, the first is treated as the main
100 program prompt and the others as 'continuation' prompts, like
100 program prompt and the others as 'continuation' prompts, like
101 python's. This means that blank lines in the input source are
101 python's. This means that blank lines in the input source are
102 ommitted when the first prompt is matched, but are NOT ommitted when
102 ommitted when the first prompt is matched, but are NOT ommitted when
103 the continuation one matches, since this is how python signals the
103 the continuation one matches, since this is how python signals the
104 end of multiline input interactively.
104 end of multiline input interactively.
105
105
106 Optional inputs:
106 Optional inputs:
107
107
108 - args(None): optional list of strings to pass as arguments to the
108 - args(None): optional list of strings to pass as arguments to the
109 child program.
109 child program.
110
110
111 - out(sys.stdout): if given, an output stream to be used when writing
111 - out(sys.stdout): if given, an output stream to be used when writing
112 output. The only requirement is that it must have a .write() method.
112 output. The only requirement is that it must have a .write() method.
113
113
114 Public members not parameterized in the constructor:
114 Public members not parameterized in the constructor:
115
115
116 - delaybeforesend(0): Newer versions of pexpect have a delay before
116 - delaybeforesend(0): Newer versions of pexpect have a delay before
117 sending each new input. For our purposes here, it's typically best
117 sending each new input. For our purposes here, it's typically best
118 to just set this to zero, but if you encounter reliability problems
118 to just set this to zero, but if you encounter reliability problems
119 or want an interactive run to pause briefly at each prompt, just
119 or want an interactive run to pause briefly at each prompt, just
120 increase this value (it is measured in seconds). Note that this
120 increase this value (it is measured in seconds). Note that this
121 variable is not honored at all by older versions of pexpect.
121 variable is not honored at all by older versions of pexpect.
122 """
122 """
123
123
124 self.program = program
124 self.program = program
125 self.prompts = prompts
125 self.prompts = prompts
126 if args is None: args = []
126 if args is None: args = []
127 self.args = args
127 self.args = args
128 self.out = out
128 self.out = out
129 self.echo = echo
129 self.echo = echo
130 # Other public members which we don't make as parameters, but which
130 # Other public members which we don't make as parameters, but which
131 # users may occasionally want to tweak
131 # users may occasionally want to tweak
132 self.delaybeforesend = 0
132 self.delaybeforesend = 0
133
133
134 # Create child process and hold on to it so we don't have to re-create
134 # Create child process and hold on to it so we don't have to re-create
135 # for every single execution call
135 # for every single execution call
136 c = self.child = pexpect.spawn(self.program,self.args,timeout=None)
136 c = self.child = pexpect.spawn(self.program,self.args,timeout=None)
137 c.delaybeforesend = self.delaybeforesend
137 c.delaybeforesend = self.delaybeforesend
138 # pexpect hard-codes the terminal size as (24,80) (rows,columns).
138 # pexpect hard-codes the terminal size as (24,80) (rows,columns).
139 # This causes problems because any line longer than 80 characters gets
139 # This causes problems because any line longer than 80 characters gets
140 # completely overwrapped on the printed outptut (even though
140 # completely overwrapped on the printed outptut (even though
141 # internally the code runs fine). We reset this to 99 rows X 200
141 # internally the code runs fine). We reset this to 99 rows X 200
142 # columns (arbitrarily chosen), which should avoid problems in all
142 # columns (arbitrarily chosen), which should avoid problems in all
143 # reasonable cases.
143 # reasonable cases.
144 c.setwinsize(99,200)
144 c.setwinsize(99,200)
145
145
146 def close(self):
146 def close(self):
147 """close child process"""
147 """close child process"""
148
148
149 self.child.close()
149 self.child.close()
150
150
151 def run_file(self,fname,interact=False,get_output=False):
151 def run_file(self,fname,interact=False,get_output=False):
152 """Run the given file interactively.
152 """Run the given file interactively.
153
153
154 Inputs:
154 Inputs:
155
155
156 -fname: name of the file to execute.
156 -fname: name of the file to execute.
157
157
158 See the run_source docstring for the meaning of the optional
158 See the run_source docstring for the meaning of the optional
159 arguments."""
159 arguments."""
160
160
161 fobj = open(fname,'r')
161 fobj = open(fname,'r')
162 try:
162 try:
163 out = self.run_source(fobj,interact,get_output)
163 out = self.run_source(fobj,interact,get_output)
164 finally:
164 finally:
165 fobj.close()
165 fobj.close()
166 if get_output:
166 if get_output:
167 return out
167 return out
168
168
169 def run_source(self,source,interact=False,get_output=False):
169 def run_source(self,source,interact=False,get_output=False):
170 """Run the given source code interactively.
170 """Run the given source code interactively.
171
171
172 Inputs:
172 Inputs:
173
173
174 - source: a string of code to be executed, or an open file object we
174 - source: a string of code to be executed, or an open file object we
175 can iterate over.
175 can iterate over.
176
176
177 Optional inputs:
177 Optional inputs:
178
178
179 - interact(False): if true, start to interact with the running
179 - interact(False): if true, start to interact with the running
180 program at the end of the script. Otherwise, just exit.
180 program at the end of the script. Otherwise, just exit.
181
181
182 - get_output(False): if true, capture the output of the child process
182 - get_output(False): if true, capture the output of the child process
183 (filtering the input commands out) and return it as a string.
183 (filtering the input commands out) and return it as a string.
184
184
185 Returns:
185 Returns:
186 A string containing the process output, but only if requested.
186 A string containing the process output, but only if requested.
187 """
187 """
188
188
189 # if the source is a string, chop it up in lines so we can iterate
189 # if the source is a string, chop it up in lines so we can iterate
190 # over it just as if it were an open file.
190 # over it just as if it were an open file.
191 if not isinstance(source,file):
191 if not isinstance(source,file):
192 source = source.splitlines(True)
192 source = source.splitlines(True)
193
193
194 if self.echo:
194 if self.echo:
195 # normalize all strings we write to use the native OS line
195 # normalize all strings we write to use the native OS line
196 # separators.
196 # separators.
197 linesep = os.linesep
197 linesep = os.linesep
198 stdwrite = self.out.write
198 stdwrite = self.out.write
199 write = lambda s: stdwrite(s.replace('\r\n',linesep))
199 write = lambda s: stdwrite(s.replace('\r\n',linesep))
200 else:
200 else:
201 # Quiet mode, all writes are no-ops
201 # Quiet mode, all writes are no-ops
202 write = lambda s: None
202 write = lambda s: None
203
203
204 c = self.child
204 c = self.child
205 prompts = c.compile_pattern_list(self.prompts)
205 prompts = c.compile_pattern_list(self.prompts)
206 prompt_idx = c.expect_list(prompts)
206 prompt_idx = c.expect_list(prompts)
207
207
208 # Flag whether the script ends normally or not, to know whether we can
208 # Flag whether the script ends normally or not, to know whether we can
209 # do anything further with the underlying process.
209 # do anything further with the underlying process.
210 end_normal = True
210 end_normal = True
211
211
212 # If the output was requested, store it in a list for return at the end
212 # If the output was requested, store it in a list for return at the end
213 if get_output:
213 if get_output:
214 output = []
214 output = []
215 store_output = output.append
215 store_output = output.append
216
216
217 for cmd in source:
217 for cmd in source:
218 # skip blank lines for all matches to the 'main' prompt, while the
218 # skip blank lines for all matches to the 'main' prompt, while the
219 # secondary prompts do not
219 # secondary prompts do not
220 if prompt_idx==0 and \
220 if prompt_idx==0 and \
221 (cmd.isspace() or cmd.lstrip().startswith('#')):
221 (cmd.isspace() or cmd.lstrip().startswith('#')):
222 write(cmd)
222 write(cmd)
223 continue
223 continue
224
224
225 #write('AFTER: '+c.after) # dbg
225 # write('AFTER: '+c.after) # dbg
226 write(c.after)
226 write(c.after)
227 c.send(cmd)
227 c.send(cmd)
228 try:
228 try:
229 prompt_idx = c.expect_list(prompts)
229 prompt_idx = c.expect_list(prompts)
230 except pexpect.EOF:
230 except pexpect.EOF:
231 # this will happen if the child dies unexpectedly
231 # this will happen if the child dies unexpectedly
232 write(c.before)
232 write(c.before)
233 end_normal = False
233 end_normal = False
234 break
234 break
235
235
236 write(c.before)
236 write(c.before)
237
237
238 # With an echoing process, the output we get in c.before contains
238 # With an echoing process, the output we get in c.before contains
239 # the command sent, a newline, and then the actual process output
239 # the command sent, a newline, and then the actual process output
240 if get_output:
240 if get_output:
241 store_output(c.before[len(cmd+'\n'):])
241 store_output(c.before[len(cmd+'\n'):])
242 #write('CMD: <<%s>>' % cmd) # dbg
242 #write('CMD: <<%s>>' % cmd) # dbg
243 #write('OUTPUT: <<%s>>' % output[-1]) # dbg
243 #write('OUTPUT: <<%s>>' % output[-1]) # dbg
244
244
245 self.out.flush()
245 self.out.flush()
246 if end_normal:
246 if end_normal:
247 if interact:
247 if interact:
248 c.send('\n')
248 c.send('\n')
249 print '<< Starting interactive mode >>',
249 print '<< Starting interactive mode >>',
250 try:
250 try:
251 c.interact()
251 c.interact()
252 except OSError:
252 except OSError:
253 # This is what fires when the child stops. Simply print a
253 # This is what fires when the child stops. Simply print a
254 # newline so the system prompt is aligned. The extra
254 # newline so the system prompt is aligned. The extra
255 # space is there to make sure it gets printed, otherwise
255 # space is there to make sure it gets printed, otherwise
256 # OS buffering sometimes just suppresses it.
256 # OS buffering sometimes just suppresses it.
257 write(' \n')
257 write(' \n')
258 self.out.flush()
258 self.out.flush()
259 else:
259 else:
260 if interact:
260 if interact:
261 e="Further interaction is not possible: child process is dead."
261 e="Further interaction is not possible: child process is dead."
262 print >> sys.stderr, e
262 print >> sys.stderr, e
263
263
264 # Leave the child ready for more input later on, otherwise select just
264 # Leave the child ready for more input later on, otherwise select just
265 # hangs on the second invocation.
265 # hangs on the second invocation.
266 c.send('\n')
266 c.send('\n')
267
267
268 # Return any requested output
268 # Return any requested output
269 if get_output:
269 if get_output:
270 return ''.join(output)
270 return ''.join(output)
271
271
272 def main(self,argv=None):
272 def main(self,argv=None):
273 """Run as a command-line script."""
273 """Run as a command-line script."""
274
274
275 parser = optparse.OptionParser(usage=USAGE % self.__class__.__name__)
275 parser = optparse.OptionParser(usage=USAGE % self.__class__.__name__)
276 newopt = parser.add_option
276 newopt = parser.add_option
277 newopt('-i','--interact',action='store_true',default=False,
277 newopt('-i','--interact',action='store_true',default=False,
278 help='Interact with the program after the script is run.')
278 help='Interact with the program after the script is run.')
279
279
280 opts,args = parser.parse_args(argv)
280 opts,args = parser.parse_args(argv)
281
281
282 if len(args) != 1:
282 if len(args) != 1:
283 print >> sys.stderr,"You must supply exactly one file to run."
283 print >> sys.stderr,"You must supply exactly one file to run."
284 sys.exit(1)
284 sys.exit(1)
285
285
286 self.run_file(args[0],opts.interact)
286 self.run_file(args[0],opts.interact)
287
287
288
288
289 # Specific runners for particular programs
289 # Specific runners for particular programs
290 class IPythonRunner(InteractiveRunner):
290 class IPythonRunner(InteractiveRunner):
291 """Interactive IPython runner.
291 """Interactive IPython runner.
292
292
293 This initalizes IPython in 'nocolor' mode for simplicity. This lets us
293 This initalizes IPython in 'nocolor' mode for simplicity. This lets us
294 avoid having to write a regexp that matches ANSI sequences, though pexpect
294 avoid having to write a regexp that matches ANSI sequences, though pexpect
295 does support them. If anyone contributes patches for ANSI color support,
295 does support them. If anyone contributes patches for ANSI color support,
296 they will be welcome.
296 they will be welcome.
297
297
298 It also sets the prompts manually, since the prompt regexps for
298 It also sets the prompts manually, since the prompt regexps for
299 pexpect need to be matched to the actual prompts, so user-customized
299 pexpect need to be matched to the actual prompts, so user-customized
300 prompts would break this.
300 prompts would break this.
301 """
301 """
302
302
303 def __init__(self,program = 'ipython',args=None,out=sys.stdout,echo=True):
303 def __init__(self,program = 'ipython',args=None,out=sys.stdout,echo=True):
304 """New runner, optionally passing the ipython command to use."""
304 """New runner, optionally passing the ipython command to use."""
305
305
306 args0 = ['-colors','NoColor',
306 args0 = ['-colors','NoColor',
307 '-pi1','In [\\#]: ',
307 '-pi1','In [\\#]: ',
308 '-pi2',' .\\D.: ',
308 '-pi2',' .\\D.: ',
309 '-noterm_title',
309 '-noterm_title',
310 '-noautoindent']
310 '-noautoindent']
311 if args is None: args = args0
311 if args is None: args = args0
312 else: args = args0 + args
312 else: args = args0 + args
313 prompts = [r'In \[\d+\]: ',r' \.*: ']
313 prompts = [r'In \[\d+\]: ',r' \.*: ']
314 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
314 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
315
315
316
316
317 class PythonRunner(InteractiveRunner):
317 class PythonRunner(InteractiveRunner):
318 """Interactive Python runner."""
318 """Interactive Python runner."""
319
319
320 def __init__(self,program='python',args=None,out=sys.stdout,echo=True):
320 def __init__(self,program='python',args=None,out=sys.stdout,echo=True):
321 """New runner, optionally passing the python command to use."""
321 """New runner, optionally passing the python command to use."""
322
322
323 prompts = [r'>>> ',r'\.\.\. ']
323 prompts = [r'>>> ',r'\.\.\. ']
324 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
324 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
325
325
326
326
327 class SAGERunner(InteractiveRunner):
327 class SAGERunner(InteractiveRunner):
328 """Interactive SAGE runner.
328 """Interactive SAGE runner.
329
329
330 WARNING: this runner only works if you manually configure your SAGE copy
330 WARNING: this runner only works if you manually configure your SAGE copy
331 to use 'colors NoColor' in the ipythonrc config file, since currently the
331 to use 'colors NoColor' in the ipythonrc config file, since currently the
332 prompt matching regexp does not identify color sequences."""
332 prompt matching regexp does not identify color sequences."""
333
333
334 def __init__(self,program='sage',args=None,out=sys.stdout,echo=True):
334 def __init__(self,program='sage',args=None,out=sys.stdout,echo=True):
335 """New runner, optionally passing the sage command to use."""
335 """New runner, optionally passing the sage command to use."""
336
336
337 prompts = ['sage: ',r'\s*\.\.\. ']
337 prompts = ['sage: ',r'\s*\.\.\. ']
338 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
338 InteractiveRunner.__init__(self,program,prompts,args,out,echo)
339
339
340
340
341 class RunnerFactory(object):
341 class RunnerFactory(object):
342 """Code runner factory.
342 """Code runner factory.
343
343
344 This class provides an IPython code runner, but enforces that only one
344 This class provides an IPython code runner, but enforces that only one
345 runner is ever instantiated. The runner is created based on the extension
345 runner is ever instantiated. The runner is created based on the extension
346 of the first file to run, and it raises an exception if a runner is later
346 of the first file to run, and it raises an exception if a runner is later
347 requested for a different extension type.
347 requested for a different extension type.
348
348
349 This ensures that we don't generate example files for doctest with a mix of
349 This ensures that we don't generate example files for doctest with a mix of
350 python and ipython syntax.
350 python and ipython syntax.
351 """
351 """
352
352
353 def __init__(self,out=sys.stdout):
353 def __init__(self,out=sys.stdout):
354 """Instantiate a code runner."""
354 """Instantiate a code runner."""
355
355
356 self.out = out
356 self.out = out
357 self.runner = None
357 self.runner = None
358 self.runnerClass = None
358 self.runnerClass = None
359
359
360 def _makeRunner(self,runnerClass):
360 def _makeRunner(self,runnerClass):
361 self.runnerClass = runnerClass
361 self.runnerClass = runnerClass
362 self.runner = runnerClass(out=self.out)
362 self.runner = runnerClass(out=self.out)
363 return self.runner
363 return self.runner
364
364
365 def __call__(self,fname):
365 def __call__(self,fname):
366 """Return a runner for the given filename."""
366 """Return a runner for the given filename."""
367
367
368 if fname.endswith('.py'):
368 if fname.endswith('.py'):
369 runnerClass = PythonRunner
369 runnerClass = PythonRunner
370 elif fname.endswith('.ipy'):
370 elif fname.endswith('.ipy'):
371 runnerClass = IPythonRunner
371 runnerClass = IPythonRunner
372 else:
372 else:
373 raise ValueError('Unknown file type for Runner: %r' % fname)
373 raise ValueError('Unknown file type for Runner: %r' % fname)
374
374
375 if self.runner is None:
375 if self.runner is None:
376 return self._makeRunner(runnerClass)
376 return self._makeRunner(runnerClass)
377 else:
377 else:
378 if runnerClass==self.runnerClass:
378 if runnerClass==self.runnerClass:
379 return self.runner
379 return self.runner
380 else:
380 else:
381 e='A runner of type %r can not run file %r' % \
381 e='A runner of type %r can not run file %r' % \
382 (self.runnerClass,fname)
382 (self.runnerClass,fname)
383 raise ValueError(e)
383 raise ValueError(e)
384
384
385
385
386 # Global usage string, to avoid indentation issues if typed in a function def.
386 # Global usage string, to avoid indentation issues if typed in a function def.
387 MAIN_USAGE = """
387 MAIN_USAGE = """
388 %prog [options] file_to_run
388 %prog [options] file_to_run
389
389
390 This is an interface to the various interactive runners available in this
390 This is an interface to the various interactive runners available in this
391 module. If you want to pass specific options to one of the runners, you need
391 module. If you want to pass specific options to one of the runners, you need
392 to first terminate the main options with a '--', and then provide the runner's
392 to first terminate the main options with a '--', and then provide the runner's
393 options. For example:
393 options. For example:
394
394
395 irunner.py --python -- --help
395 irunner.py --python -- --help
396
396
397 will pass --help to the python runner. Similarly,
397 will pass --help to the python runner. Similarly,
398
398
399 irunner.py --ipython -- --interact script.ipy
399 irunner.py --ipython -- --interact script.ipy
400
400
401 will run the script.ipy file under the IPython runner, and then will start to
401 will run the script.ipy file under the IPython runner, and then will start to
402 interact with IPython at the end of the script (instead of exiting).
402 interact with IPython at the end of the script (instead of exiting).
403
403
404 The already implemented runners are listed below; adding one for a new program
404 The already implemented runners are listed below; adding one for a new program
405 is a trivial task, see the source for examples.
405 is a trivial task, see the source for examples.
406
406
407 WARNING: the SAGE runner only works if you manually configure your SAGE copy
407 WARNING: the SAGE runner only works if you manually configure your SAGE copy
408 to use 'colors NoColor' in the ipythonrc config file, since currently the
408 to use 'colors NoColor' in the ipythonrc config file, since currently the
409 prompt matching regexp does not identify color sequences.
409 prompt matching regexp does not identify color sequences.
410 """
410 """
411
411
412 def main():
412 def main():
413 """Run as a command-line script."""
413 """Run as a command-line script."""
414
414
415 parser = optparse.OptionParser(usage=MAIN_USAGE)
415 parser = optparse.OptionParser(usage=MAIN_USAGE)
416 newopt = parser.add_option
416 newopt = parser.add_option
417 parser.set_defaults(mode='ipython')
417 parser.set_defaults(mode='ipython')
418 newopt('--ipython',action='store_const',dest='mode',const='ipython',
418 newopt('--ipython',action='store_const',dest='mode',const='ipython',
419 help='IPython interactive runner (default).')
419 help='IPython interactive runner (default).')
420 newopt('--python',action='store_const',dest='mode',const='python',
420 newopt('--python',action='store_const',dest='mode',const='python',
421 help='Python interactive runner.')
421 help='Python interactive runner.')
422 newopt('--sage',action='store_const',dest='mode',const='sage',
422 newopt('--sage',action='store_const',dest='mode',const='sage',
423 help='SAGE interactive runner.')
423 help='SAGE interactive runner.')
424
424
425 opts,args = parser.parse_args()
425 opts,args = parser.parse_args()
426 runners = dict(ipython=IPythonRunner,
426 runners = dict(ipython=IPythonRunner,
427 python=PythonRunner,
427 python=PythonRunner,
428 sage=SAGERunner)
428 sage=SAGERunner)
429
429
430 try:
430 try:
431 ext = os.path.splitext(args[0])[-1]
431 ext = os.path.splitext(args[0])[-1]
432 except IndexError:
432 except IndexError:
433 ext = ''
433 ext = ''
434 modes = {'.ipy':'ipython',
434 modes = {'.ipy':'ipython',
435 '.py':'python',
435 '.py':'python',
436 '.sage':'sage'}
436 '.sage':'sage'}
437 mode = modes.get(ext,opts.mode)
437 mode = modes.get(ext,opts.mode)
438 runners[mode]().main(args)
438 runners[mode]().main(args)
439
439
440 if __name__ == '__main__':
440 if __name__ == '__main__':
441 main()
441 main()
@@ -1,334 +1,324 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) or trial recursively. This
8 calling this script (with different arguments) or trial 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 For now, this script requires that both nose and twisted are installed. This
15 For now, this script requires that both nose and twisted are installed. This
16 will change in the future.
16 will change in the future.
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Module imports
20 # Module imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 import os
23 import os
24 import os.path as path
24 import os.path as path
25 import sys
25 import sys
26 import subprocess
26 import subprocess
27 import tempfile
27 import tempfile
28 import time
28 import time
29 import warnings
29 import warnings
30
30
31 import nose.plugins.builtin
31 import nose.plugins.builtin
32 from nose.core import TestProgram
32 from nose.core import TestProgram
33
33
34 from IPython.utils.platutils import find_cmd
34 from IPython.utils.platutils import find_cmd
35 from IPython.testing.plugin.ipdoctest import IPythonDoctest
35 # from IPython.testing.plugin.ipdoctest import IPythonDoctest
36
36
37 pjoin = path.join
37 pjoin = path.join
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Logic for skipping doctests
40 # Logic for skipping doctests
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 def test_for(mod):
43 def test_for(mod):
44 """Test to see if mod is importable."""
44 """Test to see if mod is importable."""
45 try:
45 try:
46 __import__(mod)
46 __import__(mod)
47 except ImportError:
47 except ImportError:
48 return False
48 return False
49 else:
49 else:
50 return True
50 return True
51
51
52 have_curses = test_for('_curses')
52 have_curses = test_for('_curses')
53 have_wx = test_for('wx')
53 have_wx = test_for('wx')
54 have_wx_aui = test_for('wx.aui')
54 have_wx_aui = test_for('wx.aui')
55 have_zi = test_for('zope.interface')
55 have_zi = test_for('zope.interface')
56 have_twisted = test_for('twisted')
56 have_twisted = test_for('twisted')
57 have_foolscap = test_for('foolscap')
57 have_foolscap = test_for('foolscap')
58 have_objc = test_for('objc')
58 have_objc = test_for('objc')
59 have_pexpect = test_for('pexpect')
59 have_pexpect = test_for('pexpect')
60 have_gtk = test_for('gtk')
60 have_gtk = test_for('gtk')
61 have_gobject = test_for('gobject')
61 have_gobject = test_for('gobject')
62
62
63
63
64 def make_exclude():
64 def make_exclude():
65
65
66 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
66 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
67 # testing problems. We should strive to minimize the number of skipped
67 # testing problems. We should strive to minimize the number of skipped
68 # modules, since this means untested code. As the testing machinery
68 # modules, since this means untested code. As the testing machinery
69 # solidifies, this list should eventually become empty.
69 # solidifies, this list should eventually become empty.
70 EXCLUDE = [pjoin('IPython', 'external'),
70 EXCLUDE = [pjoin('IPython', 'external'),
71 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
71 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
72 pjoin('IPython_doctest_plugin'),
72 pjoin('IPython_doctest_plugin'),
73 pjoin('IPython', 'extensions', 'ipy_'),
73 pjoin('IPython', 'quarantine'),
74 pjoin('IPython', 'extensions', 'PhysicalQInput'),
74 pjoin('IPython', 'deathrow'),
75 pjoin('IPython', 'extensions', 'PhysicalQInteractive'),
76 pjoin('IPython', 'extensions', 'InterpreterPasteInput'),
77 pjoin('IPython', 'extensions', 'scitedirector'),
78 pjoin('IPython', 'extensions', 'numeric_formats'),
79 pjoin('IPython', 'testing', 'attic'),
75 pjoin('IPython', 'testing', 'attic'),
80 pjoin('IPython', 'testing', 'tools'),
76 pjoin('IPython', 'testing', 'tools'),
81 pjoin('IPython', 'testing', 'mkdoctests'),
77 pjoin('IPython', 'testing', 'mkdoctests'),
82 pjoin('IPython', 'lib', 'inputhook')
78 pjoin('IPython', 'lib', 'inputhook')
83 ]
79 ]
84
80
85 if not have_wx:
81 if not have_wx:
86 EXCLUDE.append(pjoin('IPython', 'extensions', 'igrid'))
87 EXCLUDE.append(pjoin('IPython', 'gui'))
82 EXCLUDE.append(pjoin('IPython', 'gui'))
88 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
83 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
89 EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookwx'))
84 EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookwx'))
90
85
91 if not have_gtk or not have_gobject:
86 if not have_gtk or not have_gobject:
92 EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookgtk'))
87 EXCLUDE.append(pjoin('IPython', 'lib', 'inputhookgtk'))
93
88
94 if not have_wx_aui:
89 if not have_wx_aui:
95 EXCLUDE.append(pjoin('IPython', 'gui', 'wx', 'wxIPython'))
90 EXCLUDE.append(pjoin('IPython', 'gui', 'wx', 'wxIPython'))
96
91
97 if not have_objc:
92 if not have_objc:
98 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
93 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
99
94
100 if not have_curses:
101 EXCLUDE.append(pjoin('IPython', 'extensions', 'ibrowse'))
102
103 if not sys.platform == 'win32':
95 if not sys.platform == 'win32':
104 EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_win32'))
96 EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_win32'))
105
97
106 # These have to be skipped on win32 because the use echo, rm, cd, etc.
98 # These have to be skipped on win32 because the use echo, rm, cd, etc.
107 # See ticket https://bugs.launchpad.net/bugs/366982
99 # See ticket https://bugs.launchpad.net/bugs/366982
108 if sys.platform == 'win32':
100 if sys.platform == 'win32':
109 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
101 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
110 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
102 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
111
103
112 if not os.name == 'posix':
104 if not os.name == 'posix':
113 EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_posix'))
105 EXCLUDE.append(pjoin('IPython', 'utils', 'platutils_posix'))
114
106
115 if not have_pexpect:
107 if not have_pexpect:
116 EXCLUDE.append(pjoin('IPython', 'scripts', 'irunner'))
108 EXCLUDE.append(pjoin('IPython', 'scripts', 'irunner'))
117
109
118 # This is scary. We still have things in frontend and testing that
110 # This is scary. We still have things in frontend and testing that
119 # are being tested by nose that use twisted. We need to rethink
111 # are being tested by nose that use twisted. We need to rethink
120 # how we are isolating dependencies in testing.
112 # how we are isolating dependencies in testing.
121 if not (have_twisted and have_zi and have_foolscap):
113 if not (have_twisted and have_zi and have_foolscap):
122 EXCLUDE.append(pjoin('IPython', 'frontend', 'asyncfrontendbase'))
114 EXCLUDE.append(pjoin('IPython', 'frontend', 'asyncfrontendbase'))
123 EXCLUDE.append(pjoin('IPython', 'frontend', 'prefilterfrontend'))
115 EXCLUDE.append(pjoin('IPython', 'frontend', 'prefilterfrontend'))
124 EXCLUDE.append(pjoin('IPython', 'frontend', 'frontendbase'))
116 EXCLUDE.append(pjoin('IPython', 'frontend', 'frontendbase'))
125 EXCLUDE.append(pjoin('IPython', 'frontend', 'linefrontendbase'))
117 EXCLUDE.append(pjoin('IPython', 'frontend', 'linefrontendbase'))
126 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
118 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
127 'test_linefrontend'))
119 'test_linefrontend'))
128 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
120 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
129 'test_frontendbase'))
121 'test_frontendbase'))
130 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
122 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
131 'test_prefilterfrontend'))
123 'test_prefilterfrontend'))
132 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
124 EXCLUDE.append(pjoin('IPython', 'frontend', 'tests',
133 'test_asyncfrontendbase')),
125 'test_asyncfrontendbase')),
134 EXCLUDE.append(pjoin('IPython', 'testing', 'parametric'))
126 EXCLUDE.append(pjoin('IPython', 'testing', 'parametric'))
135 EXCLUDE.append(pjoin('IPython', 'testing', 'util'))
127 EXCLUDE.append(pjoin('IPython', 'testing', 'util'))
136 EXCLUDE.append(pjoin('IPython', 'testing', 'tests',
128 EXCLUDE.append(pjoin('IPython', 'testing', 'tests',
137 'test_decorators_trial'))
129 'test_decorators_trial'))
138
130
139 # Skip shell always because of a bug in FakeModule.
140 EXCLUDE.append(pjoin('IPython', 'core', 'shell'))
141
142 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
131 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
143 if sys.platform == 'win32':
132 if sys.platform == 'win32':
144 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
133 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
145
134
146 return EXCLUDE
135 return EXCLUDE
147
136
148
137
149 #-----------------------------------------------------------------------------
138 #-----------------------------------------------------------------------------
150 # Functions and classes
139 # Functions and classes
151 #-----------------------------------------------------------------------------
140 #-----------------------------------------------------------------------------
152
141
153 def run_iptest():
142 def run_iptest():
154 """Run the IPython test suite using nose.
143 """Run the IPython test suite using nose.
155
144
156 This function is called when this script is **not** called with the form
145 This function is called when this script is **not** called with the form
157 `iptest all`. It simply calls nose with appropriate command line flags
146 `iptest all`. It simply calls nose with appropriate command line flags
158 and accepts all of the standard nose arguments.
147 and accepts all of the standard nose arguments.
159 """
148 """
160
149
161 warnings.filterwarnings('ignore',
150 warnings.filterwarnings('ignore',
162 'This will be removed soon. Use IPython.testing.util instead')
151 'This will be removed soon. Use IPython.testing.util instead')
163
152
164 argv = sys.argv + [
153 argv = sys.argv + [
165 # Loading ipdoctest causes problems with Twisted.
154 # Loading ipdoctest causes problems with Twisted.
166 # I am removing this as a temporary fix to get the
155 # I am removing this as a temporary fix to get the
167 # test suite back into working shape. Our nose
156 # test suite back into working shape. Our nose
168 # plugin needs to be gone through with a fine
157 # plugin needs to be gone through with a fine
169 # toothed comb to find what is causing the problem.
158 # toothed comb to find what is causing the problem.
170 '--with-ipdoctest',
159 # '--with-ipdoctest',
171 '--ipdoctest-tests','--ipdoctest-extension=txt',
160 # '--ipdoctest-tests','--ipdoctest-extension=txt',
172 '--detailed-errors',
161 # '--detailed-errors',
173
162
174 # We add --exe because of setuptools' imbecility (it
163 # We add --exe because of setuptools' imbecility (it
175 # blindly does chmod +x on ALL files). Nose does the
164 # blindly does chmod +x on ALL files). Nose does the
176 # right thing and it tries to avoid executables,
165 # right thing and it tries to avoid executables,
177 # setuptools unfortunately forces our hand here. This
166 # setuptools unfortunately forces our hand here. This
178 # has been discussed on the distutils list and the
167 # has been discussed on the distutils list and the
179 # setuptools devs refuse to fix this problem!
168 # setuptools devs refuse to fix this problem!
180 '--exe',
169 '--exe',
181 ]
170 ]
182
171
183 # Detect if any tests were required by explicitly calling an IPython
172 # Detect if any tests were required by explicitly calling an IPython
184 # submodule or giving a specific path
173 # submodule or giving a specific path
185 has_tests = False
174 has_tests = False
186 for arg in sys.argv:
175 for arg in sys.argv:
187 if 'IPython' in arg or arg.endswith('.py') or \
176 if 'IPython' in arg or arg.endswith('.py') or \
188 (':' in arg and '.py' in arg):
177 (':' in arg and '.py' in arg):
189 has_tests = True
178 has_tests = True
190 break
179 break
191
180
192 # If nothing was specifically requested, test full IPython
181 # If nothing was specifically requested, test full IPython
193 if not has_tests:
182 if not has_tests:
194 argv.append('IPython')
183 argv.append('IPython')
195
184
196 # Construct list of plugins, omitting the existing doctest plugin, which
185 # Construct list of plugins, omitting the existing doctest plugin, which
197 # ours replaces (and extends).
186 # ours replaces (and extends).
198 EXCLUDE = make_exclude()
187 EXCLUDE = make_exclude()
199 plugins = [IPythonDoctest(EXCLUDE)]
188 plugins = []
189 # plugins = [IPythonDoctest(EXCLUDE)]
200 for p in nose.plugins.builtin.plugins:
190 for p in nose.plugins.builtin.plugins:
201 plug = p()
191 plug = p()
202 if plug.name == 'doctest':
192 if plug.name == 'doctest':
203 continue
193 continue
204 plugins.append(plug)
194 plugins.append(plug)
205
195
206 TestProgram(argv=argv,plugins=plugins)
196 TestProgram(argv=argv,plugins=plugins)
207
197
208
198
209 class IPTester(object):
199 class IPTester(object):
210 """Call that calls iptest or trial in a subprocess.
200 """Call that calls iptest or trial in a subprocess.
211 """
201 """
212 def __init__(self,runner='iptest',params=None):
202 def __init__(self,runner='iptest',params=None):
213 """ """
203 """ """
214 if runner == 'iptest':
204 if runner == 'iptest':
215 self.runner = ['iptest','-v']
205 self.runner = ['iptest','-v']
216 else:
206 else:
217 self.runner = [find_cmd('trial')]
207 self.runner = [find_cmd('trial')]
218 if params is None:
208 if params is None:
219 params = []
209 params = []
220 if isinstance(params,str):
210 if isinstance(params,str):
221 params = [params]
211 params = [params]
222 self.params = params
212 self.params = params
223
213
224 # Assemble call
214 # Assemble call
225 self.call_args = self.runner+self.params
215 self.call_args = self.runner+self.params
226
216
227 if sys.platform == 'win32':
217 if sys.platform == 'win32':
228 def run(self):
218 def run(self):
229 """Run the stored commands"""
219 """Run the stored commands"""
230 # On Windows, cd to temporary directory to run tests. Otherwise,
220 # On Windows, cd to temporary directory to run tests. Otherwise,
231 # Twisted's trial may not be able to execute 'trial IPython', since
221 # Twisted's trial may not be able to execute 'trial IPython', since
232 # it will confuse the IPython module name with the ipython
222 # it will confuse the IPython module name with the ipython
233 # execution scripts, because the windows file system isn't case
223 # execution scripts, because the windows file system isn't case
234 # sensitive.
224 # sensitive.
235 # We also use os.system instead of subprocess.call, because I was
225 # We also use os.system instead of subprocess.call, because I was
236 # having problems with subprocess and I just don't know enough
226 # having problems with subprocess and I just don't know enough
237 # about win32 to debug this reliably. Os.system may be the 'old
227 # about win32 to debug this reliably. Os.system may be the 'old
238 # fashioned' way to do it, but it works just fine. If someone
228 # fashioned' way to do it, but it works just fine. If someone
239 # later can clean this up that's fine, as long as the tests run
229 # later can clean this up that's fine, as long as the tests run
240 # reliably in win32.
230 # reliably in win32.
241 curdir = os.getcwd()
231 curdir = os.getcwd()
242 os.chdir(tempfile.gettempdir())
232 os.chdir(tempfile.gettempdir())
243 stat = os.system(' '.join(self.call_args))
233 stat = os.system(' '.join(self.call_args))
244 os.chdir(curdir)
234 os.chdir(curdir)
245 return stat
235 return stat
246 else:
236 else:
247 def run(self):
237 def run(self):
248 """Run the stored commands"""
238 """Run the stored commands"""
249 return subprocess.call(self.call_args)
239 return subprocess.call(self.call_args)
250
240
251
241
252 def make_runners():
242 def make_runners():
253 """Define the top-level packages that need to be tested.
243 """Define the top-level packages that need to be tested.
254 """
244 """
255
245
256 nose_packages = ['config', 'core', 'extensions',
246 nose_packages = ['config', 'core', 'extensions',
257 'frontend', 'lib', 'quarantine',
247 'frontend', 'lib',
258 'scripts', 'testing', 'utils']
248 'scripts', 'testing', 'utils']
259 trial_packages = ['kernel']
249 trial_packages = ['kernel']
260
250
261 if have_wx:
251 if have_wx:
262 nose_packages.append('gui')
252 nose_packages.append('gui')
263
253
264 nose_packages = ['IPython.%s' % m for m in nose_packages ]
254 nose_packages = ['IPython.%s' % m for m in nose_packages ]
265 trial_packages = ['IPython.%s' % m for m in trial_packages ]
255 trial_packages = ['IPython.%s' % m for m in trial_packages ]
266
256
267 # Make runners
257 # Make runners
268 runners = dict()
258 runners = dict()
269
259
270 nose_runners = dict(zip(nose_packages, [IPTester(params=v) for v in nose_packages]))
260 nose_runners = dict(zip(nose_packages, [IPTester(params=v) for v in nose_packages]))
271 if have_zi and have_twisted and have_foolscap:
261 if have_zi and have_twisted and have_foolscap:
272 trial_runners = dict(zip(trial_packages, [IPTester('trial',params=v) for v in trial_packages]))
262 trial_runners = dict(zip(trial_packages, [IPTester('trial',params=v) for v in trial_packages]))
273 runners.update(nose_runners)
263 runners.update(nose_runners)
274 runners.update(trial_runners)
264 runners.update(trial_runners)
275
265
276 return runners
266 return runners
277
267
278
268
279 def run_iptestall():
269 def run_iptestall():
280 """Run the entire IPython test suite by calling nose and trial.
270 """Run the entire IPython test suite by calling nose and trial.
281
271
282 This function constructs :class:`IPTester` instances for all IPython
272 This function constructs :class:`IPTester` instances for all IPython
283 modules and package and then runs each of them. This causes the modules
273 modules and package and then runs each of them. This causes the modules
284 and packages of IPython to be tested each in their own subprocess using
274 and packages of IPython to be tested each in their own subprocess using
285 nose or twisted.trial appropriately.
275 nose or twisted.trial appropriately.
286 """
276 """
287
277
288 runners = make_runners()
278 runners = make_runners()
289
279
290 # Run all test runners, tracking execution time
280 # Run all test runners, tracking execution time
291 failed = {}
281 failed = {}
292 t_start = time.time()
282 t_start = time.time()
293 for name,runner in runners.iteritems():
283 for name,runner in runners.iteritems():
294 print '*'*77
284 print '*'*77
295 print 'IPython test group:',name
285 print 'IPython test group:',name
296 res = runner.run()
286 res = runner.run()
297 if res:
287 if res:
298 failed[name] = res
288 failed[name] = res
299 t_end = time.time()
289 t_end = time.time()
300 t_tests = t_end - t_start
290 t_tests = t_end - t_start
301 nrunners = len(runners)
291 nrunners = len(runners)
302 nfail = len(failed)
292 nfail = len(failed)
303 # summarize results
293 # summarize results
304 print
294 print
305 print '*'*77
295 print '*'*77
306 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
296 print 'Ran %s test groups in %.3fs' % (nrunners, t_tests)
307 print
297 print
308 if not failed:
298 if not failed:
309 print 'OK'
299 print 'OK'
310 else:
300 else:
311 # If anything went wrong, point out what command to rerun manually to
301 # If anything went wrong, point out what command to rerun manually to
312 # see the actual errors and individual summary
302 # see the actual errors and individual summary
313 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
303 print 'ERROR - %s out of %s test groups failed.' % (nfail, nrunners)
314 for name in failed:
304 for name in failed:
315 failed_runner = runners[name]
305 failed_runner = runners[name]
316 print '-'*40
306 print '-'*40
317 print 'Runner failed:',name
307 print 'Runner failed:',name
318 print 'You may wish to rerun this one individually, with:'
308 print 'You may wish to rerun this one individually, with:'
319 print ' '.join(failed_runner.call_args)
309 print ' '.join(failed_runner.call_args)
320 print
310 print
321
311
322
312
323 def main():
313 def main():
324 if len(sys.argv) == 1:
314 if len(sys.argv) == 1:
325 run_iptestall()
315 run_iptestall()
326 else:
316 else:
327 if sys.argv[1] == 'all':
317 if sys.argv[1] == 'all':
328 run_iptestall()
318 run_iptestall()
329 else:
319 else:
330 run_iptest()
320 run_iptest()
331
321
332
322
333 if __name__ == '__main__':
323 if __name__ == '__main__':
334 main()
324 main()
General Comments 0
You need to be logged in to leave comments. Login now