##// END OF EJS Templates
irunner.py => lib/irunner.py and imports updated.
Brian Granger -
Show More
1 NO CONTENT: file renamed from IPython/irunner.py to IPython/lib/irunner.py
NO CONTENT: file renamed from IPython/irunner.py to IPython/lib/irunner.py
@@ -1,11 +1,14 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_backgroundjobs():
4 def test_import_backgroundjobs():
5 from IPython.lib import backgroundjobs
5 from IPython.lib import backgroundjobs
6
6
7 def test_import_deepreload():
7 def test_import_deepreload():
8 from IPython.lib import deepreload
8 from IPython.lib import deepreload
9
9
10 def test_import_demo():
10 def test_import_demo():
11 from IPython.lib import demo
11 from IPython.lib import demo
12
13 def test_import_irunner():
14 from IPython.lib import demo
@@ -1,800 +1,800 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """IPython-enhanced doctest module with unittest integration.
2 """IPython-enhanced doctest module with unittest integration.
3
3
4 This module is heavily based on the standard library's doctest module, but
4 This module is heavily based on the standard library's doctest module, but
5 enhances it with IPython support. This enables docstrings to contain
5 enhances it with IPython support. This enables docstrings to contain
6 unmodified IPython input and output pasted from real IPython sessions.
6 unmodified IPython input and output pasted from real IPython sessions.
7
7
8 It should be possible to use this module as a drop-in replacement for doctest
8 It should be possible to use this module as a drop-in replacement for doctest
9 whenever you wish to use IPython input.
9 whenever you wish to use IPython input.
10
10
11 Since the module absorbs all normal doctest functionality, you can use a mix of
11 Since the module absorbs all normal doctest functionality, you can use a mix of
12 both plain Python and IPython examples in any given module, though not in the
12 both plain Python and IPython examples in any given module, though not in the
13 same docstring.
13 same docstring.
14
14
15 See a simple example at the bottom of this code which serves as self-test and
15 See a simple example at the bottom of this code which serves as self-test and
16 demonstration code. Simply run this file (use -v for details) to run the
16 demonstration code. Simply run this file (use -v for details) to run the
17 tests.
17 tests.
18
18
19 This module also contains routines to ease the integration of doctests with
19 This module also contains routines to ease the integration of doctests with
20 regular unittest-based testing. In particular, see the DocTestLoader class and
20 regular unittest-based testing. In particular, see the DocTestLoader class and
21 the makeTestSuite utility function.
21 the makeTestSuite utility function.
22
22
23
23
24 Limitations:
24 Limitations:
25
25
26 - When generating examples for use as doctests, make sure that you have
26 - When generating examples for use as doctests, make sure that you have
27 pretty-printing OFF. This can be done either by starting ipython with the
27 pretty-printing OFF. This can be done either by starting ipython with the
28 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
28 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
29 interactively disabling it with %Pprint. This is required so that IPython
29 interactively disabling it with %Pprint. This is required so that IPython
30 output matches that of normal Python, which is used by doctest for internal
30 output matches that of normal Python, which is used by doctest for internal
31 execution.
31 execution.
32
32
33 - Do not rely on specific prompt numbers for results (such as using
33 - Do not rely on specific prompt numbers for results (such as using
34 '_34==True', for example). For IPython tests run via an external process
34 '_34==True', for example). For IPython tests run via an external process
35 the prompt numbers may be different, and IPython tests run as normal python
35 the prompt numbers may be different, and IPython tests run as normal python
36 code won't even have these special _NN variables set at all.
36 code won't even have these special _NN variables set at all.
37
37
38 - IPython functions that produce output as a side-effect of calling a system
38 - IPython functions that produce output as a side-effect of calling a system
39 process (e.g. 'ls') can be doc-tested, but they must be handled in an
39 process (e.g. 'ls') can be doc-tested, but they must be handled in an
40 external IPython process. Such doctests must be tagged with:
40 external IPython process. Such doctests must be tagged with:
41
41
42 # ipdoctest: EXTERNAL
42 # ipdoctest: EXTERNAL
43
43
44 so that the testing machinery handles them differently. Since these are run
44 so that the testing machinery handles them differently. Since these are run
45 via pexpect in an external process, they can't deal with exceptions or other
45 via pexpect in an external process, they can't deal with exceptions or other
46 fancy featurs of regular doctests. You must limit such tests to simple
46 fancy featurs of regular doctests. You must limit such tests to simple
47 matching of the output. For this reason, I recommend you limit these kinds
47 matching of the output. For this reason, I recommend you limit these kinds
48 of doctests to features that truly require a separate process, and use the
48 of doctests to features that truly require a separate process, and use the
49 normal IPython ones (which have all the features of normal doctests) for
49 normal IPython ones (which have all the features of normal doctests) for
50 everything else. See the examples at the bottom of this file for a
50 everything else. See the examples at the bottom of this file for a
51 comparison of what can be done with both types.
51 comparison of what can be done with both types.
52 """
52 """
53
53
54 # Standard library imports
54 # Standard library imports
55 import __builtin__
55 import __builtin__
56 import doctest
56 import doctest
57 import inspect
57 import inspect
58 import os
58 import os
59 import re
59 import re
60 import sys
60 import sys
61 import unittest
61 import unittest
62
62
63 from doctest import *
63 from doctest import *
64
64
65 from IPython.tools import utils
65 from IPython.tools import utils
66 from IPython.core import ipapi
66 from IPython.core import ipapi
67
67
68 ###########################################################################
68 ###########################################################################
69 #
69 #
70 # We must start our own ipython object and heavily muck with it so that all the
70 # We must start our own ipython object and heavily muck with it so that all the
71 # modifications IPython makes to system behavior don't send the doctest
71 # modifications IPython makes to system behavior don't send the doctest
72 # machinery into a fit. This code should be considered a gross hack, but it
72 # machinery into a fit. This code should be considered a gross hack, but it
73 # gets the job done.
73 # gets the job done.
74
74
75 import IPython
75 import IPython
76
76
77 # Hack to restore __main__, which ipython modifies upon startup
77 # Hack to restore __main__, which ipython modifies upon startup
78 _main = sys.modules.get('__main__')
78 _main = sys.modules.get('__main__')
79 ipython = IPython.Shell.IPShell(['--classic','--noterm_title']).IP
79 ipython = IPython.Shell.IPShell(['--classic','--noterm_title']).IP
80 sys.modules['__main__'] = _main
80 sys.modules['__main__'] = _main
81
81
82 # Deactivate the various python system hooks added by ipython for
82 # Deactivate the various python system hooks added by ipython for
83 # interactive convenience so we don't confuse the doctest system
83 # interactive convenience so we don't confuse the doctest system
84 sys.displayhook = sys.__displayhook__
84 sys.displayhook = sys.__displayhook__
85 sys.excepthook = sys.__excepthook__
85 sys.excepthook = sys.__excepthook__
86
86
87 # So that ipython magics and aliases can be doctested
87 # So that ipython magics and aliases can be doctested
88 __builtin__._ip = ipapi.get()
88 __builtin__._ip = ipapi.get()
89
89
90 # for debugging only!!!
90 # for debugging only!!!
91 #from IPython.Shell import IPShellEmbed;ipshell=IPShellEmbed(['--noterm_title']) # dbg
91 #from IPython.Shell import IPShellEmbed;ipshell=IPShellEmbed(['--noterm_title']) # dbg
92
92
93
93
94 # runner
94 # runner
95 from IPython.irunner import IPythonRunner
95 from IPython.lib.irunner import IPythonRunner
96 iprunner = IPythonRunner(echo=False)
96 iprunner = IPythonRunner(echo=False)
97
97
98 ###########################################################################
98 ###########################################################################
99
99
100 # A simple subclassing of the original with a different class name, so we can
100 # A simple subclassing of the original with a different class name, so we can
101 # distinguish and treat differently IPython examples from pure python ones.
101 # distinguish and treat differently IPython examples from pure python ones.
102 class IPExample(doctest.Example): pass
102 class IPExample(doctest.Example): pass
103
103
104 class IPExternalExample(doctest.Example):
104 class IPExternalExample(doctest.Example):
105 """Doctest examples to be run in an external process."""
105 """Doctest examples to be run in an external process."""
106
106
107 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
107 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
108 options=None):
108 options=None):
109 # Parent constructor
109 # Parent constructor
110 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
110 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
111
111
112 # An EXTRA newline is needed to prevent pexpect hangs
112 # An EXTRA newline is needed to prevent pexpect hangs
113 self.source += '\n'
113 self.source += '\n'
114
114
115 class IPDocTestParser(doctest.DocTestParser):
115 class IPDocTestParser(doctest.DocTestParser):
116 """
116 """
117 A class used to parse strings containing doctest examples.
117 A class used to parse strings containing doctest examples.
118
118
119 Note: This is a version modified to properly recognize IPython input and
119 Note: This is a version modified to properly recognize IPython input and
120 convert any IPython examples into valid Python ones.
120 convert any IPython examples into valid Python ones.
121 """
121 """
122 # This regular expression is used to find doctest examples in a
122 # This regular expression is used to find doctest examples in a
123 # string. It defines three groups: `source` is the source code
123 # string. It defines three groups: `source` is the source code
124 # (including leading indentation and prompts); `indent` is the
124 # (including leading indentation and prompts); `indent` is the
125 # indentation of the first (PS1) line of the source code; and
125 # indentation of the first (PS1) line of the source code; and
126 # `want` is the expected output (including leading indentation).
126 # `want` is the expected output (including leading indentation).
127
127
128 # Classic Python prompts or default IPython ones
128 # Classic Python prompts or default IPython ones
129 _PS1_PY = r'>>>'
129 _PS1_PY = r'>>>'
130 _PS2_PY = r'\.\.\.'
130 _PS2_PY = r'\.\.\.'
131
131
132 _PS1_IP = r'In\ \[\d+\]:'
132 _PS1_IP = r'In\ \[\d+\]:'
133 _PS2_IP = r'\ \ \ \.\.\.+:'
133 _PS2_IP = r'\ \ \ \.\.\.+:'
134
134
135 _RE_TPL = r'''
135 _RE_TPL = r'''
136 # Source consists of a PS1 line followed by zero or more PS2 lines.
136 # Source consists of a PS1 line followed by zero or more PS2 lines.
137 (?P<source>
137 (?P<source>
138 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
138 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
139 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
139 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
140 \n? # a newline
140 \n? # a newline
141 # Want consists of any non-blank lines that do not start with PS1.
141 # Want consists of any non-blank lines that do not start with PS1.
142 (?P<want> (?:(?![ ]*$) # Not a blank line
142 (?P<want> (?:(?![ ]*$) # Not a blank line
143 (?![ ]*%s) # Not a line starting with PS1
143 (?![ ]*%s) # Not a line starting with PS1
144 (?![ ]*%s) # Not a line starting with PS2
144 (?![ ]*%s) # Not a line starting with PS2
145 .*$\n? # But any other line
145 .*$\n? # But any other line
146 )*)
146 )*)
147 '''
147 '''
148
148
149 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
149 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
150 re.MULTILINE | re.VERBOSE)
150 re.MULTILINE | re.VERBOSE)
151
151
152 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
152 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
153 re.MULTILINE | re.VERBOSE)
153 re.MULTILINE | re.VERBOSE)
154
154
155 def ip2py(self,source):
155 def ip2py(self,source):
156 """Convert input IPython source into valid Python."""
156 """Convert input IPython source into valid Python."""
157 out = []
157 out = []
158 newline = out.append
158 newline = out.append
159 for line in source.splitlines():
159 for line in source.splitlines():
160 newline(ipython.prefilter(line,True))
160 newline(ipython.prefilter(line,True))
161 newline('') # ensure a closing newline, needed by doctest
161 newline('') # ensure a closing newline, needed by doctest
162 return '\n'.join(out)
162 return '\n'.join(out)
163
163
164 def parse(self, string, name='<string>'):
164 def parse(self, string, name='<string>'):
165 """
165 """
166 Divide the given string into examples and intervening text,
166 Divide the given string into examples and intervening text,
167 and return them as a list of alternating Examples and strings.
167 and return them as a list of alternating Examples and strings.
168 Line numbers for the Examples are 0-based. The optional
168 Line numbers for the Examples are 0-based. The optional
169 argument `name` is a name identifying this string, and is only
169 argument `name` is a name identifying this string, and is only
170 used for error messages.
170 used for error messages.
171 """
171 """
172 string = string.expandtabs()
172 string = string.expandtabs()
173 # If all lines begin with the same indentation, then strip it.
173 # If all lines begin with the same indentation, then strip it.
174 min_indent = self._min_indent(string)
174 min_indent = self._min_indent(string)
175 if min_indent > 0:
175 if min_indent > 0:
176 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
176 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
177
177
178 output = []
178 output = []
179 charno, lineno = 0, 0
179 charno, lineno = 0, 0
180
180
181 # Whether to convert the input from ipython to python syntax
181 # Whether to convert the input from ipython to python syntax
182 ip2py = False
182 ip2py = False
183 # Find all doctest examples in the string. First, try them as Python
183 # Find all doctest examples in the string. First, try them as Python
184 # examples, then as IPython ones
184 # examples, then as IPython ones
185 terms = list(self._EXAMPLE_RE_PY.finditer(string))
185 terms = list(self._EXAMPLE_RE_PY.finditer(string))
186 if terms:
186 if terms:
187 # Normal Python example
187 # Normal Python example
188 Example = doctest.Example
188 Example = doctest.Example
189 else:
189 else:
190 # It's an ipython example. Note that IPExamples are run
190 # It's an ipython example. Note that IPExamples are run
191 # in-process, so their syntax must be turned into valid python.
191 # in-process, so their syntax must be turned into valid python.
192 # IPExternalExamples are run out-of-process (via pexpect) so they
192 # IPExternalExamples are run out-of-process (via pexpect) so they
193 # don't need any filtering (a real ipython will be executing them).
193 # don't need any filtering (a real ipython will be executing them).
194 terms = list(self._EXAMPLE_RE_IP.finditer(string))
194 terms = list(self._EXAMPLE_RE_IP.finditer(string))
195 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
195 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
196 #print '-'*70 # dbg
196 #print '-'*70 # dbg
197 #print 'IPExternalExample, Source:\n',string # dbg
197 #print 'IPExternalExample, Source:\n',string # dbg
198 #print '-'*70 # dbg
198 #print '-'*70 # dbg
199 Example = IPExternalExample
199 Example = IPExternalExample
200 else:
200 else:
201 #print '-'*70 # dbg
201 #print '-'*70 # dbg
202 #print 'IPExample, Source:\n',string # dbg
202 #print 'IPExample, Source:\n',string # dbg
203 #print '-'*70 # dbg
203 #print '-'*70 # dbg
204 Example = IPExample
204 Example = IPExample
205 ip2py = True
205 ip2py = True
206
206
207 for m in terms:
207 for m in terms:
208 # Add the pre-example text to `output`.
208 # Add the pre-example text to `output`.
209 output.append(string[charno:m.start()])
209 output.append(string[charno:m.start()])
210 # Update lineno (lines before this example)
210 # Update lineno (lines before this example)
211 lineno += string.count('\n', charno, m.start())
211 lineno += string.count('\n', charno, m.start())
212 # Extract info from the regexp match.
212 # Extract info from the regexp match.
213 (source, options, want, exc_msg) = \
213 (source, options, want, exc_msg) = \
214 self._parse_example(m, name, lineno,ip2py)
214 self._parse_example(m, name, lineno,ip2py)
215 if Example is IPExternalExample:
215 if Example is IPExternalExample:
216 options[doctest.NORMALIZE_WHITESPACE] = True
216 options[doctest.NORMALIZE_WHITESPACE] = True
217 # Create an Example, and add it to the list.
217 # Create an Example, and add it to the list.
218 if not self._IS_BLANK_OR_COMMENT(source):
218 if not self._IS_BLANK_OR_COMMENT(source):
219 output.append(Example(source, want, exc_msg,
219 output.append(Example(source, want, exc_msg,
220 lineno=lineno,
220 lineno=lineno,
221 indent=min_indent+len(m.group('indent')),
221 indent=min_indent+len(m.group('indent')),
222 options=options))
222 options=options))
223 # Update lineno (lines inside this example)
223 # Update lineno (lines inside this example)
224 lineno += string.count('\n', m.start(), m.end())
224 lineno += string.count('\n', m.start(), m.end())
225 # Update charno.
225 # Update charno.
226 charno = m.end()
226 charno = m.end()
227 # Add any remaining post-example text to `output`.
227 # Add any remaining post-example text to `output`.
228 output.append(string[charno:])
228 output.append(string[charno:])
229
229
230 return output
230 return output
231
231
232 def _parse_example(self, m, name, lineno,ip2py=False):
232 def _parse_example(self, m, name, lineno,ip2py=False):
233 """
233 """
234 Given a regular expression match from `_EXAMPLE_RE` (`m`),
234 Given a regular expression match from `_EXAMPLE_RE` (`m`),
235 return a pair `(source, want)`, where `source` is the matched
235 return a pair `(source, want)`, where `source` is the matched
236 example's source code (with prompts and indentation stripped);
236 example's source code (with prompts and indentation stripped);
237 and `want` is the example's expected output (with indentation
237 and `want` is the example's expected output (with indentation
238 stripped).
238 stripped).
239
239
240 `name` is the string's name, and `lineno` is the line number
240 `name` is the string's name, and `lineno` is the line number
241 where the example starts; both are used for error messages.
241 where the example starts; both are used for error messages.
242
242
243 Optional:
243 Optional:
244 `ip2py`: if true, filter the input via IPython to convert the syntax
244 `ip2py`: if true, filter the input via IPython to convert the syntax
245 into valid python.
245 into valid python.
246 """
246 """
247
247
248 # Get the example's indentation level.
248 # Get the example's indentation level.
249 indent = len(m.group('indent'))
249 indent = len(m.group('indent'))
250
250
251 # Divide source into lines; check that they're properly
251 # Divide source into lines; check that they're properly
252 # indented; and then strip their indentation & prompts.
252 # indented; and then strip their indentation & prompts.
253 source_lines = m.group('source').split('\n')
253 source_lines = m.group('source').split('\n')
254
254
255 # We're using variable-length input prompts
255 # We're using variable-length input prompts
256 ps1 = m.group('ps1')
256 ps1 = m.group('ps1')
257 ps2 = m.group('ps2')
257 ps2 = m.group('ps2')
258 ps1_len = len(ps1)
258 ps1_len = len(ps1)
259
259
260 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
260 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
261 if ps2:
261 if ps2:
262 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
262 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
263
263
264 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
264 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
265
265
266 if ip2py:
266 if ip2py:
267 # Convert source input from IPython into valid Python syntax
267 # Convert source input from IPython into valid Python syntax
268 source = self.ip2py(source)
268 source = self.ip2py(source)
269
269
270 # Divide want into lines; check that it's properly indented; and
270 # Divide want into lines; check that it's properly indented; and
271 # then strip the indentation. Spaces before the last newline should
271 # then strip the indentation. Spaces before the last newline should
272 # be preserved, so plain rstrip() isn't good enough.
272 # be preserved, so plain rstrip() isn't good enough.
273 want = m.group('want')
273 want = m.group('want')
274 want_lines = want.split('\n')
274 want_lines = want.split('\n')
275 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
275 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
276 del want_lines[-1] # forget final newline & spaces after it
276 del want_lines[-1] # forget final newline & spaces after it
277 self._check_prefix(want_lines, ' '*indent, name,
277 self._check_prefix(want_lines, ' '*indent, name,
278 lineno + len(source_lines))
278 lineno + len(source_lines))
279
279
280 # Remove ipython output prompt that might be present in the first line
280 # Remove ipython output prompt that might be present in the first line
281 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
281 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
282
282
283 want = '\n'.join([wl[indent:] for wl in want_lines])
283 want = '\n'.join([wl[indent:] for wl in want_lines])
284
284
285 # If `want` contains a traceback message, then extract it.
285 # If `want` contains a traceback message, then extract it.
286 m = self._EXCEPTION_RE.match(want)
286 m = self._EXCEPTION_RE.match(want)
287 if m:
287 if m:
288 exc_msg = m.group('msg')
288 exc_msg = m.group('msg')
289 else:
289 else:
290 exc_msg = None
290 exc_msg = None
291
291
292 # Extract options from the source.
292 # Extract options from the source.
293 options = self._find_options(source, name, lineno)
293 options = self._find_options(source, name, lineno)
294
294
295 return source, options, want, exc_msg
295 return source, options, want, exc_msg
296
296
297 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
297 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
298 """
298 """
299 Given the lines of a source string (including prompts and
299 Given the lines of a source string (including prompts and
300 leading indentation), check to make sure that every prompt is
300 leading indentation), check to make sure that every prompt is
301 followed by a space character. If any line is not followed by
301 followed by a space character. If any line is not followed by
302 a space character, then raise ValueError.
302 a space character, then raise ValueError.
303
303
304 Note: IPython-modified version which takes the input prompt length as a
304 Note: IPython-modified version which takes the input prompt length as a
305 parameter, so that prompts of variable length can be dealt with.
305 parameter, so that prompts of variable length can be dealt with.
306 """
306 """
307 space_idx = indent+ps1_len
307 space_idx = indent+ps1_len
308 min_len = space_idx+1
308 min_len = space_idx+1
309 for i, line in enumerate(lines):
309 for i, line in enumerate(lines):
310 if len(line) >= min_len and line[space_idx] != ' ':
310 if len(line) >= min_len and line[space_idx] != ' ':
311 raise ValueError('line %r of the docstring for %s '
311 raise ValueError('line %r of the docstring for %s '
312 'lacks blank after %s: %r' %
312 'lacks blank after %s: %r' %
313 (lineno+i+1, name,
313 (lineno+i+1, name,
314 line[indent:space_idx], line))
314 line[indent:space_idx], line))
315
315
316
316
317 SKIP = register_optionflag('SKIP')
317 SKIP = register_optionflag('SKIP')
318
318
319 class IPDocTestRunner(doctest.DocTestRunner):
319 class IPDocTestRunner(doctest.DocTestRunner):
320 """Modified DocTestRunner which can also run IPython tests.
320 """Modified DocTestRunner which can also run IPython tests.
321
321
322 This runner is capable of handling IPython doctests that require
322 This runner is capable of handling IPython doctests that require
323 out-of-process output capture (such as system calls via !cmd or aliases).
323 out-of-process output capture (such as system calls via !cmd or aliases).
324 Note however that because these tests are run in a separate process, many
324 Note however that because these tests are run in a separate process, many
325 of doctest's fancier capabilities (such as detailed exception analysis) are
325 of doctest's fancier capabilities (such as detailed exception analysis) are
326 not available. So try to limit such tests to simple cases of matching
326 not available. So try to limit such tests to simple cases of matching
327 actual output.
327 actual output.
328 """
328 """
329
329
330 #/////////////////////////////////////////////////////////////////
330 #/////////////////////////////////////////////////////////////////
331 # DocTest Running
331 # DocTest Running
332 #/////////////////////////////////////////////////////////////////
332 #/////////////////////////////////////////////////////////////////
333
333
334 def _run_iptest(self, test, out):
334 def _run_iptest(self, test, out):
335 """
335 """
336 Run the examples in `test`. Write the outcome of each example with one
336 Run the examples in `test`. Write the outcome of each example with one
337 of the `DocTestRunner.report_*` methods, using the writer function
337 of the `DocTestRunner.report_*` methods, using the writer function
338 `out`. Return a tuple `(f, t)`, where `t` is the number of examples
338 `out`. Return a tuple `(f, t)`, where `t` is the number of examples
339 tried, and `f` is the number of examples that failed. The examples are
339 tried, and `f` is the number of examples that failed. The examples are
340 run in the namespace `test.globs`.
340 run in the namespace `test.globs`.
341
341
342 IPython note: this is a modified version of the original __run()
342 IPython note: this is a modified version of the original __run()
343 private method to handle out-of-process examples.
343 private method to handle out-of-process examples.
344 """
344 """
345
345
346 if out is None:
346 if out is None:
347 out = sys.stdout.write
347 out = sys.stdout.write
348
348
349 # Keep track of the number of failures and tries.
349 # Keep track of the number of failures and tries.
350 failures = tries = 0
350 failures = tries = 0
351
351
352 # Save the option flags (since option directives can be used
352 # Save the option flags (since option directives can be used
353 # to modify them).
353 # to modify them).
354 original_optionflags = self.optionflags
354 original_optionflags = self.optionflags
355
355
356 SUCCESS, FAILURE, BOOM = range(3) # `outcome` state
356 SUCCESS, FAILURE, BOOM = range(3) # `outcome` state
357
357
358 check = self._checker.check_output
358 check = self._checker.check_output
359
359
360 # Process each example.
360 # Process each example.
361 for examplenum, example in enumerate(test.examples):
361 for examplenum, example in enumerate(test.examples):
362
362
363 # If REPORT_ONLY_FIRST_FAILURE is set, then supress
363 # If REPORT_ONLY_FIRST_FAILURE is set, then supress
364 # reporting after the first failure.
364 # reporting after the first failure.
365 quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and
365 quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and
366 failures > 0)
366 failures > 0)
367
367
368 # Merge in the example's options.
368 # Merge in the example's options.
369 self.optionflags = original_optionflags
369 self.optionflags = original_optionflags
370 if example.options:
370 if example.options:
371 for (optionflag, val) in example.options.items():
371 for (optionflag, val) in example.options.items():
372 if val:
372 if val:
373 self.optionflags |= optionflag
373 self.optionflags |= optionflag
374 else:
374 else:
375 self.optionflags &= ~optionflag
375 self.optionflags &= ~optionflag
376
376
377 # If 'SKIP' is set, then skip this example.
377 # If 'SKIP' is set, then skip this example.
378 if self.optionflags & SKIP:
378 if self.optionflags & SKIP:
379 continue
379 continue
380
380
381 # Record that we started this example.
381 # Record that we started this example.
382 tries += 1
382 tries += 1
383 if not quiet:
383 if not quiet:
384 self.report_start(out, test, example)
384 self.report_start(out, test, example)
385
385
386 # Run the example in the given context (globs), and record
386 # Run the example in the given context (globs), and record
387 # any exception that gets raised. (But don't intercept
387 # any exception that gets raised. (But don't intercept
388 # keyboard interrupts.)
388 # keyboard interrupts.)
389 try:
389 try:
390 # Don't blink! This is where the user's code gets run.
390 # Don't blink! This is where the user's code gets run.
391 got = ''
391 got = ''
392 # The code is run in an external process
392 # The code is run in an external process
393 got = iprunner.run_source(example.source,get_output=True)
393 got = iprunner.run_source(example.source,get_output=True)
394 except KeyboardInterrupt:
394 except KeyboardInterrupt:
395 raise
395 raise
396 except:
396 except:
397 self.debugger.set_continue() # ==== Example Finished ====
397 self.debugger.set_continue() # ==== Example Finished ====
398
398
399 outcome = FAILURE # guilty until proved innocent or insane
399 outcome = FAILURE # guilty until proved innocent or insane
400
400
401 if check(example.want, got, self.optionflags):
401 if check(example.want, got, self.optionflags):
402 outcome = SUCCESS
402 outcome = SUCCESS
403
403
404 # Report the outcome.
404 # Report the outcome.
405 if outcome is SUCCESS:
405 if outcome is SUCCESS:
406 if not quiet:
406 if not quiet:
407 self.report_success(out, test, example, got)
407 self.report_success(out, test, example, got)
408 elif outcome is FAILURE:
408 elif outcome is FAILURE:
409 if not quiet:
409 if not quiet:
410 self.report_failure(out, test, example, got)
410 self.report_failure(out, test, example, got)
411 failures += 1
411 failures += 1
412 elif outcome is BOOM:
412 elif outcome is BOOM:
413 if not quiet:
413 if not quiet:
414 self.report_unexpected_exception(out, test, example,
414 self.report_unexpected_exception(out, test, example,
415 exc_info)
415 exc_info)
416 failures += 1
416 failures += 1
417 else:
417 else:
418 assert False, ("unknown outcome", outcome)
418 assert False, ("unknown outcome", outcome)
419
419
420 # Restore the option flags (in case they were modified)
420 # Restore the option flags (in case they were modified)
421 self.optionflags = original_optionflags
421 self.optionflags = original_optionflags
422
422
423 # Record and return the number of failures and tries.
423 # Record and return the number of failures and tries.
424
424
425 # Hack to access a parent private method by working around Python's
425 # Hack to access a parent private method by working around Python's
426 # name mangling (which is fortunately simple).
426 # name mangling (which is fortunately simple).
427 doctest.DocTestRunner._DocTestRunner__record_outcome(self,test,
427 doctest.DocTestRunner._DocTestRunner__record_outcome(self,test,
428 failures, tries)
428 failures, tries)
429 return failures, tries
429 return failures, tries
430
430
431 def run(self, test, compileflags=None, out=None, clear_globs=True):
431 def run(self, test, compileflags=None, out=None, clear_globs=True):
432 """Run examples in `test`.
432 """Run examples in `test`.
433
433
434 This method will defer to the parent for normal Python examples, but it
434 This method will defer to the parent for normal Python examples, but it
435 will run IPython ones via pexpect.
435 will run IPython ones via pexpect.
436 """
436 """
437 if not test.examples:
437 if not test.examples:
438 return
438 return
439
439
440 if isinstance(test.examples[0],IPExternalExample):
440 if isinstance(test.examples[0],IPExternalExample):
441 self._run_iptest(test,out)
441 self._run_iptest(test,out)
442 else:
442 else:
443 DocTestRunner.run(self,test,compileflags,out,clear_globs)
443 DocTestRunner.run(self,test,compileflags,out,clear_globs)
444
444
445
445
446 class IPDebugRunner(IPDocTestRunner,doctest.DebugRunner):
446 class IPDebugRunner(IPDocTestRunner,doctest.DebugRunner):
447 """IPython-modified DebugRunner, see the original class for details."""
447 """IPython-modified DebugRunner, see the original class for details."""
448
448
449 def run(self, test, compileflags=None, out=None, clear_globs=True):
449 def run(self, test, compileflags=None, out=None, clear_globs=True):
450 r = IPDocTestRunner.run(self, test, compileflags, out, False)
450 r = IPDocTestRunner.run(self, test, compileflags, out, False)
451 if clear_globs:
451 if clear_globs:
452 test.globs.clear()
452 test.globs.clear()
453 return r
453 return r
454
454
455
455
456 class IPDocTestLoader(unittest.TestLoader):
456 class IPDocTestLoader(unittest.TestLoader):
457 """A test loader with IPython-enhanced doctest support.
457 """A test loader with IPython-enhanced doctest support.
458
458
459 Instances of this loader will automatically add doctests found in a module
459 Instances of this loader will automatically add doctests found in a module
460 to the test suite returned by the loadTestsFromModule method. In
460 to the test suite returned by the loadTestsFromModule method. In
461 addition, at initialization time a string of doctests can be given to the
461 addition, at initialization time a string of doctests can be given to the
462 loader, enabling it to add doctests to a module which didn't have them in
462 loader, enabling it to add doctests to a module which didn't have them in
463 its docstring, coming from an external source."""
463 its docstring, coming from an external source."""
464
464
465
465
466 def __init__(self,dt_files=None,dt_modules=None,test_finder=None):
466 def __init__(self,dt_files=None,dt_modules=None,test_finder=None):
467 """Initialize the test loader.
467 """Initialize the test loader.
468
468
469 :Keywords:
469 :Keywords:
470
470
471 dt_files : list (None)
471 dt_files : list (None)
472 List of names of files to be executed as doctests.
472 List of names of files to be executed as doctests.
473
473
474 dt_modules : list (None)
474 dt_modules : list (None)
475 List of module names to be scanned for doctests in their
475 List of module names to be scanned for doctests in their
476 docstrings.
476 docstrings.
477
477
478 test_finder : instance (None)
478 test_finder : instance (None)
479 Instance of a testfinder (see doctest for details).
479 Instance of a testfinder (see doctest for details).
480 """
480 """
481
481
482 if dt_files is None: dt_files = []
482 if dt_files is None: dt_files = []
483 if dt_modules is None: dt_modules = []
483 if dt_modules is None: dt_modules = []
484 self.dt_files = utils.list_strings(dt_files)
484 self.dt_files = utils.list_strings(dt_files)
485 self.dt_modules = utils.list_strings(dt_modules)
485 self.dt_modules = utils.list_strings(dt_modules)
486 if test_finder is None:
486 if test_finder is None:
487 test_finder = doctest.DocTestFinder(parser=IPDocTestParser())
487 test_finder = doctest.DocTestFinder(parser=IPDocTestParser())
488 self.test_finder = test_finder
488 self.test_finder = test_finder
489
489
490 def loadTestsFromModule(self, module):
490 def loadTestsFromModule(self, module):
491 """Return a suite of all tests cases contained in the given module.
491 """Return a suite of all tests cases contained in the given module.
492
492
493 If the loader was initialized with a doctests argument, then this
493 If the loader was initialized with a doctests argument, then this
494 string is assigned as the module's docstring."""
494 string is assigned as the module's docstring."""
495
495
496 # Start by loading any tests in the called module itself
496 # Start by loading any tests in the called module itself
497 suite = super(self.__class__,self).loadTestsFromModule(module)
497 suite = super(self.__class__,self).loadTestsFromModule(module)
498
498
499 # Now, load also tests referenced at construction time as companion
499 # Now, load also tests referenced at construction time as companion
500 # doctests that reside in standalone files
500 # doctests that reside in standalone files
501 for fname in self.dt_files:
501 for fname in self.dt_files:
502 #print 'mod:',module # dbg
502 #print 'mod:',module # dbg
503 #print 'fname:',fname # dbg
503 #print 'fname:',fname # dbg
504 #suite.addTest(doctest.DocFileSuite(fname))
504 #suite.addTest(doctest.DocFileSuite(fname))
505 suite.addTest(doctest.DocFileSuite(fname,module_relative=False))
505 suite.addTest(doctest.DocFileSuite(fname,module_relative=False))
506 # Add docstring tests from module, if given at construction time
506 # Add docstring tests from module, if given at construction time
507 for mod in self.dt_modules:
507 for mod in self.dt_modules:
508 suite.addTest(doctest.DocTestSuite(mod,
508 suite.addTest(doctest.DocTestSuite(mod,
509 test_finder=self.test_finder))
509 test_finder=self.test_finder))
510
510
511 #ipshell() # dbg
511 #ipshell() # dbg
512 return suite
512 return suite
513
513
514 def my_import(name):
514 def my_import(name):
515 """Module importer - taken from the python documentation.
515 """Module importer - taken from the python documentation.
516
516
517 This function allows importing names with dots in them."""
517 This function allows importing names with dots in them."""
518
518
519 mod = __import__(name)
519 mod = __import__(name)
520 components = name.split('.')
520 components = name.split('.')
521 for comp in components[1:]:
521 for comp in components[1:]:
522 mod = getattr(mod, comp)
522 mod = getattr(mod, comp)
523 return mod
523 return mod
524
524
525 def makeTestSuite(module_name,dt_files=None,dt_modules=None,idt=True):
525 def makeTestSuite(module_name,dt_files=None,dt_modules=None,idt=True):
526 """Make a TestSuite object for a given module, specified by name.
526 """Make a TestSuite object for a given module, specified by name.
527
527
528 This extracts all the doctests associated with a module using a
528 This extracts all the doctests associated with a module using a
529 DocTestLoader object.
529 DocTestLoader object.
530
530
531 :Parameters:
531 :Parameters:
532
532
533 - module_name: a string containing the name of a module with unittests.
533 - module_name: a string containing the name of a module with unittests.
534
534
535 :Keywords:
535 :Keywords:
536
536
537 dt_files : list of strings
537 dt_files : list of strings
538 List of names of plain text files to be treated as doctests.
538 List of names of plain text files to be treated as doctests.
539
539
540 dt_modules : list of strings
540 dt_modules : list of strings
541 List of names of modules to be scanned for doctests in docstrings.
541 List of names of modules to be scanned for doctests in docstrings.
542
542
543 idt : bool (True)
543 idt : bool (True)
544 If True, return integrated doctests. This means that each filename
544 If True, return integrated doctests. This means that each filename
545 listed in dt_files is turned into a *single* unittest, suitable for
545 listed in dt_files is turned into a *single* unittest, suitable for
546 running via unittest's runner or Twisted's Trial runner. If false, the
546 running via unittest's runner or Twisted's Trial runner. If false, the
547 dt_files parameter is returned unmodified, so that other test runners
547 dt_files parameter is returned unmodified, so that other test runners
548 (such as oilrun) can run the doctests with finer granularity.
548 (such as oilrun) can run the doctests with finer granularity.
549 """
549 """
550
550
551 mod = my_import(module_name)
551 mod = my_import(module_name)
552 if idt:
552 if idt:
553 suite = IPDocTestLoader(dt_files,dt_modules).loadTestsFromModule(mod)
553 suite = IPDocTestLoader(dt_files,dt_modules).loadTestsFromModule(mod)
554 else:
554 else:
555 suite = IPDocTestLoader(None,dt_modules).loadTestsFromModule(mod)
555 suite = IPDocTestLoader(None,dt_modules).loadTestsFromModule(mod)
556
556
557 if idt:
557 if idt:
558 return suite
558 return suite
559 else:
559 else:
560 return suite,dt_files
560 return suite,dt_files
561
561
562 # Copied from doctest in py2.5 and modified for our purposes (since they don't
562 # Copied from doctest in py2.5 and modified for our purposes (since they don't
563 # parametrize what we need)
563 # parametrize what we need)
564
564
565 # For backward compatibility, a global instance of a DocTestRunner
565 # For backward compatibility, a global instance of a DocTestRunner
566 # class, updated by testmod.
566 # class, updated by testmod.
567 master = None
567 master = None
568
568
569 def testmod(m=None, name=None, globs=None, verbose=None,
569 def testmod(m=None, name=None, globs=None, verbose=None,
570 report=True, optionflags=0, extraglobs=None,
570 report=True, optionflags=0, extraglobs=None,
571 raise_on_error=False, exclude_empty=False):
571 raise_on_error=False, exclude_empty=False):
572 """m=None, name=None, globs=None, verbose=None, report=True,
572 """m=None, name=None, globs=None, verbose=None, report=True,
573 optionflags=0, extraglobs=None, raise_on_error=False,
573 optionflags=0, extraglobs=None, raise_on_error=False,
574 exclude_empty=False
574 exclude_empty=False
575
575
576 Note: IPython-modified version which loads test finder and runners that
576 Note: IPython-modified version which loads test finder and runners that
577 recognize IPython syntax in doctests.
577 recognize IPython syntax in doctests.
578
578
579 Test examples in docstrings in functions and classes reachable
579 Test examples in docstrings in functions and classes reachable
580 from module m (or the current module if m is not supplied), starting
580 from module m (or the current module if m is not supplied), starting
581 with m.__doc__.
581 with m.__doc__.
582
582
583 Also test examples reachable from dict m.__test__ if it exists and is
583 Also test examples reachable from dict m.__test__ if it exists and is
584 not None. m.__test__ maps names to functions, classes and strings;
584 not None. m.__test__ maps names to functions, classes and strings;
585 function and class docstrings are tested even if the name is private;
585 function and class docstrings are tested even if the name is private;
586 strings are tested directly, as if they were docstrings.
586 strings are tested directly, as if they were docstrings.
587
587
588 Return (#failures, #tests).
588 Return (#failures, #tests).
589
589
590 See doctest.__doc__ for an overview.
590 See doctest.__doc__ for an overview.
591
591
592 Optional keyword arg "name" gives the name of the module; by default
592 Optional keyword arg "name" gives the name of the module; by default
593 use m.__name__.
593 use m.__name__.
594
594
595 Optional keyword arg "globs" gives a dict to be used as the globals
595 Optional keyword arg "globs" gives a dict to be used as the globals
596 when executing examples; by default, use m.__dict__. A copy of this
596 when executing examples; by default, use m.__dict__. A copy of this
597 dict is actually used for each docstring, so that each docstring's
597 dict is actually used for each docstring, so that each docstring's
598 examples start with a clean slate.
598 examples start with a clean slate.
599
599
600 Optional keyword arg "extraglobs" gives a dictionary that should be
600 Optional keyword arg "extraglobs" gives a dictionary that should be
601 merged into the globals that are used to execute examples. By
601 merged into the globals that are used to execute examples. By
602 default, no extra globals are used. This is new in 2.4.
602 default, no extra globals are used. This is new in 2.4.
603
603
604 Optional keyword arg "verbose" prints lots of stuff if true, prints
604 Optional keyword arg "verbose" prints lots of stuff if true, prints
605 only failures if false; by default, it's true iff "-v" is in sys.argv.
605 only failures if false; by default, it's true iff "-v" is in sys.argv.
606
606
607 Optional keyword arg "report" prints a summary at the end when true,
607 Optional keyword arg "report" prints a summary at the end when true,
608 else prints nothing at the end. In verbose mode, the summary is
608 else prints nothing at the end. In verbose mode, the summary is
609 detailed, else very brief (in fact, empty if all tests passed).
609 detailed, else very brief (in fact, empty if all tests passed).
610
610
611 Optional keyword arg "optionflags" or's together module constants,
611 Optional keyword arg "optionflags" or's together module constants,
612 and defaults to 0. This is new in 2.3. Possible values (see the
612 and defaults to 0. This is new in 2.3. Possible values (see the
613 docs for details):
613 docs for details):
614
614
615 DONT_ACCEPT_TRUE_FOR_1
615 DONT_ACCEPT_TRUE_FOR_1
616 DONT_ACCEPT_BLANKLINE
616 DONT_ACCEPT_BLANKLINE
617 NORMALIZE_WHITESPACE
617 NORMALIZE_WHITESPACE
618 ELLIPSIS
618 ELLIPSIS
619 SKIP
619 SKIP
620 IGNORE_EXCEPTION_DETAIL
620 IGNORE_EXCEPTION_DETAIL
621 REPORT_UDIFF
621 REPORT_UDIFF
622 REPORT_CDIFF
622 REPORT_CDIFF
623 REPORT_NDIFF
623 REPORT_NDIFF
624 REPORT_ONLY_FIRST_FAILURE
624 REPORT_ONLY_FIRST_FAILURE
625
625
626 Optional keyword arg "raise_on_error" raises an exception on the
626 Optional keyword arg "raise_on_error" raises an exception on the
627 first unexpected exception or failure. This allows failures to be
627 first unexpected exception or failure. This allows failures to be
628 post-mortem debugged.
628 post-mortem debugged.
629
629
630 Advanced tomfoolery: testmod runs methods of a local instance of
630 Advanced tomfoolery: testmod runs methods of a local instance of
631 class doctest.Tester, then merges the results into (or creates)
631 class doctest.Tester, then merges the results into (or creates)
632 global Tester instance doctest.master. Methods of doctest.master
632 global Tester instance doctest.master. Methods of doctest.master
633 can be called directly too, if you want to do something unusual.
633 can be called directly too, if you want to do something unusual.
634 Passing report=0 to testmod is especially useful then, to delay
634 Passing report=0 to testmod is especially useful then, to delay
635 displaying a summary. Invoke doctest.master.summarize(verbose)
635 displaying a summary. Invoke doctest.master.summarize(verbose)
636 when you're done fiddling.
636 when you're done fiddling.
637 """
637 """
638 global master
638 global master
639
639
640 # If no module was given, then use __main__.
640 # If no module was given, then use __main__.
641 if m is None:
641 if m is None:
642 # DWA - m will still be None if this wasn't invoked from the command
642 # DWA - m will still be None if this wasn't invoked from the command
643 # line, in which case the following TypeError is about as good an error
643 # line, in which case the following TypeError is about as good an error
644 # as we should expect
644 # as we should expect
645 m = sys.modules.get('__main__')
645 m = sys.modules.get('__main__')
646
646
647 # Check that we were actually given a module.
647 # Check that we were actually given a module.
648 if not inspect.ismodule(m):
648 if not inspect.ismodule(m):
649 raise TypeError("testmod: module required; %r" % (m,))
649 raise TypeError("testmod: module required; %r" % (m,))
650
650
651 # If no name was given, then use the module's name.
651 # If no name was given, then use the module's name.
652 if name is None:
652 if name is None:
653 name = m.__name__
653 name = m.__name__
654
654
655 #----------------------------------------------------------------------
655 #----------------------------------------------------------------------
656 # fperez - make IPython finder and runner:
656 # fperez - make IPython finder and runner:
657 # Find, parse, and run all tests in the given module.
657 # Find, parse, and run all tests in the given module.
658 finder = DocTestFinder(exclude_empty=exclude_empty,
658 finder = DocTestFinder(exclude_empty=exclude_empty,
659 parser=IPDocTestParser())
659 parser=IPDocTestParser())
660
660
661 if raise_on_error:
661 if raise_on_error:
662 runner = IPDebugRunner(verbose=verbose, optionflags=optionflags)
662 runner = IPDebugRunner(verbose=verbose, optionflags=optionflags)
663 else:
663 else:
664 runner = IPDocTestRunner(verbose=verbose, optionflags=optionflags,
664 runner = IPDocTestRunner(verbose=verbose, optionflags=optionflags,
665 #checker=IPOutputChecker() # dbg
665 #checker=IPOutputChecker() # dbg
666 )
666 )
667
667
668 # /fperez - end of ipython changes
668 # /fperez - end of ipython changes
669 #----------------------------------------------------------------------
669 #----------------------------------------------------------------------
670
670
671 for test in finder.find(m, name, globs=globs, extraglobs=extraglobs):
671 for test in finder.find(m, name, globs=globs, extraglobs=extraglobs):
672 runner.run(test)
672 runner.run(test)
673
673
674 if report:
674 if report:
675 runner.summarize()
675 runner.summarize()
676
676
677 if master is None:
677 if master is None:
678 master = runner
678 master = runner
679 else:
679 else:
680 master.merge(runner)
680 master.merge(runner)
681
681
682 return runner.failures, runner.tries
682 return runner.failures, runner.tries
683
683
684
684
685 # Simple testing and example code
685 # Simple testing and example code
686 if __name__ == "__main__":
686 if __name__ == "__main__":
687
687
688 def ipfunc():
688 def ipfunc():
689 """
689 """
690 Some ipython tests...
690 Some ipython tests...
691
691
692 In [1]: import os
692 In [1]: import os
693
693
694 In [2]: cd /
694 In [2]: cd /
695 /
695 /
696
696
697 In [3]: 2+3
697 In [3]: 2+3
698 Out[3]: 5
698 Out[3]: 5
699
699
700 In [26]: for i in range(3):
700 In [26]: for i in range(3):
701 ....: print i,
701 ....: print i,
702 ....: print i+1,
702 ....: print i+1,
703 ....:
703 ....:
704 0 1 1 2 2 3
704 0 1 1 2 2 3
705
705
706
706
707 Examples that access the operating system work:
707 Examples that access the operating system work:
708
708
709 In [19]: cd /tmp
709 In [19]: cd /tmp
710 /tmp
710 /tmp
711
711
712 In [20]: mkdir foo_ipython
712 In [20]: mkdir foo_ipython
713
713
714 In [21]: cd foo_ipython
714 In [21]: cd foo_ipython
715 /tmp/foo_ipython
715 /tmp/foo_ipython
716
716
717 In [23]: !touch bar baz
717 In [23]: !touch bar baz
718
718
719 # We unfortunately can't just call 'ls' because its output is not
719 # We unfortunately can't just call 'ls' because its output is not
720 # seen by doctest, since it happens in a separate process
720 # seen by doctest, since it happens in a separate process
721
721
722 In [24]: os.listdir('.')
722 In [24]: os.listdir('.')
723 Out[24]: ['bar', 'baz']
723 Out[24]: ['bar', 'baz']
724
724
725 In [25]: cd /tmp
725 In [25]: cd /tmp
726 /tmp
726 /tmp
727
727
728 In [26]: rm -rf foo_ipython
728 In [26]: rm -rf foo_ipython
729
729
730
730
731 It's OK to use '_' for the last result, but do NOT try to use IPython's
731 It's OK to use '_' for the last result, but do NOT try to use IPython's
732 numbered history of _NN outputs, since those won't exist under the
732 numbered history of _NN outputs, since those won't exist under the
733 doctest environment:
733 doctest environment:
734
734
735 In [7]: 3+4
735 In [7]: 3+4
736 Out[7]: 7
736 Out[7]: 7
737
737
738 In [8]: _+3
738 In [8]: _+3
739 Out[8]: 10
739 Out[8]: 10
740 """
740 """
741
741
742 def ipfunc_external():
742 def ipfunc_external():
743 """
743 """
744 Tests that must be run in an external process
744 Tests that must be run in an external process
745
745
746
746
747 # ipdoctest: EXTERNAL
747 # ipdoctest: EXTERNAL
748
748
749 In [11]: for i in range(10):
749 In [11]: for i in range(10):
750 ....: print i,
750 ....: print i,
751 ....: print i+1,
751 ....: print i+1,
752 ....:
752 ....:
753 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10
753 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10
754
754
755
755
756 In [1]: import os
756 In [1]: import os
757
757
758 In [1]: print "hello"
758 In [1]: print "hello"
759 hello
759 hello
760
760
761 In [19]: cd /tmp
761 In [19]: cd /tmp
762 /tmp
762 /tmp
763
763
764 In [20]: mkdir foo_ipython2
764 In [20]: mkdir foo_ipython2
765
765
766 In [21]: cd foo_ipython2
766 In [21]: cd foo_ipython2
767 /tmp/foo_ipython2
767 /tmp/foo_ipython2
768
768
769 In [23]: !touch bar baz
769 In [23]: !touch bar baz
770
770
771 In [24]: ls
771 In [24]: ls
772 bar baz
772 bar baz
773
773
774 In [24]: !ls
774 In [24]: !ls
775 bar baz
775 bar baz
776
776
777 In [25]: cd /tmp
777 In [25]: cd /tmp
778 /tmp
778 /tmp
779
779
780 In [26]: rm -rf foo_ipython2
780 In [26]: rm -rf foo_ipython2
781 """
781 """
782
782
783 def pyfunc():
783 def pyfunc():
784 """
784 """
785 Some pure python tests...
785 Some pure python tests...
786
786
787 >>> import os
787 >>> import os
788
788
789 >>> 2+3
789 >>> 2+3
790 5
790 5
791
791
792 >>> for i in range(3):
792 >>> for i in range(3):
793 ... print i,
793 ... print i,
794 ... print i+1,
794 ... print i+1,
795 ...
795 ...
796 0 1 1 2 2 3
796 0 1 1 2 2 3
797 """
797 """
798
798
799 # Call the global testmod() just like you would with normal doctest
799 # Call the global testmod() just like you would with normal doctest
800 testmod()
800 testmod()
@@ -1,300 +1,300 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 time
27 import time
28 import warnings
28 import warnings
29
29
30 import nose.plugins.builtin
30 import nose.plugins.builtin
31 from nose.core import TestProgram
31 from nose.core import TestProgram
32
32
33 from IPython.platutils import find_cmd
33 from IPython.platutils import find_cmd
34 from IPython.testing.plugin.ipdoctest import IPythonDoctest
34 from IPython.testing.plugin.ipdoctest import IPythonDoctest
35
35
36 pjoin = path.join
36 pjoin = path.join
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Logic for skipping doctests
39 # Logic for skipping doctests
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 def test_for(mod):
42 def test_for(mod):
43 """Test to see if mod is importable."""
43 """Test to see if mod is importable."""
44 try:
44 try:
45 __import__(mod)
45 __import__(mod)
46 except ImportError:
46 except ImportError:
47 return False
47 return False
48 else:
48 else:
49 return True
49 return True
50
50
51 have_curses = test_for('_curses')
51 have_curses = test_for('_curses')
52 have_wx = test_for('wx')
52 have_wx = test_for('wx')
53 have_zi = test_for('zope.interface')
53 have_zi = test_for('zope.interface')
54 have_twisted = test_for('twisted')
54 have_twisted = test_for('twisted')
55 have_foolscap = test_for('foolscap')
55 have_foolscap = test_for('foolscap')
56 have_objc = test_for('objc')
56 have_objc = test_for('objc')
57 have_pexpect = test_for('pexpect')
57 have_pexpect = test_for('pexpect')
58
58
59 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
59 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
60 # testing problems. We should strive to minimize the number of skipped
60 # testing problems. We should strive to minimize the number of skipped
61 # modules, since this means untested code. As the testing machinery
61 # modules, since this means untested code. As the testing machinery
62 # solidifies, this list should eventually become empty.
62 # solidifies, this list should eventually become empty.
63 EXCLUDE = [pjoin('IPython', 'external'),
63 EXCLUDE = [pjoin('IPython', 'external'),
64 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
64 pjoin('IPython', 'frontend', 'process', 'winprocess.py'),
65 pjoin('IPython_doctest_plugin'),
65 pjoin('IPython_doctest_plugin'),
66 pjoin('IPython', 'Gnuplot'),
66 pjoin('IPython', 'Gnuplot'),
67 pjoin('IPython', 'Extensions', 'ipy_'),
67 pjoin('IPython', 'Extensions', 'ipy_'),
68 pjoin('IPython', 'Extensions', 'clearcmd'),
68 pjoin('IPython', 'Extensions', 'clearcmd'),
69 pjoin('IPython', 'Extensions', 'PhysicalQInteractive'),
69 pjoin('IPython', 'Extensions', 'PhysicalQInteractive'),
70 pjoin('IPython', 'Extensions', 'scitedirector'),
70 pjoin('IPython', 'Extensions', 'scitedirector'),
71 pjoin('IPython', 'Extensions', 'numeric_formats'),
71 pjoin('IPython', 'Extensions', 'numeric_formats'),
72 pjoin('IPython', 'testing', 'attic'),
72 pjoin('IPython', 'testing', 'attic'),
73 pjoin('IPython', 'testing', 'tutils'),
73 pjoin('IPython', 'testing', 'tutils'),
74 pjoin('IPython', 'testing', 'tools'),
74 pjoin('IPython', 'testing', 'tools'),
75 pjoin('IPython', 'testing', 'mkdoctests')
75 pjoin('IPython', 'testing', 'mkdoctests')
76 ]
76 ]
77
77
78 if not have_wx:
78 if not have_wx:
79 EXCLUDE.append(pjoin('IPython', 'Extensions', 'igrid'))
79 EXCLUDE.append(pjoin('IPython', 'Extensions', 'igrid'))
80 EXCLUDE.append(pjoin('IPython', 'gui'))
80 EXCLUDE.append(pjoin('IPython', 'gui'))
81 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
81 EXCLUDE.append(pjoin('IPython', 'frontend', 'wx'))
82
82
83 if not have_objc:
83 if not have_objc:
84 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
84 EXCLUDE.append(pjoin('IPython', 'frontend', 'cocoa'))
85
85
86 if not have_curses:
86 if not have_curses:
87 EXCLUDE.append(pjoin('IPython', 'Extensions', 'ibrowse'))
87 EXCLUDE.append(pjoin('IPython', 'Extensions', 'ibrowse'))
88
88
89 if not sys.platform == 'win32':
89 if not sys.platform == 'win32':
90 EXCLUDE.append(pjoin('IPython', 'platutils_win32'))
90 EXCLUDE.append(pjoin('IPython', 'platutils_win32'))
91
91
92 # These have to be skipped on win32 because the use echo, rm, cd, etc.
92 # These have to be skipped on win32 because the use echo, rm, cd, etc.
93 # See ticket https://bugs.launchpad.net/bugs/366982
93 # See ticket https://bugs.launchpad.net/bugs/366982
94 if sys.platform == 'win32':
94 if sys.platform == 'win32':
95 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
95 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'test_exampleip'))
96 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
96 EXCLUDE.append(pjoin('IPython', 'testing', 'plugin', 'dtexample'))
97
97
98 if not os.name == 'posix':
98 if not os.name == 'posix':
99 EXCLUDE.append(pjoin('IPython', 'platutils_posix'))
99 EXCLUDE.append(pjoin('IPython', 'platutils_posix'))
100
100
101 if not have_pexpect:
101 if not have_pexpect:
102 EXCLUDE.append(pjoin('IPython', 'irunner'))
102 EXCLUDE.append(pjoin('IPython', 'lib', 'irunner'))
103
103
104 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
104 # This is needed for the reg-exp to match on win32 in the ipdoctest plugin.
105 if sys.platform == 'win32':
105 if sys.platform == 'win32':
106 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
106 EXCLUDE = [s.replace('\\','\\\\') for s in EXCLUDE]
107
107
108
108
109 #-----------------------------------------------------------------------------
109 #-----------------------------------------------------------------------------
110 # Functions and classes
110 # Functions and classes
111 #-----------------------------------------------------------------------------
111 #-----------------------------------------------------------------------------
112
112
113 def run_iptest():
113 def run_iptest():
114 """Run the IPython test suite using nose.
114 """Run the IPython test suite using nose.
115
115
116 This function is called when this script is **not** called with the form
116 This function is called when this script is **not** called with the form
117 `iptest all`. It simply calls nose with appropriate command line flags
117 `iptest all`. It simply calls nose with appropriate command line flags
118 and accepts all of the standard nose arguments.
118 and accepts all of the standard nose arguments.
119 """
119 """
120
120
121 warnings.filterwarnings('ignore',
121 warnings.filterwarnings('ignore',
122 'This will be removed soon. Use IPython.testing.util instead')
122 'This will be removed soon. Use IPython.testing.util instead')
123
123
124 argv = sys.argv + [
124 argv = sys.argv + [
125 # Loading ipdoctest causes problems with Twisted.
125 # Loading ipdoctest causes problems with Twisted.
126 # I am removing this as a temporary fix to get the
126 # I am removing this as a temporary fix to get the
127 # test suite back into working shape. Our nose
127 # test suite back into working shape. Our nose
128 # plugin needs to be gone through with a fine
128 # plugin needs to be gone through with a fine
129 # toothed comb to find what is causing the problem.
129 # toothed comb to find what is causing the problem.
130 '--with-ipdoctest',
130 '--with-ipdoctest',
131 '--ipdoctest-tests','--ipdoctest-extension=txt',
131 '--ipdoctest-tests','--ipdoctest-extension=txt',
132 '--detailed-errors',
132 '--detailed-errors',
133
133
134 # We add --exe because of setuptools' imbecility (it
134 # We add --exe because of setuptools' imbecility (it
135 # blindly does chmod +x on ALL files). Nose does the
135 # blindly does chmod +x on ALL files). Nose does the
136 # right thing and it tries to avoid executables,
136 # right thing and it tries to avoid executables,
137 # setuptools unfortunately forces our hand here. This
137 # setuptools unfortunately forces our hand here. This
138 # has been discussed on the distutils list and the
138 # has been discussed on the distutils list and the
139 # setuptools devs refuse to fix this problem!
139 # setuptools devs refuse to fix this problem!
140 '--exe',
140 '--exe',
141 ]
141 ]
142
142
143 # Detect if any tests were required by explicitly calling an IPython
143 # Detect if any tests were required by explicitly calling an IPython
144 # submodule or giving a specific path
144 # submodule or giving a specific path
145 has_tests = False
145 has_tests = False
146 for arg in sys.argv:
146 for arg in sys.argv:
147 if 'IPython' in arg or arg.endswith('.py') or \
147 if 'IPython' in arg or arg.endswith('.py') or \
148 (':' in arg and '.py' in arg):
148 (':' in arg and '.py' in arg):
149 has_tests = True
149 has_tests = True
150 break
150 break
151
151
152 # If nothing was specifically requested, test full IPython
152 # If nothing was specifically requested, test full IPython
153 if not has_tests:
153 if not has_tests:
154 argv.append('IPython')
154 argv.append('IPython')
155
155
156 # Construct list of plugins, omitting the existing doctest plugin, which
156 # Construct list of plugins, omitting the existing doctest plugin, which
157 # ours replaces (and extends).
157 # ours replaces (and extends).
158 plugins = [IPythonDoctest(EXCLUDE)]
158 plugins = [IPythonDoctest(EXCLUDE)]
159 for p in nose.plugins.builtin.plugins:
159 for p in nose.plugins.builtin.plugins:
160 plug = p()
160 plug = p()
161 if plug.name == 'doctest':
161 if plug.name == 'doctest':
162 continue
162 continue
163
163
164 #print '*** adding plugin:',plug.name # dbg
164 #print '*** adding plugin:',plug.name # dbg
165 plugins.append(plug)
165 plugins.append(plug)
166
166
167 TestProgram(argv=argv,plugins=plugins)
167 TestProgram(argv=argv,plugins=plugins)
168
168
169
169
170 class IPTester(object):
170 class IPTester(object):
171 """Call that calls iptest or trial in a subprocess.
171 """Call that calls iptest or trial in a subprocess.
172 """
172 """
173 def __init__(self,runner='iptest',params=None):
173 def __init__(self,runner='iptest',params=None):
174 """ """
174 """ """
175 if runner == 'iptest':
175 if runner == 'iptest':
176 self.runner = ['iptest','-v']
176 self.runner = ['iptest','-v']
177 else:
177 else:
178 self.runner = [find_cmd('trial')]
178 self.runner = [find_cmd('trial')]
179 if params is None:
179 if params is None:
180 params = []
180 params = []
181 if isinstance(params,str):
181 if isinstance(params,str):
182 params = [params]
182 params = [params]
183 self.params = params
183 self.params = params
184
184
185 # Assemble call
185 # Assemble call
186 self.call_args = self.runner+self.params
186 self.call_args = self.runner+self.params
187
187
188 def run(self):
188 def run(self):
189 """Run the stored commands"""
189 """Run the stored commands"""
190 return subprocess.call(self.call_args)
190 return subprocess.call(self.call_args)
191
191
192
192
193 def make_runners():
193 def make_runners():
194 """Define the modules and packages that need to be tested.
194 """Define the modules and packages that need to be tested.
195 """
195 """
196
196
197 # This omits additional top-level modules that should not be doctested.
197 # This omits additional top-level modules that should not be doctested.
198 # XXX: Shell.py is also ommited because of a bug in the skip_doctest
198 # XXX: Shell.py is also ommited because of a bug in the skip_doctest
199 # decorator. See ticket https://bugs.launchpad.net/bugs/366209
199 # decorator. See ticket https://bugs.launchpad.net/bugs/366209
200 top_mod = \
200 top_mod = \
201 ['backgroundjobs.py', 'coloransi.py', 'completer.py', 'configloader.py',
201 ['backgroundjobs.py', 'coloransi.py', 'completer.py', 'configloader.py',
202 'crashhandler.py', 'debugger.py', 'deepreload.py', 'demo.py',
202 'crashhandler.py', 'debugger.py', 'deepreload.py', 'demo.py',
203 'DPyGetOpt.py', 'dtutils.py', 'excolors.py', 'fakemodule.py',
203 'DPyGetOpt.py', 'dtutils.py', 'excolors.py', 'fakemodule.py',
204 'generics.py', 'genutils.py', 'history.py', 'hooks.py', 'ipapi.py',
204 'generics.py', 'genutils.py', 'history.py', 'hooks.py', 'ipapi.py',
205 'iplib.py', 'ipmaker.py', 'ipstruct.py', 'Itpl.py',
205 'iplib.py', 'ipmaker.py', 'ipstruct.py', 'Itpl.py',
206 'logger.py', 'macro.py', 'Magic.py', 'OInspect.py',
206 'logger.py', 'macro.py', 'Magic.py', 'OInspect.py',
207 'OutputTrap.py', 'platutils.py', 'prefilter.py', 'Prompts.py',
207 'OutputTrap.py', 'platutils.py', 'prefilter.py', 'Prompts.py',
208 'PyColorize.py', 'Release.py', 'rlineimpl.py', 'shadowns.py',
208 'PyColorize.py', 'Release.py', 'rlineimpl.py', 'shadowns.py',
209 'shellglobals.py', 'strdispatch.py', 'twshell.py',
209 'shellglobals.py', 'strdispatch.py', 'twshell.py',
210 'ultraTB.py', 'upgrade_dir.py', 'usage.py', 'wildcard.py',
210 'ultraTB.py', 'upgrade_dir.py', 'usage.py', 'wildcard.py',
211 # See note above for why this is skipped
211 # See note above for why this is skipped
212 # 'Shell.py',
212 # 'Shell.py',
213 'winconsole.py']
213 'winconsole.py']
214
214
215 if have_pexpect:
215 if have_pexpect:
216 top_mod.append('irunner.py')
216 top_mod.append('irunner.py')
217
217
218 if sys.platform == 'win32':
218 if sys.platform == 'win32':
219 top_mod.append('platutils_win32.py')
219 top_mod.append('platutils_win32.py')
220 elif os.name == 'posix':
220 elif os.name == 'posix':
221 top_mod.append('platutils_posix.py')
221 top_mod.append('platutils_posix.py')
222 else:
222 else:
223 top_mod.append('platutils_dummy.py')
223 top_mod.append('platutils_dummy.py')
224
224
225 # These are tested by nose, so skip IPython.kernel
225 # These are tested by nose, so skip IPython.kernel
226 top_pack = ['config','Extensions','frontend',
226 top_pack = ['config','Extensions','frontend',
227 'testing','tests','tools','UserConfig']
227 'testing','tests','tools','UserConfig']
228
228
229 if have_wx:
229 if have_wx:
230 top_pack.append('gui')
230 top_pack.append('gui')
231
231
232 modules = ['IPython.%s' % m[:-3] for m in top_mod ]
232 modules = ['IPython.%s' % m[:-3] for m in top_mod ]
233 packages = ['IPython.%s' % m for m in top_pack ]
233 packages = ['IPython.%s' % m for m in top_pack ]
234
234
235 # Make runners
235 # Make runners
236 runners = dict(zip(top_pack, [IPTester(params=v) for v in packages]))
236 runners = dict(zip(top_pack, [IPTester(params=v) for v in packages]))
237
237
238 # Test IPython.kernel using trial if twisted is installed
238 # Test IPython.kernel using trial if twisted is installed
239 if have_zi and have_twisted and have_foolscap:
239 if have_zi and have_twisted and have_foolscap:
240 runners['trial'] = IPTester('trial',['IPython'])
240 runners['trial'] = IPTester('trial',['IPython'])
241
241
242 runners['modules'] = IPTester(params=modules)
242 runners['modules'] = IPTester(params=modules)
243
243
244 return runners
244 return runners
245
245
246
246
247 def run_iptestall():
247 def run_iptestall():
248 """Run the entire IPython test suite by calling nose and trial.
248 """Run the entire IPython test suite by calling nose and trial.
249
249
250 This function constructs :class:`IPTester` instances for all IPython
250 This function constructs :class:`IPTester` instances for all IPython
251 modules and package and then runs each of them. This causes the modules
251 modules and package and then runs each of them. This causes the modules
252 and packages of IPython to be tested each in their own subprocess using
252 and packages of IPython to be tested each in their own subprocess using
253 nose or twisted.trial appropriately.
253 nose or twisted.trial appropriately.
254 """
254 """
255 runners = make_runners()
255 runners = make_runners()
256 # Run all test runners, tracking execution time
256 # Run all test runners, tracking execution time
257 failed = {}
257 failed = {}
258 t_start = time.time()
258 t_start = time.time()
259 for name,runner in runners.iteritems():
259 for name,runner in runners.iteritems():
260 print '*'*77
260 print '*'*77
261 print 'IPython test set:',name
261 print 'IPython test set:',name
262 res = runner.run()
262 res = runner.run()
263 if res:
263 if res:
264 failed[name] = res
264 failed[name] = res
265 t_end = time.time()
265 t_end = time.time()
266 t_tests = t_end - t_start
266 t_tests = t_end - t_start
267 nrunners = len(runners)
267 nrunners = len(runners)
268 nfail = len(failed)
268 nfail = len(failed)
269 # summarize results
269 # summarize results
270 print
270 print
271 print '*'*77
271 print '*'*77
272 print 'Ran %s test sets in %.3fs' % (nrunners, t_tests)
272 print 'Ran %s test sets in %.3fs' % (nrunners, t_tests)
273 print
273 print
274 if not failed:
274 if not failed:
275 print 'OK'
275 print 'OK'
276 else:
276 else:
277 # If anything went wrong, point out what command to rerun manually to
277 # If anything went wrong, point out what command to rerun manually to
278 # see the actual errors and individual summary
278 # see the actual errors and individual summary
279 print 'ERROR - %s out of %s test sets failed.' % (nfail, nrunners)
279 print 'ERROR - %s out of %s test sets failed.' % (nfail, nrunners)
280 for name in failed:
280 for name in failed:
281 failed_runner = runners[name]
281 failed_runner = runners[name]
282 print '-'*40
282 print '-'*40
283 print 'Runner failed:',name
283 print 'Runner failed:',name
284 print 'You may wish to rerun this one individually, with:'
284 print 'You may wish to rerun this one individually, with:'
285 print ' '.join(failed_runner.call_args)
285 print ' '.join(failed_runner.call_args)
286 print
286 print
287
287
288
288
289 def main():
289 def main():
290 if len(sys.argv) == 1:
290 if len(sys.argv) == 1:
291 run_iptestall()
291 run_iptestall()
292 else:
292 else:
293 if sys.argv[1] == 'all':
293 if sys.argv[1] == 'all':
294 run_iptestall()
294 run_iptestall()
295 else:
295 else:
296 run_iptest()
296 run_iptest()
297
297
298
298
299 if __name__ == '__main__':
299 if __name__ == '__main__':
300 main() No newline at end of file
300 main()
@@ -1,244 +1,244 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """Utility for making a doctest file out of Python or IPython input.
2 """Utility for making a doctest file out of Python or IPython input.
3
3
4 %prog [options] input_file [output_file]
4 %prog [options] input_file [output_file]
5
5
6 This script is a convenient generator of doctest files that uses IPython's
6 This script is a convenient generator of doctest files that uses IPython's
7 irunner script to execute valid Python or IPython input in a separate process,
7 irunner script to execute valid Python or IPython input in a separate process,
8 capture all of the output, and write it to an output file.
8 capture all of the output, and write it to an output file.
9
9
10 It can be used in one of two ways:
10 It can be used in one of two ways:
11
11
12 1. With a plain Python or IPython input file (denoted by extensions '.py' or
12 1. With a plain Python or IPython input file (denoted by extensions '.py' or
13 '.ipy'. In this case, the output is an auto-generated reST file with a
13 '.ipy'. In this case, the output is an auto-generated reST file with a
14 basic header, and the captured Python input and output contained in an
14 basic header, and the captured Python input and output contained in an
15 indented code block.
15 indented code block.
16
16
17 If no output filename is given, the input name is used, with the extension
17 If no output filename is given, the input name is used, with the extension
18 replaced by '.txt'.
18 replaced by '.txt'.
19
19
20 2. With an input template file. Template files are simply plain text files
20 2. With an input template file. Template files are simply plain text files
21 with special directives of the form
21 with special directives of the form
22
22
23 %run filename
23 %run filename
24
24
25 to include the named file at that point.
25 to include the named file at that point.
26
26
27 If no output filename is given and the input filename is of the form
27 If no output filename is given and the input filename is of the form
28 'base.tpl.txt', the output will be automatically named 'base.txt'.
28 'base.tpl.txt', the output will be automatically named 'base.txt'.
29 """
29 """
30
30
31 # Standard library imports
31 # Standard library imports
32
32
33 import optparse
33 import optparse
34 import os
34 import os
35 import re
35 import re
36 import sys
36 import sys
37 import tempfile
37 import tempfile
38
38
39 # IPython-specific libraries
39 # IPython-specific libraries
40 from IPython import irunner
40 from IPython.lib import irunner
41 from IPython.utils.genutils import fatal
41 from IPython.utils.genutils import fatal
42
42
43 class IndentOut(object):
43 class IndentOut(object):
44 """A simple output stream that indents all output by a fixed amount.
44 """A simple output stream that indents all output by a fixed amount.
45
45
46 Instances of this class trap output to a given stream and first reformat it
46 Instances of this class trap output to a given stream and first reformat it
47 to indent every input line."""
47 to indent every input line."""
48
48
49 def __init__(self,out=sys.stdout,indent=4):
49 def __init__(self,out=sys.stdout,indent=4):
50 """Create an indented writer.
50 """Create an indented writer.
51
51
52 :Keywords:
52 :Keywords:
53
53
54 - `out` : stream (sys.stdout)
54 - `out` : stream (sys.stdout)
55 Output stream to actually write to after indenting.
55 Output stream to actually write to after indenting.
56
56
57 - `indent` : int
57 - `indent` : int
58 Number of spaces to indent every input line by.
58 Number of spaces to indent every input line by.
59 """
59 """
60
60
61 self.indent_text = ' '*indent
61 self.indent_text = ' '*indent
62 self.indent = re.compile('^',re.MULTILINE).sub
62 self.indent = re.compile('^',re.MULTILINE).sub
63 self.out = out
63 self.out = out
64 self._write = out.write
64 self._write = out.write
65 self.buffer = []
65 self.buffer = []
66 self._closed = False
66 self._closed = False
67
67
68 def write(self,data):
68 def write(self,data):
69 """Write a string to the output stream."""
69 """Write a string to the output stream."""
70
70
71 if self._closed:
71 if self._closed:
72 raise ValueError('I/O operation on closed file')
72 raise ValueError('I/O operation on closed file')
73 self.buffer.append(data)
73 self.buffer.append(data)
74
74
75 def flush(self):
75 def flush(self):
76 if self.buffer:
76 if self.buffer:
77 data = ''.join(self.buffer)
77 data = ''.join(self.buffer)
78 self.buffer[:] = []
78 self.buffer[:] = []
79 self._write(self.indent(self.indent_text,data))
79 self._write(self.indent(self.indent_text,data))
80
80
81 def close(self):
81 def close(self):
82 self.flush()
82 self.flush()
83 self._closed = True
83 self._closed = True
84
84
85 class RunnerFactory(object):
85 class RunnerFactory(object):
86 """Code runner factory.
86 """Code runner factory.
87
87
88 This class provides an IPython code runner, but enforces that only one
88 This class provides an IPython code runner, but enforces that only one
89 runner is every instantiated. The runner is created based on the extension
89 runner is every instantiated. The runner is created based on the extension
90 of the first file to run, and it raises an exception if a runner is later
90 of the first file to run, and it raises an exception if a runner is later
91 requested for a different extension type.
91 requested for a different extension type.
92
92
93 This ensures that we don't generate example files for doctest with a mix of
93 This ensures that we don't generate example files for doctest with a mix of
94 python and ipython syntax.
94 python and ipython syntax.
95 """
95 """
96
96
97 def __init__(self,out=sys.stdout):
97 def __init__(self,out=sys.stdout):
98 """Instantiate a code runner."""
98 """Instantiate a code runner."""
99
99
100 self.out = out
100 self.out = out
101 self.runner = None
101 self.runner = None
102 self.runnerClass = None
102 self.runnerClass = None
103
103
104 def _makeRunner(self,runnerClass):
104 def _makeRunner(self,runnerClass):
105 self.runnerClass = runnerClass
105 self.runnerClass = runnerClass
106 self.runner = runnerClass(out=self.out)
106 self.runner = runnerClass(out=self.out)
107 return self.runner
107 return self.runner
108
108
109 def __call__(self,fname):
109 def __call__(self,fname):
110 """Return a runner for the given filename."""
110 """Return a runner for the given filename."""
111
111
112 if fname.endswith('.py'):
112 if fname.endswith('.py'):
113 runnerClass = irunner.PythonRunner
113 runnerClass = irunner.PythonRunner
114 elif fname.endswith('.ipy'):
114 elif fname.endswith('.ipy'):
115 runnerClass = irunner.IPythonRunner
115 runnerClass = irunner.IPythonRunner
116 else:
116 else:
117 raise ValueError('Unknown file type for Runner: %r' % fname)
117 raise ValueError('Unknown file type for Runner: %r' % fname)
118
118
119 if self.runner is None:
119 if self.runner is None:
120 return self._makeRunner(runnerClass)
120 return self._makeRunner(runnerClass)
121 else:
121 else:
122 if runnerClass==self.runnerClass:
122 if runnerClass==self.runnerClass:
123 return self.runner
123 return self.runner
124 else:
124 else:
125 e='A runner of type %r can not run file %r' % \
125 e='A runner of type %r can not run file %r' % \
126 (self.runnerClass,fname)
126 (self.runnerClass,fname)
127 raise ValueError(e)
127 raise ValueError(e)
128
128
129 TPL = """
129 TPL = """
130 =========================
130 =========================
131 Auto-generated doctests
131 Auto-generated doctests
132 =========================
132 =========================
133
133
134 This file was auto-generated by IPython in its entirety. If you need finer
134 This file was auto-generated by IPython in its entirety. If you need finer
135 control over the contents, simply make a manual template. See the
135 control over the contents, simply make a manual template. See the
136 mkdoctests.py script for details.
136 mkdoctests.py script for details.
137
137
138 %%run %s
138 %%run %s
139 """
139 """
140
140
141 def main():
141 def main():
142 """Run as a script."""
142 """Run as a script."""
143
143
144 # Parse options and arguments.
144 # Parse options and arguments.
145 parser = optparse.OptionParser(usage=__doc__)
145 parser = optparse.OptionParser(usage=__doc__)
146 newopt = parser.add_option
146 newopt = parser.add_option
147 newopt('-f','--force',action='store_true',dest='force',default=False,
147 newopt('-f','--force',action='store_true',dest='force',default=False,
148 help='Force overwriting of the output file.')
148 help='Force overwriting of the output file.')
149 newopt('-s','--stdout',action='store_true',dest='stdout',default=False,
149 newopt('-s','--stdout',action='store_true',dest='stdout',default=False,
150 help='Use stdout instead of a file for output.')
150 help='Use stdout instead of a file for output.')
151
151
152 opts,args = parser.parse_args()
152 opts,args = parser.parse_args()
153 if len(args) < 1:
153 if len(args) < 1:
154 parser.error("incorrect number of arguments")
154 parser.error("incorrect number of arguments")
155
155
156 # Input filename
156 # Input filename
157 fname = args[0]
157 fname = args[0]
158
158
159 # We auto-generate the output file based on a trivial template to make it
159 # We auto-generate the output file based on a trivial template to make it
160 # really easy to create simple doctests.
160 # really easy to create simple doctests.
161
161
162 auto_gen_output = False
162 auto_gen_output = False
163 try:
163 try:
164 outfname = args[1]
164 outfname = args[1]
165 except IndexError:
165 except IndexError:
166 outfname = None
166 outfname = None
167
167
168 if fname.endswith('.tpl.txt') and outfname is None:
168 if fname.endswith('.tpl.txt') and outfname is None:
169 outfname = fname.replace('.tpl.txt','.txt')
169 outfname = fname.replace('.tpl.txt','.txt')
170 else:
170 else:
171 bname, ext = os.path.splitext(fname)
171 bname, ext = os.path.splitext(fname)
172 if ext in ['.py','.ipy']:
172 if ext in ['.py','.ipy']:
173 auto_gen_output = True
173 auto_gen_output = True
174 if outfname is None:
174 if outfname is None:
175 outfname = bname+'.txt'
175 outfname = bname+'.txt'
176
176
177 # Open input file
177 # Open input file
178
178
179 # In auto-gen mode, we actually change the name of the input file to be our
179 # In auto-gen mode, we actually change the name of the input file to be our
180 # auto-generated template
180 # auto-generated template
181 if auto_gen_output:
181 if auto_gen_output:
182 infile = tempfile.TemporaryFile()
182 infile = tempfile.TemporaryFile()
183 infile.write(TPL % fname)
183 infile.write(TPL % fname)
184 infile.flush()
184 infile.flush()
185 infile.seek(0)
185 infile.seek(0)
186 else:
186 else:
187 infile = open(fname)
187 infile = open(fname)
188
188
189 # Now open the output file. If opts.stdout was given, this overrides any
189 # Now open the output file. If opts.stdout was given, this overrides any
190 # explicit choice of output filename and just directs all output to
190 # explicit choice of output filename and just directs all output to
191 # stdout.
191 # stdout.
192 if opts.stdout:
192 if opts.stdout:
193 outfile = sys.stdout
193 outfile = sys.stdout
194 else:
194 else:
195 # Argument processing finished, start main code
195 # Argument processing finished, start main code
196 if os.path.isfile(outfname) and not opts.force:
196 if os.path.isfile(outfname) and not opts.force:
197 fatal("Output file %r exists, use --force (-f) to overwrite."
197 fatal("Output file %r exists, use --force (-f) to overwrite."
198 % outfname)
198 % outfname)
199 outfile = open(outfname,'w')
199 outfile = open(outfname,'w')
200
200
201
201
202 # all output from included files will be indented
202 # all output from included files will be indented
203 indentOut = IndentOut(outfile,4)
203 indentOut = IndentOut(outfile,4)
204 getRunner = RunnerFactory(indentOut)
204 getRunner = RunnerFactory(indentOut)
205
205
206 # Marker in reST for transition lines
206 # Marker in reST for transition lines
207 rst_transition = '\n'+'-'*76+'\n\n'
207 rst_transition = '\n'+'-'*76+'\n\n'
208
208
209 # local shorthand for loop
209 # local shorthand for loop
210 write = outfile.write
210 write = outfile.write
211
211
212 # Process input, simply writing back out all normal lines and executing the
212 # Process input, simply writing back out all normal lines and executing the
213 # files in lines marked as '%run filename'.
213 # files in lines marked as '%run filename'.
214 for line in infile:
214 for line in infile:
215 if line.startswith('%run '):
215 if line.startswith('%run '):
216 # We don't support files with spaces in their names.
216 # We don't support files with spaces in their names.
217 incfname = line.split()[1]
217 incfname = line.split()[1]
218
218
219 # We make the output of the included file appear bracketed between
219 # We make the output of the included file appear bracketed between
220 # clear reST transition marks, and indent it so that if anyone
220 # clear reST transition marks, and indent it so that if anyone
221 # makes an HTML or PDF out of the file, all doctest input and
221 # makes an HTML or PDF out of the file, all doctest input and
222 # output appears in proper literal blocks.
222 # output appears in proper literal blocks.
223 write(rst_transition)
223 write(rst_transition)
224 write('Begin included file %s::\n\n' % incfname)
224 write('Begin included file %s::\n\n' % incfname)
225
225
226 # I deliberately do NOT trap any exceptions here, so that if
226 # I deliberately do NOT trap any exceptions here, so that if
227 # there's any problem, the user running this at the command line
227 # there's any problem, the user running this at the command line
228 # finds out immediately by the code blowing up, rather than ending
228 # finds out immediately by the code blowing up, rather than ending
229 # up silently with an incomplete or incorrect file.
229 # up silently with an incomplete or incorrect file.
230 getRunner(incfname).run_file(incfname)
230 getRunner(incfname).run_file(incfname)
231
231
232 write('\nEnd included file %s\n' % incfname)
232 write('\nEnd included file %s\n' % incfname)
233 write(rst_transition)
233 write(rst_transition)
234 else:
234 else:
235 # The rest of the input file is just written out
235 # The rest of the input file is just written out
236 write(line)
236 write(line)
237 infile.close()
237 infile.close()
238
238
239 # Don't close sys.stdout!!!
239 # Don't close sys.stdout!!!
240 if outfile is not sys.stdout:
240 if outfile is not sys.stdout:
241 outfile.close()
241 outfile.close()
242
242
243 if __name__ == '__main__':
243 if __name__ == '__main__':
244 main()
244 main()
General Comments 0
You need to be logged in to leave comments. Login now