##// END OF EJS Templates
Cleanup testing machinery.
Fernando Perez -
Show More
@@ -0,0 +1,6 b''
1 """Simple script to instantiate a class for testing %run"""
2
3 class foo: pass
4
5 def f():
6 foo()
@@ -1,55 +1,93 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """IPython Test Suite Runner.
2 """IPython Test Suite Runner.
3
4 This module provides a main entry point to a user script to test IPython itself
5 from the command line. The main() routine can be used in a similar manner to
6 the ``nosetests`` script, and it takes similar arguments, but if no arguments
7 are given it defaults to testing all of IPython. This should be preferred to
8 using plain ``nosetests`` because a number of nose plugins necessary to test
9 IPython correctly are automatically configured by this code.
3 """
10 """
4
11
12 #-----------------------------------------------------------------------------
13 # Module imports
14 #-----------------------------------------------------------------------------
15
16 # stdlib
5 import sys
17 import sys
6 import warnings
18 import warnings
7
19
8 from nose.core import TestProgram
20 # third-party
9 import nose.plugins.builtin
21 import nose.plugins.builtin
22 from nose.core import TestProgram
10
23
24 # Our own imports
11 from IPython.testing.plugin.ipdoctest import IPythonDoctest
25 from IPython.testing.plugin.ipdoctest import IPythonDoctest
12
26
27 #-----------------------------------------------------------------------------
28 # Constants and globals
29 #-----------------------------------------------------------------------------
30
31 # For the IPythonDoctest plugin, we need to exclude certain patterns that cause
32 # testing problems. We should strive to minimize the number of skipped
33 # modules, since this means untested code. As the testing machinery
34 # solidifies, this list should eventually become empty.
35 EXCLUDE = ['IPython/external/',
36 'IPython/platutils_win32',
37 'IPython/frontend/cocoa',
38 'IPython_doctest_plugin',
39 'IPython/Gnuplot',
40 'IPython/Extensions/ipy_',
41 'IPython/Extensions/clearcmd',
42 'IPython/Extensions/PhysicalQIn',
43 'IPython/Extensions/scitedirector',
44 'IPython/testing/plugin',
45 ]
46
47 #-----------------------------------------------------------------------------
48 # Functions and classes
49 #-----------------------------------------------------------------------------
50
13 def main():
51 def main():
14 """Run the IPython test suite.
52 """Run the IPython test suite.
15 """
53 """
16
54
17 warnings.filterwarnings('ignore',
55 warnings.filterwarnings('ignore',
18 'This will be removed soon. Use IPython.testing.util instead')
56 'This will be removed soon. Use IPython.testing.util instead')
19
57
20 argv = sys.argv + [ '--with-ipdoctest',
58 argv = sys.argv + [ '--with-ipdoctest',
21 '--doctest-tests','--doctest-extension=txt',
59 '--doctest-tests','--doctest-extension=txt',
22 '--detailed-errors',
60 '--detailed-errors',
23
61
24 # We add --exe because of setuptools' imbecility (it
62 # We add --exe because of setuptools' imbecility (it
25 # blindly does chmod +x on ALL files). Nose does the
63 # blindly does chmod +x on ALL files). Nose does the
26 # right thing and it tries to avoid executables,
64 # right thing and it tries to avoid executables,
27 # setuptools unfortunately forces our hand here. This
65 # setuptools unfortunately forces our hand here. This
28 # has been discussed on the distutils list and the
66 # has been discussed on the distutils list and the
29 # setuptools devs refuse to fix this problem!
67 # setuptools devs refuse to fix this problem!
30 '--exe',
68 '--exe',
31 ]
69 ]
32
70
33 # Detect if any tests were required by explicitly calling an IPython
71 # Detect if any tests were required by explicitly calling an IPython
34 # submodule or giving a specific path
72 # submodule or giving a specific path
35 has_tests = False
73 has_tests = False
36 for arg in sys.argv:
74 for arg in sys.argv:
37 if 'IPython' in arg or arg.endswith('.py') or \
75 if 'IPython' in arg or arg.endswith('.py') or \
38 (':' in arg and '.py' in arg):
76 (':' in arg and '.py' in arg):
39 has_tests = True
77 has_tests = True
40 break
78 break
41 # If nothing was specifically requested, test full IPython
79 # If nothing was specifically requested, test full IPython
42 if not has_tests:
80 if not has_tests:
43 argv.append('IPython')
81 argv.append('IPython')
44
82
45 # construct list of plugins, omitting the existing doctest plugin
83 # Construct list of plugins, omitting the existing doctest plugin.
46 plugins = [IPythonDoctest()]
84 plugins = [IPythonDoctest(EXCLUDE)]
47 for p in nose.plugins.builtin.plugins:
85 for p in nose.plugins.builtin.plugins:
48 plug = p()
86 plug = p()
49 if plug.name == 'doctest':
87 if plug.name == 'doctest':
50 continue
88 continue
51
89
52 #print '*** adding plugin:',plug.name # dbg
90 #print '*** adding plugin:',plug.name # dbg
53 plugins.append(plug)
91 plugins.append(plug)
54
92
55 TestProgram(argv=argv,plugins=plugins)
93 TestProgram(argv=argv,plugins=plugins)
@@ -1,843 +1,847 b''
1 """Nose Plugin that supports IPython doctests.
1 """Nose Plugin that supports IPython doctests.
2
2
3 Limitations:
3 Limitations:
4
4
5 - When generating examples for use as doctests, make sure that you have
5 - When generating examples for use as doctests, make sure that you have
6 pretty-printing OFF. This can be done either by starting ipython with the
6 pretty-printing OFF. This can be done either by starting ipython with the
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
8 interactively disabling it with %Pprint. This is required so that IPython
8 interactively disabling it with %Pprint. This is required so that IPython
9 output matches that of normal Python, which is used by doctest for internal
9 output matches that of normal Python, which is used by doctest for internal
10 execution.
10 execution.
11
11
12 - Do not rely on specific prompt numbers for results (such as using
12 - Do not rely on specific prompt numbers for results (such as using
13 '_34==True', for example). For IPython tests run via an external process the
13 '_34==True', for example). For IPython tests run via an external process the
14 prompt numbers may be different, and IPython tests run as normal python code
14 prompt numbers may be different, and IPython tests run as normal python code
15 won't even have these special _NN variables set at all.
15 won't even have these special _NN variables set at all.
16 """
16 """
17
17
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Module imports
20 # Module imports
21
21
22 # From the standard library
22 # From the standard library
23 import __builtin__
23 import __builtin__
24 import commands
24 import commands
25 import doctest
25 import doctest
26 import inspect
26 import inspect
27 import logging
27 import logging
28 import os
28 import os
29 import re
29 import re
30 import sys
30 import sys
31 import traceback
31 import traceback
32 import unittest
32 import unittest
33
33
34 from inspect import getmodule
34 from inspect import getmodule
35 from StringIO import StringIO
35 from StringIO import StringIO
36
36
37 # We are overriding the default doctest runner, so we need to import a few
37 # We are overriding the default doctest runner, so we need to import a few
38 # things from doctest directly
38 # things from doctest directly
39 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
39 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
40 _unittest_reportflags, DocTestRunner,
40 _unittest_reportflags, DocTestRunner,
41 _extract_future_flags, pdb, _OutputRedirectingPdb,
41 _extract_future_flags, pdb, _OutputRedirectingPdb,
42 _exception_traceback,
42 _exception_traceback,
43 linecache)
43 linecache)
44
44
45 # Third-party modules
45 # Third-party modules
46 import nose.core
46 import nose.core
47
47
48 from nose.plugins import doctests, Plugin
48 from nose.plugins import doctests, Plugin
49 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
49 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
50
50
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52 # Module globals and other constants
52 # Module globals and other constants
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56 ###########################################################################
56 ###########################################################################
57 # *** HACK ***
57 # *** HACK ***
58 # We must start our own ipython object and heavily muck with it so that all the
58 # We must start our own ipython object and heavily muck with it so that all the
59 # modifications IPython makes to system behavior don't send the doctest
59 # modifications IPython makes to system behavior don't send the doctest
60 # machinery into a fit. This code should be considered a gross hack, but it
60 # machinery into a fit. This code should be considered a gross hack, but it
61 # gets the job done.
61 # gets the job done.
62
62
63
63
64 # Hack to modify the %run command so we can sync the user's namespace with the
64 # Hack to modify the %run command so we can sync the user's namespace with the
65 # test globals. Once we move over to a clean magic system, this will be done
65 # test globals. Once we move over to a clean magic system, this will be done
66 # with much less ugliness.
66 # with much less ugliness.
67
67
68 class py_file_finder(object):
68 class py_file_finder(object):
69 def __init__(self,test_filename):
69 def __init__(self,test_filename):
70 self.test_filename = test_filename
70 self.test_filename = test_filename
71
71
72 def __call__(self,name):
72 def __call__(self,name):
73 from IPython.genutils import get_py_filename
73 from IPython.genutils import get_py_filename
74 try:
74 try:
75 get_py_filename(name)
75 get_py_filename(name)
76 except IOError:
76 except IOError:
77 test_dir = os.path.dirname(self.test_filename)
77 test_dir = os.path.dirname(self.test_filename)
78 new_path = os.path.join(test_dir,name)
78 new_path = os.path.join(test_dir,name)
79 return get_py_filename(new_path)
79 return get_py_filename(new_path)
80
80
81
81
82 def _run_ns_sync(self,arg_s,runner=None):
82 def _run_ns_sync(self,arg_s,runner=None):
83 """Modified version of %run that syncs testing namespaces.
83 """Modified version of %run that syncs testing namespaces.
84
84
85 This is strictly needed for running doctests that call %run.
85 This is strictly needed for running doctests that call %run.
86 """
86 """
87
87
88 finder = py_file_finder(_run_ns_sync.test_filename)
88 finder = py_file_finder(_run_ns_sync.test_filename)
89 out = _ip.IP.magic_run_ori(arg_s,runner,finder)
89 out = _ip.IP.magic_run_ori(arg_s,runner,finder)
90 _run_ns_sync.test_globs.update(_ip.user_ns)
90 _run_ns_sync.test_globs.update(_ip.user_ns)
91 return out
91 return out
92
92
93
93
94 class ipnsdict(dict):
94 class ipnsdict(dict):
95 """A special subclass of dict for use as an IPython namespace in doctests.
95 """A special subclass of dict for use as an IPython namespace in doctests.
96
96
97 This subclass adds a simple checkpointing capability so that when testing
97 This subclass adds a simple checkpointing capability so that when testing
98 machinery clears it (we use it as the test execution context), it doesn't
98 machinery clears it (we use it as the test execution context), it doesn't
99 get completely destroyed.
99 get completely destroyed.
100 """
100 """
101
101
102 def __init__(self,*a):
102 def __init__(self,*a):
103 dict.__init__(self,*a)
103 dict.__init__(self,*a)
104 self._savedict = {}
104 self._savedict = {}
105
105
106 def clear(self):
106 def clear(self):
107 dict.clear(self)
107 dict.clear(self)
108 self.update(self._savedict)
108 self.update(self._savedict)
109
109
110 def _checkpoint(self):
110 def _checkpoint(self):
111 self._savedict.clear()
111 self._savedict.clear()
112 self._savedict.update(self)
112 self._savedict.update(self)
113
113
114 def update(self,other):
114 def update(self,other):
115 self._checkpoint()
115 self._checkpoint()
116 dict.update(self,other)
116 dict.update(self,other)
117 # If '_' is in the namespace, python won't set it when executing code,
117 # If '_' is in the namespace, python won't set it when executing code,
118 # and we have examples that test it. So we ensure that the namespace
118 # and we have examples that test it. So we ensure that the namespace
119 # is always 'clean' of it before it's used for test code execution.
119 # is always 'clean' of it before it's used for test code execution.
120 self.pop('_',None)
120 self.pop('_',None)
121
121
122
122
123 def start_ipython():
123 def start_ipython():
124 """Start a global IPython shell, which we need for IPython-specific syntax.
124 """Start a global IPython shell, which we need for IPython-specific syntax.
125 """
125 """
126 import new
126 import new
127
127
128 import IPython
128 import IPython
129
129
130 def xsys(cmd):
130 def xsys(cmd):
131 """Execute a command and print its output.
131 """Execute a command and print its output.
132
132
133 This is just a convenience function to replace the IPython system call
133 This is just a convenience function to replace the IPython system call
134 with one that is more doctest-friendly.
134 with one that is more doctest-friendly.
135 """
135 """
136 cmd = _ip.IP.var_expand(cmd,depth=1)
136 cmd = _ip.IP.var_expand(cmd,depth=1)
137 sys.stdout.write(commands.getoutput(cmd))
137 sys.stdout.write(commands.getoutput(cmd))
138 sys.stdout.flush()
138 sys.stdout.flush()
139
139
140 # Store certain global objects that IPython modifies
140 # Store certain global objects that IPython modifies
141 _displayhook = sys.displayhook
141 _displayhook = sys.displayhook
142 _excepthook = sys.excepthook
142 _excepthook = sys.excepthook
143 _main = sys.modules.get('__main__')
143 _main = sys.modules.get('__main__')
144
144
145 # Start IPython instance. We customize it to start with minimal frills.
145 # Start IPython instance. We customize it to start with minimal frills.
146 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
146 user_ns,global_ns = IPython.ipapi.make_user_namespaces(ipnsdict(),dict())
147 IPython.Shell.IPShell(['--colors=NoColor','--noterm_title'],
147 IPython.Shell.IPShell(['--colors=NoColor','--noterm_title'],
148 user_ns,global_ns)
148 user_ns,global_ns)
149
149
150 # Deactivate the various python system hooks added by ipython for
150 # Deactivate the various python system hooks added by ipython for
151 # interactive convenience so we don't confuse the doctest system
151 # interactive convenience so we don't confuse the doctest system
152 sys.modules['__main__'] = _main
152 sys.modules['__main__'] = _main
153 sys.displayhook = _displayhook
153 sys.displayhook = _displayhook
154 sys.excepthook = _excepthook
154 sys.excepthook = _excepthook
155
155
156 # So that ipython magics and aliases can be doctested (they work by making
156 # So that ipython magics and aliases can be doctested (they work by making
157 # a call into a global _ip object)
157 # a call into a global _ip object)
158 _ip = IPython.ipapi.get()
158 _ip = IPython.ipapi.get()
159 __builtin__._ip = _ip
159 __builtin__._ip = _ip
160
160
161 # Modify the IPython system call with one that uses getoutput, so that we
161 # Modify the IPython system call with one that uses getoutput, so that we
162 # can capture subcommands and print them to Python's stdout, otherwise the
162 # can capture subcommands and print them to Python's stdout, otherwise the
163 # doctest machinery would miss them.
163 # doctest machinery would miss them.
164 _ip.system = xsys
164 _ip.system = xsys
165
165
166 # Also patch our %run function in.
166 # Also patch our %run function in.
167 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
167 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
168 _ip.IP.magic_run_ori = _ip.IP.magic_run
168 _ip.IP.magic_run_ori = _ip.IP.magic_run
169 _ip.IP.magic_run = im
169 _ip.IP.magic_run = im
170
170
171 # The start call MUST be made here. I'm not sure yet why it doesn't work if
171 # The start call MUST be made here. I'm not sure yet why it doesn't work if
172 # it is made later, at plugin initialization time, but in all my tests, that's
172 # it is made later, at plugin initialization time, but in all my tests, that's
173 # the case.
173 # the case.
174 start_ipython()
174 start_ipython()
175
175
176 # *** END HACK ***
176 # *** END HACK ***
177 ###########################################################################
177 ###########################################################################
178
178
179 # Classes and functions
179 # Classes and functions
180
180
181 def is_extension_module(filename):
181 def is_extension_module(filename):
182 """Return whether the given filename is an extension module.
182 """Return whether the given filename is an extension module.
183
183
184 This simply checks that the extension is either .so or .pyd.
184 This simply checks that the extension is either .so or .pyd.
185 """
185 """
186 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
186 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
187
187
188
188
189 class DocTestSkip(object):
189 class DocTestSkip(object):
190 """Object wrapper for doctests to be skipped."""
190 """Object wrapper for doctests to be skipped."""
191
191
192 ds_skip = """Doctest to skip.
192 ds_skip = """Doctest to skip.
193 >>> 1 #doctest: +SKIP
193 >>> 1 #doctest: +SKIP
194 """
194 """
195
195
196 def __init__(self,obj):
196 def __init__(self,obj):
197 self.obj = obj
197 self.obj = obj
198
198
199 def __getattribute__(self,key):
199 def __getattribute__(self,key):
200 if key == '__doc__':
200 if key == '__doc__':
201 return DocTestSkip.ds_skip
201 return DocTestSkip.ds_skip
202 else:
202 else:
203 return getattr(object.__getattribute__(self,'obj'),key)
203 return getattr(object.__getattribute__(self,'obj'),key)
204
204
205 # Modified version of the one in the stdlib, that fixes a python bug (doctests
205 # Modified version of the one in the stdlib, that fixes a python bug (doctests
206 # not found in extension modules, http://bugs.python.org/issue3158)
206 # not found in extension modules, http://bugs.python.org/issue3158)
207 class DocTestFinder(doctest.DocTestFinder):
207 class DocTestFinder(doctest.DocTestFinder):
208
208
209 def _from_module(self, module, object):
209 def _from_module(self, module, object):
210 """
210 """
211 Return true if the given object is defined in the given
211 Return true if the given object is defined in the given
212 module.
212 module.
213 """
213 """
214 if module is None:
214 if module is None:
215 return True
215 return True
216 elif inspect.isfunction(object):
216 elif inspect.isfunction(object):
217 return module.__dict__ is object.func_globals
217 return module.__dict__ is object.func_globals
218 elif inspect.isbuiltin(object):
218 elif inspect.isbuiltin(object):
219 return module.__name__ == object.__module__
219 return module.__name__ == object.__module__
220 elif inspect.isclass(object):
220 elif inspect.isclass(object):
221 return module.__name__ == object.__module__
221 return module.__name__ == object.__module__
222 elif inspect.ismethod(object):
222 elif inspect.ismethod(object):
223 # This one may be a bug in cython that fails to correctly set the
223 # This one may be a bug in cython that fails to correctly set the
224 # __module__ attribute of methods, but since the same error is easy
224 # __module__ attribute of methods, but since the same error is easy
225 # to make by extension code writers, having this safety in place
225 # to make by extension code writers, having this safety in place
226 # isn't such a bad idea
226 # isn't such a bad idea
227 return module.__name__ == object.im_class.__module__
227 return module.__name__ == object.im_class.__module__
228 elif inspect.getmodule(object) is not None:
228 elif inspect.getmodule(object) is not None:
229 return module is inspect.getmodule(object)
229 return module is inspect.getmodule(object)
230 elif hasattr(object, '__module__'):
230 elif hasattr(object, '__module__'):
231 return module.__name__ == object.__module__
231 return module.__name__ == object.__module__
232 elif isinstance(object, property):
232 elif isinstance(object, property):
233 return True # [XX] no way not be sure.
233 return True # [XX] no way not be sure.
234 else:
234 else:
235 raise ValueError("object must be a class or function")
235 raise ValueError("object must be a class or function")
236
236
237 def _find(self, tests, obj, name, module, source_lines, globs, seen):
237 def _find(self, tests, obj, name, module, source_lines, globs, seen):
238 """
238 """
239 Find tests for the given object and any contained objects, and
239 Find tests for the given object and any contained objects, and
240 add them to `tests`.
240 add them to `tests`.
241 """
241 """
242
242
243 if hasattr(obj,"skip_doctest"):
243 if hasattr(obj,"skip_doctest"):
244 #print 'SKIPPING DOCTEST FOR:',obj # dbg
244 #print 'SKIPPING DOCTEST FOR:',obj # dbg
245 obj = DocTestSkip(obj)
245 obj = DocTestSkip(obj)
246
246
247 doctest.DocTestFinder._find(self,tests, obj, name, module,
247 doctest.DocTestFinder._find(self,tests, obj, name, module,
248 source_lines, globs, seen)
248 source_lines, globs, seen)
249
249
250 # Below we re-run pieces of the above method with manual modifications,
250 # Below we re-run pieces of the above method with manual modifications,
251 # because the original code is buggy and fails to correctly identify
251 # because the original code is buggy and fails to correctly identify
252 # doctests in extension modules.
252 # doctests in extension modules.
253
253
254 # Local shorthands
254 # Local shorthands
255 from inspect import isroutine, isclass, ismodule
255 from inspect import isroutine, isclass, ismodule
256
256
257 # Look for tests in a module's contained objects.
257 # Look for tests in a module's contained objects.
258 if inspect.ismodule(obj) and self._recurse:
258 if inspect.ismodule(obj) and self._recurse:
259 for valname, val in obj.__dict__.items():
259 for valname, val in obj.__dict__.items():
260 valname1 = '%s.%s' % (name, valname)
260 valname1 = '%s.%s' % (name, valname)
261 if ( (isroutine(val) or isclass(val))
261 if ( (isroutine(val) or isclass(val))
262 and self._from_module(module, val) ):
262 and self._from_module(module, val) ):
263
263
264 self._find(tests, val, valname1, module, source_lines,
264 self._find(tests, val, valname1, module, source_lines,
265 globs, seen)
265 globs, seen)
266
266
267 # Look for tests in a class's contained objects.
267 # Look for tests in a class's contained objects.
268 if inspect.isclass(obj) and self._recurse:
268 if inspect.isclass(obj) and self._recurse:
269 #print 'RECURSE into class:',obj # dbg
269 #print 'RECURSE into class:',obj # dbg
270 for valname, val in obj.__dict__.items():
270 for valname, val in obj.__dict__.items():
271 # Special handling for staticmethod/classmethod.
271 # Special handling for staticmethod/classmethod.
272 if isinstance(val, staticmethod):
272 if isinstance(val, staticmethod):
273 val = getattr(obj, valname)
273 val = getattr(obj, valname)
274 if isinstance(val, classmethod):
274 if isinstance(val, classmethod):
275 val = getattr(obj, valname).im_func
275 val = getattr(obj, valname).im_func
276
276
277 # Recurse to methods, properties, and nested classes.
277 # Recurse to methods, properties, and nested classes.
278 if ((inspect.isfunction(val) or inspect.isclass(val) or
278 if ((inspect.isfunction(val) or inspect.isclass(val) or
279 inspect.ismethod(val) or
279 inspect.ismethod(val) or
280 isinstance(val, property)) and
280 isinstance(val, property)) and
281 self._from_module(module, val)):
281 self._from_module(module, val)):
282 valname = '%s.%s' % (name, valname)
282 valname = '%s.%s' % (name, valname)
283 self._find(tests, val, valname, module, source_lines,
283 self._find(tests, val, valname, module, source_lines,
284 globs, seen)
284 globs, seen)
285
285
286
286
287 class IPDoctestOutputChecker(doctest.OutputChecker):
287 class IPDoctestOutputChecker(doctest.OutputChecker):
288 """Second-chance checker with support for random tests.
288 """Second-chance checker with support for random tests.
289
289
290 If the default comparison doesn't pass, this checker looks in the expected
290 If the default comparison doesn't pass, this checker looks in the expected
291 output string for flags that tell us to ignore the output.
291 output string for flags that tell us to ignore the output.
292 """
292 """
293
293
294 random_re = re.compile(r'#\s*random\s+')
294 random_re = re.compile(r'#\s*random\s+')
295
295
296 def check_output(self, want, got, optionflags):
296 def check_output(self, want, got, optionflags):
297 """Check output, accepting special markers embedded in the output.
297 """Check output, accepting special markers embedded in the output.
298
298
299 If the output didn't pass the default validation but the special string
299 If the output didn't pass the default validation but the special string
300 '#random' is included, we accept it."""
300 '#random' is included, we accept it."""
301
301
302 # Let the original tester verify first, in case people have valid tests
302 # Let the original tester verify first, in case people have valid tests
303 # that happen to have a comment saying '#random' embedded in.
303 # that happen to have a comment saying '#random' embedded in.
304 ret = doctest.OutputChecker.check_output(self, want, got,
304 ret = doctest.OutputChecker.check_output(self, want, got,
305 optionflags)
305 optionflags)
306 if not ret and self.random_re.search(want):
306 if not ret and self.random_re.search(want):
307 #print >> sys.stderr, 'RANDOM OK:',want # dbg
307 #print >> sys.stderr, 'RANDOM OK:',want # dbg
308 return True
308 return True
309
309
310 return ret
310 return ret
311
311
312
312
313 class DocTestCase(doctests.DocTestCase):
313 class DocTestCase(doctests.DocTestCase):
314 """Proxy for DocTestCase: provides an address() method that
314 """Proxy for DocTestCase: provides an address() method that
315 returns the correct address for the doctest case. Otherwise
315 returns the correct address for the doctest case. Otherwise
316 acts as a proxy to the test case. To provide hints for address(),
316 acts as a proxy to the test case. To provide hints for address(),
317 an obj may also be passed -- this will be used as the test object
317 an obj may also be passed -- this will be used as the test object
318 for purposes of determining the test address, if it is provided.
318 for purposes of determining the test address, if it is provided.
319 """
319 """
320
320
321 # Note: this method was taken from numpy's nosetester module.
321 # Note: this method was taken from numpy's nosetester module.
322
322
323 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
323 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
324 # its constructor that blocks non-default arguments from being passed
324 # its constructor that blocks non-default arguments from being passed
325 # down into doctest.DocTestCase
325 # down into doctest.DocTestCase
326
326
327 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
327 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
328 checker=None, obj=None, result_var='_'):
328 checker=None, obj=None, result_var='_'):
329 self._result_var = result_var
329 self._result_var = result_var
330 doctests.DocTestCase.__init__(self, test,
330 doctests.DocTestCase.__init__(self, test,
331 optionflags=optionflags,
331 optionflags=optionflags,
332 setUp=setUp, tearDown=tearDown,
332 setUp=setUp, tearDown=tearDown,
333 checker=checker)
333 checker=checker)
334 # Now we must actually copy the original constructor from the stdlib
334 # Now we must actually copy the original constructor from the stdlib
335 # doctest class, because we can't call it directly and a bug in nose
335 # doctest class, because we can't call it directly and a bug in nose
336 # means it never gets passed the right arguments.
336 # means it never gets passed the right arguments.
337
337
338 self._dt_optionflags = optionflags
338 self._dt_optionflags = optionflags
339 self._dt_checker = checker
339 self._dt_checker = checker
340 self._dt_test = test
340 self._dt_test = test
341 self._dt_setUp = setUp
341 self._dt_setUp = setUp
342 self._dt_tearDown = tearDown
342 self._dt_tearDown = tearDown
343
343
344 # XXX - store this runner once in the object!
344 # XXX - store this runner once in the object!
345 runner = IPDocTestRunner(optionflags=optionflags,
345 runner = IPDocTestRunner(optionflags=optionflags,
346 checker=checker, verbose=False)
346 checker=checker, verbose=False)
347 self._dt_runner = runner
347 self._dt_runner = runner
348
348
349
349
350 # Each doctest should remember what directory it was loaded from...
350 # Each doctest should remember what directory it was loaded from...
351 self._ori_dir = os.getcwd()
351 self._ori_dir = os.getcwd()
352
352
353 # Modified runTest from the default stdlib
353 # Modified runTest from the default stdlib
354 def runTest(self):
354 def runTest(self):
355 test = self._dt_test
355 test = self._dt_test
356 runner = self._dt_runner
356 runner = self._dt_runner
357
357
358 old = sys.stdout
358 old = sys.stdout
359 new = StringIO()
359 new = StringIO()
360 optionflags = self._dt_optionflags
360 optionflags = self._dt_optionflags
361
361
362 if not (optionflags & REPORTING_FLAGS):
362 if not (optionflags & REPORTING_FLAGS):
363 # The option flags don't include any reporting flags,
363 # The option flags don't include any reporting flags,
364 # so add the default reporting flags
364 # so add the default reporting flags
365 optionflags |= _unittest_reportflags
365 optionflags |= _unittest_reportflags
366
366
367 try:
367 try:
368 # Save our current directory and switch out to the one where the
368 # Save our current directory and switch out to the one where the
369 # test was originally created, in case another doctest did a
369 # test was originally created, in case another doctest did a
370 # directory change. We'll restore this in the finally clause.
370 # directory change. We'll restore this in the finally clause.
371 curdir = os.getcwd()
371 curdir = os.getcwd()
372 os.chdir(self._ori_dir)
372 os.chdir(self._ori_dir)
373
373
374 runner.DIVIDER = "-"*70
374 runner.DIVIDER = "-"*70
375 failures, tries = runner.run(test,out=new.write,
375 failures, tries = runner.run(test,out=new.write,
376 clear_globs=False)
376 clear_globs=False)
377 finally:
377 finally:
378 sys.stdout = old
378 sys.stdout = old
379 os.chdir(curdir)
379 os.chdir(curdir)
380
380
381 if failures:
381 if failures:
382 raise self.failureException(self.format_failure(new.getvalue()))
382 raise self.failureException(self.format_failure(new.getvalue()))
383
383
384 def setUp(self):
384 def setUp(self):
385 """Modified test setup that syncs with ipython namespace"""
385 """Modified test setup that syncs with ipython namespace"""
386
386
387 if isinstance(self._dt_test.examples[0],IPExample):
387 if isinstance(self._dt_test.examples[0],IPExample):
388 # for IPython examples *only*, we swap the globals with the ipython
388 # for IPython examples *only*, we swap the globals with the ipython
389 # namespace, after updating it with the globals (which doctest
389 # namespace, after updating it with the globals (which doctest
390 # fills with the necessary info from the module being tested).
390 # fills with the necessary info from the module being tested).
391 _ip.IP.user_ns.update(self._dt_test.globs)
391 _ip.IP.user_ns.update(self._dt_test.globs)
392 self._dt_test.globs = _ip.IP.user_ns
392 self._dt_test.globs = _ip.IP.user_ns
393
393
394 doctests.DocTestCase.setUp(self)
394 doctests.DocTestCase.setUp(self)
395
395
396
396
397
397
398 # A simple subclassing of the original with a different class name, so we can
398 # A simple subclassing of the original with a different class name, so we can
399 # distinguish and treat differently IPython examples from pure python ones.
399 # distinguish and treat differently IPython examples from pure python ones.
400 class IPExample(doctest.Example): pass
400 class IPExample(doctest.Example): pass
401
401
402
402
403 class IPExternalExample(doctest.Example):
403 class IPExternalExample(doctest.Example):
404 """Doctest examples to be run in an external process."""
404 """Doctest examples to be run in an external process."""
405
405
406 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
406 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
407 options=None):
407 options=None):
408 # Parent constructor
408 # Parent constructor
409 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
409 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
410
410
411 # An EXTRA newline is needed to prevent pexpect hangs
411 # An EXTRA newline is needed to prevent pexpect hangs
412 self.source += '\n'
412 self.source += '\n'
413
413
414
414
415 class IPDocTestParser(doctest.DocTestParser):
415 class IPDocTestParser(doctest.DocTestParser):
416 """
416 """
417 A class used to parse strings containing doctest examples.
417 A class used to parse strings containing doctest examples.
418
418
419 Note: This is a version modified to properly recognize IPython input and
419 Note: This is a version modified to properly recognize IPython input and
420 convert any IPython examples into valid Python ones.
420 convert any IPython examples into valid Python ones.
421 """
421 """
422 # This regular expression is used to find doctest examples in a
422 # This regular expression is used to find doctest examples in a
423 # string. It defines three groups: `source` is the source code
423 # string. It defines three groups: `source` is the source code
424 # (including leading indentation and prompts); `indent` is the
424 # (including leading indentation and prompts); `indent` is the
425 # indentation of the first (PS1) line of the source code; and
425 # indentation of the first (PS1) line of the source code; and
426 # `want` is the expected output (including leading indentation).
426 # `want` is the expected output (including leading indentation).
427
427
428 # Classic Python prompts or default IPython ones
428 # Classic Python prompts or default IPython ones
429 _PS1_PY = r'>>>'
429 _PS1_PY = r'>>>'
430 _PS2_PY = r'\.\.\.'
430 _PS2_PY = r'\.\.\.'
431
431
432 _PS1_IP = r'In\ \[\d+\]:'
432 _PS1_IP = r'In\ \[\d+\]:'
433 _PS2_IP = r'\ \ \ \.\.\.+:'
433 _PS2_IP = r'\ \ \ \.\.\.+:'
434
434
435 _RE_TPL = r'''
435 _RE_TPL = r'''
436 # Source consists of a PS1 line followed by zero or more PS2 lines.
436 # Source consists of a PS1 line followed by zero or more PS2 lines.
437 (?P<source>
437 (?P<source>
438 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
438 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
439 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
439 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
440 \n? # a newline
440 \n? # a newline
441 # Want consists of any non-blank lines that do not start with PS1.
441 # Want consists of any non-blank lines that do not start with PS1.
442 (?P<want> (?:(?![ ]*$) # Not a blank line
442 (?P<want> (?:(?![ ]*$) # Not a blank line
443 (?![ ]*%s) # Not a line starting with PS1
443 (?![ ]*%s) # Not a line starting with PS1
444 (?![ ]*%s) # Not a line starting with PS2
444 (?![ ]*%s) # Not a line starting with PS2
445 .*$\n? # But any other line
445 .*$\n? # But any other line
446 )*)
446 )*)
447 '''
447 '''
448
448
449 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
449 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
450 re.MULTILINE | re.VERBOSE)
450 re.MULTILINE | re.VERBOSE)
451
451
452 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
452 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
453 re.MULTILINE | re.VERBOSE)
453 re.MULTILINE | re.VERBOSE)
454
454
455 # Mark a test as being fully random. In this case, we simply append the
455 # Mark a test as being fully random. In this case, we simply append the
456 # random marker ('#random') to each individual example's output. This way
456 # random marker ('#random') to each individual example's output. This way
457 # we don't need to modify any other code.
457 # we don't need to modify any other code.
458 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
458 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
459
459
460 # Mark tests to be executed in an external process - currently unsupported.
460 # Mark tests to be executed in an external process - currently unsupported.
461 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
461 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
462
462
463 def ip2py(self,source):
463 def ip2py(self,source):
464 """Convert input IPython source into valid Python."""
464 """Convert input IPython source into valid Python."""
465 out = []
465 out = []
466 newline = out.append
466 newline = out.append
467 for lnum,line in enumerate(source.splitlines()):
467 for lnum,line in enumerate(source.splitlines()):
468 newline(_ip.IP.prefilter(line,lnum>0))
468 newline(_ip.IP.prefilter(line,lnum>0))
469 newline('') # ensure a closing newline, needed by doctest
469 newline('') # ensure a closing newline, needed by doctest
470 #print "PYSRC:", '\n'.join(out) # dbg
470 #print "PYSRC:", '\n'.join(out) # dbg
471 return '\n'.join(out)
471 return '\n'.join(out)
472
472
473 def parse(self, string, name='<string>'):
473 def parse(self, string, name='<string>'):
474 """
474 """
475 Divide the given string into examples and intervening text,
475 Divide the given string into examples and intervening text,
476 and return them as a list of alternating Examples and strings.
476 and return them as a list of alternating Examples and strings.
477 Line numbers for the Examples are 0-based. The optional
477 Line numbers for the Examples are 0-based. The optional
478 argument `name` is a name identifying this string, and is only
478 argument `name` is a name identifying this string, and is only
479 used for error messages.
479 used for error messages.
480 """
480 """
481
481
482 #print 'Parse string:\n',string # dbg
482 #print 'Parse string:\n',string # dbg
483
483
484 string = string.expandtabs()
484 string = string.expandtabs()
485 # If all lines begin with the same indentation, then strip it.
485 # If all lines begin with the same indentation, then strip it.
486 min_indent = self._min_indent(string)
486 min_indent = self._min_indent(string)
487 if min_indent > 0:
487 if min_indent > 0:
488 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
488 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
489
489
490 output = []
490 output = []
491 charno, lineno = 0, 0
491 charno, lineno = 0, 0
492
492
493 # We make 'all random' tests by adding the '# random' mark to every
493 # We make 'all random' tests by adding the '# random' mark to every
494 # block of output in the test.
494 # block of output in the test.
495 if self._RANDOM_TEST.search(string):
495 if self._RANDOM_TEST.search(string):
496 random_marker = '\n# random'
496 random_marker = '\n# random'
497 else:
497 else:
498 random_marker = ''
498 random_marker = ''
499
499
500 # Whether to convert the input from ipython to python syntax
500 # Whether to convert the input from ipython to python syntax
501 ip2py = False
501 ip2py = False
502 # Find all doctest examples in the string. First, try them as Python
502 # Find all doctest examples in the string. First, try them as Python
503 # examples, then as IPython ones
503 # examples, then as IPython ones
504 terms = list(self._EXAMPLE_RE_PY.finditer(string))
504 terms = list(self._EXAMPLE_RE_PY.finditer(string))
505 if terms:
505 if terms:
506 # Normal Python example
506 # Normal Python example
507 #print '-'*70 # dbg
507 #print '-'*70 # dbg
508 #print 'PyExample, Source:\n',string # dbg
508 #print 'PyExample, Source:\n',string # dbg
509 #print '-'*70 # dbg
509 #print '-'*70 # dbg
510 Example = doctest.Example
510 Example = doctest.Example
511 else:
511 else:
512 # It's an ipython example. Note that IPExamples are run
512 # It's an ipython example. Note that IPExamples are run
513 # in-process, so their syntax must be turned into valid python.
513 # in-process, so their syntax must be turned into valid python.
514 # IPExternalExamples are run out-of-process (via pexpect) so they
514 # IPExternalExamples are run out-of-process (via pexpect) so they
515 # don't need any filtering (a real ipython will be executing them).
515 # don't need any filtering (a real ipython will be executing them).
516 terms = list(self._EXAMPLE_RE_IP.finditer(string))
516 terms = list(self._EXAMPLE_RE_IP.finditer(string))
517 if self._EXTERNAL_IP.search(string):
517 if self._EXTERNAL_IP.search(string):
518 #print '-'*70 # dbg
518 #print '-'*70 # dbg
519 #print 'IPExternalExample, Source:\n',string # dbg
519 #print 'IPExternalExample, Source:\n',string # dbg
520 #print '-'*70 # dbg
520 #print '-'*70 # dbg
521 Example = IPExternalExample
521 Example = IPExternalExample
522 else:
522 else:
523 #print '-'*70 # dbg
523 #print '-'*70 # dbg
524 #print 'IPExample, Source:\n',string # dbg
524 #print 'IPExample, Source:\n',string # dbg
525 #print '-'*70 # dbg
525 #print '-'*70 # dbg
526 Example = IPExample
526 Example = IPExample
527 ip2py = True
527 ip2py = True
528
528
529 for m in terms:
529 for m in terms:
530 # Add the pre-example text to `output`.
530 # Add the pre-example text to `output`.
531 output.append(string[charno:m.start()])
531 output.append(string[charno:m.start()])
532 # Update lineno (lines before this example)
532 # Update lineno (lines before this example)
533 lineno += string.count('\n', charno, m.start())
533 lineno += string.count('\n', charno, m.start())
534 # Extract info from the regexp match.
534 # Extract info from the regexp match.
535 (source, options, want, exc_msg) = \
535 (source, options, want, exc_msg) = \
536 self._parse_example(m, name, lineno,ip2py)
536 self._parse_example(m, name, lineno,ip2py)
537
537
538 # Append the random-output marker (it defaults to empty in most
538 # Append the random-output marker (it defaults to empty in most
539 # cases, it's only non-empty for 'all-random' tests):
539 # cases, it's only non-empty for 'all-random' tests):
540 want += random_marker
540 want += random_marker
541
541
542 if Example is IPExternalExample:
542 if Example is IPExternalExample:
543 options[doctest.NORMALIZE_WHITESPACE] = True
543 options[doctest.NORMALIZE_WHITESPACE] = True
544 want += '\n'
544 want += '\n'
545
545
546 # Create an Example, and add it to the list.
546 # Create an Example, and add it to the list.
547 if not self._IS_BLANK_OR_COMMENT(source):
547 if not self._IS_BLANK_OR_COMMENT(source):
548 output.append(Example(source, want, exc_msg,
548 output.append(Example(source, want, exc_msg,
549 lineno=lineno,
549 lineno=lineno,
550 indent=min_indent+len(m.group('indent')),
550 indent=min_indent+len(m.group('indent')),
551 options=options))
551 options=options))
552 # Update lineno (lines inside this example)
552 # Update lineno (lines inside this example)
553 lineno += string.count('\n', m.start(), m.end())
553 lineno += string.count('\n', m.start(), m.end())
554 # Update charno.
554 # Update charno.
555 charno = m.end()
555 charno = m.end()
556 # Add any remaining post-example text to `output`.
556 # Add any remaining post-example text to `output`.
557 output.append(string[charno:])
557 output.append(string[charno:])
558 return output
558 return output
559
559
560 def _parse_example(self, m, name, lineno,ip2py=False):
560 def _parse_example(self, m, name, lineno,ip2py=False):
561 """
561 """
562 Given a regular expression match from `_EXAMPLE_RE` (`m`),
562 Given a regular expression match from `_EXAMPLE_RE` (`m`),
563 return a pair `(source, want)`, where `source` is the matched
563 return a pair `(source, want)`, where `source` is the matched
564 example's source code (with prompts and indentation stripped);
564 example's source code (with prompts and indentation stripped);
565 and `want` is the example's expected output (with indentation
565 and `want` is the example's expected output (with indentation
566 stripped).
566 stripped).
567
567
568 `name` is the string's name, and `lineno` is the line number
568 `name` is the string's name, and `lineno` is the line number
569 where the example starts; both are used for error messages.
569 where the example starts; both are used for error messages.
570
570
571 Optional:
571 Optional:
572 `ip2py`: if true, filter the input via IPython to convert the syntax
572 `ip2py`: if true, filter the input via IPython to convert the syntax
573 into valid python.
573 into valid python.
574 """
574 """
575
575
576 # Get the example's indentation level.
576 # Get the example's indentation level.
577 indent = len(m.group('indent'))
577 indent = len(m.group('indent'))
578
578
579 # Divide source into lines; check that they're properly
579 # Divide source into lines; check that they're properly
580 # indented; and then strip their indentation & prompts.
580 # indented; and then strip their indentation & prompts.
581 source_lines = m.group('source').split('\n')
581 source_lines = m.group('source').split('\n')
582
582
583 # We're using variable-length input prompts
583 # We're using variable-length input prompts
584 ps1 = m.group('ps1')
584 ps1 = m.group('ps1')
585 ps2 = m.group('ps2')
585 ps2 = m.group('ps2')
586 ps1_len = len(ps1)
586 ps1_len = len(ps1)
587
587
588 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
588 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
589 if ps2:
589 if ps2:
590 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
590 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
591
591
592 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
592 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
593
593
594 if ip2py:
594 if ip2py:
595 # Convert source input from IPython into valid Python syntax
595 # Convert source input from IPython into valid Python syntax
596 source = self.ip2py(source)
596 source = self.ip2py(source)
597
597
598 # Divide want into lines; check that it's properly indented; and
598 # Divide want into lines; check that it's properly indented; and
599 # then strip the indentation. Spaces before the last newline should
599 # then strip the indentation. Spaces before the last newline should
600 # be preserved, so plain rstrip() isn't good enough.
600 # be preserved, so plain rstrip() isn't good enough.
601 want = m.group('want')
601 want = m.group('want')
602 want_lines = want.split('\n')
602 want_lines = want.split('\n')
603 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
603 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
604 del want_lines[-1] # forget final newline & spaces after it
604 del want_lines[-1] # forget final newline & spaces after it
605 self._check_prefix(want_lines, ' '*indent, name,
605 self._check_prefix(want_lines, ' '*indent, name,
606 lineno + len(source_lines))
606 lineno + len(source_lines))
607
607
608 # Remove ipython output prompt that might be present in the first line
608 # Remove ipython output prompt that might be present in the first line
609 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
609 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
610
610
611 want = '\n'.join([wl[indent:] for wl in want_lines])
611 want = '\n'.join([wl[indent:] for wl in want_lines])
612
612
613 # If `want` contains a traceback message, then extract it.
613 # If `want` contains a traceback message, then extract it.
614 m = self._EXCEPTION_RE.match(want)
614 m = self._EXCEPTION_RE.match(want)
615 if m:
615 if m:
616 exc_msg = m.group('msg')
616 exc_msg = m.group('msg')
617 else:
617 else:
618 exc_msg = None
618 exc_msg = None
619
619
620 # Extract options from the source.
620 # Extract options from the source.
621 options = self._find_options(source, name, lineno)
621 options = self._find_options(source, name, lineno)
622
622
623 return source, options, want, exc_msg
623 return source, options, want, exc_msg
624
624
625 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
625 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
626 """
626 """
627 Given the lines of a source string (including prompts and
627 Given the lines of a source string (including prompts and
628 leading indentation), check to make sure that every prompt is
628 leading indentation), check to make sure that every prompt is
629 followed by a space character. If any line is not followed by
629 followed by a space character. If any line is not followed by
630 a space character, then raise ValueError.
630 a space character, then raise ValueError.
631
631
632 Note: IPython-modified version which takes the input prompt length as a
632 Note: IPython-modified version which takes the input prompt length as a
633 parameter, so that prompts of variable length can be dealt with.
633 parameter, so that prompts of variable length can be dealt with.
634 """
634 """
635 space_idx = indent+ps1_len
635 space_idx = indent+ps1_len
636 min_len = space_idx+1
636 min_len = space_idx+1
637 for i, line in enumerate(lines):
637 for i, line in enumerate(lines):
638 if len(line) >= min_len and line[space_idx] != ' ':
638 if len(line) >= min_len and line[space_idx] != ' ':
639 raise ValueError('line %r of the docstring for %s '
639 raise ValueError('line %r of the docstring for %s '
640 'lacks blank after %s: %r' %
640 'lacks blank after %s: %r' %
641 (lineno+i+1, name,
641 (lineno+i+1, name,
642 line[indent:space_idx], line))
642 line[indent:space_idx], line))
643
643
644
644
645 SKIP = doctest.register_optionflag('SKIP')
645 SKIP = doctest.register_optionflag('SKIP')
646
646
647
647
648 class IPDocTestRunner(doctest.DocTestRunner,object):
648 class IPDocTestRunner(doctest.DocTestRunner,object):
649 """Test runner that synchronizes the IPython namespace with test globals.
649 """Test runner that synchronizes the IPython namespace with test globals.
650 """
650 """
651
651
652 def run(self, test, compileflags=None, out=None, clear_globs=True):
652 def run(self, test, compileflags=None, out=None, clear_globs=True):
653
653
654 # Hack: ipython needs access to the execution context of the example,
654 # Hack: ipython needs access to the execution context of the example,
655 # so that it can propagate user variables loaded by %run into
655 # so that it can propagate user variables loaded by %run into
656 # test.globs. We put them here into our modified %run as a function
656 # test.globs. We put them here into our modified %run as a function
657 # attribute. Our new %run will then only make the namespace update
657 # attribute. Our new %run will then only make the namespace update
658 # when called (rather than unconconditionally updating test.globs here
658 # when called (rather than unconconditionally updating test.globs here
659 # for all examples, most of which won't be calling %run anyway).
659 # for all examples, most of which won't be calling %run anyway).
660 _run_ns_sync.test_globs = test.globs
660 _run_ns_sync.test_globs = test.globs
661 _run_ns_sync.test_filename = test.filename
661 _run_ns_sync.test_filename = test.filename
662
662
663 return super(IPDocTestRunner,self).run(test,
663 return super(IPDocTestRunner,self).run(test,
664 compileflags,out,clear_globs)
664 compileflags,out,clear_globs)
665
665
666
666
667 class DocFileCase(doctest.DocFileCase):
667 class DocFileCase(doctest.DocFileCase):
668 """Overrides to provide filename
668 """Overrides to provide filename
669 """
669 """
670 def address(self):
670 def address(self):
671 return (self._dt_test.filename, None, None)
671 return (self._dt_test.filename, None, None)
672
672
673
673
674 class ExtensionDoctest(doctests.Doctest):
674 class ExtensionDoctest(doctests.Doctest):
675 """Nose Plugin that supports doctests in extension modules.
675 """Nose Plugin that supports doctests in extension modules.
676 """
676 """
677 name = 'extdoctest' # call nosetests with --with-extdoctest
677 name = 'extdoctest' # call nosetests with --with-extdoctest
678 enabled = True
678 enabled = True
679
679
680 def __init__(self,exclude_patterns=None):
681 """Create a new ExtensionDoctest plugin.
682
683 Parameters
684 ----------
685
686 exclude_patterns : sequence of strings, optional
687 These patterns are compiled as regular expressions, subsequently used
688 to exclude any filename which matches them from inclusion in the test
689 suite (using pattern.search(), NOT pattern.match() ).
690 """
691 if exclude_patterns is None:
692 exclude_patterns = []
693 self.exclude_patterns = map(re.compile,exclude_patterns)
694 doctests.Doctest.__init__(self)
695
680 def options(self, parser, env=os.environ):
696 def options(self, parser, env=os.environ):
681 Plugin.options(self, parser, env)
697 Plugin.options(self, parser, env)
682 parser.add_option('--doctest-tests', action='store_true',
698 parser.add_option('--doctest-tests', action='store_true',
683 dest='doctest_tests',
699 dest='doctest_tests',
684 default=env.get('NOSE_DOCTEST_TESTS',True),
700 default=env.get('NOSE_DOCTEST_TESTS',True),
685 help="Also look for doctests in test modules. "
701 help="Also look for doctests in test modules. "
686 "Note that classes, methods and functions should "
702 "Note that classes, methods and functions should "
687 "have either doctests or non-doctest tests, "
703 "have either doctests or non-doctest tests, "
688 "not both. [NOSE_DOCTEST_TESTS]")
704 "not both. [NOSE_DOCTEST_TESTS]")
689 parser.add_option('--doctest-extension', action="append",
705 parser.add_option('--doctest-extension', action="append",
690 dest="doctestExtension",
706 dest="doctestExtension",
691 help="Also look for doctests in files with "
707 help="Also look for doctests in files with "
692 "this extension [NOSE_DOCTEST_EXTENSION]")
708 "this extension [NOSE_DOCTEST_EXTENSION]")
693 # Set the default as a list, if given in env; otherwise
709 # Set the default as a list, if given in env; otherwise
694 # an additional value set on the command line will cause
710 # an additional value set on the command line will cause
695 # an error.
711 # an error.
696 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
712 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
697 if env_setting is not None:
713 if env_setting is not None:
698 parser.set_defaults(doctestExtension=tolist(env_setting))
714 parser.set_defaults(doctestExtension=tolist(env_setting))
699
715
700
716
701 def configure(self, options, config):
717 def configure(self, options, config):
702 Plugin.configure(self, options, config)
718 Plugin.configure(self, options, config)
703 self.doctest_tests = options.doctest_tests
719 self.doctest_tests = options.doctest_tests
704 self.extension = tolist(options.doctestExtension)
720 self.extension = tolist(options.doctestExtension)
705
721
706 self.parser = doctest.DocTestParser()
722 self.parser = doctest.DocTestParser()
707 self.finder = DocTestFinder()
723 self.finder = DocTestFinder()
708 self.checker = IPDoctestOutputChecker()
724 self.checker = IPDoctestOutputChecker()
709 self.globs = None
725 self.globs = None
710 self.extraglobs = None
726 self.extraglobs = None
711
727
712
728
713 def loadTestsFromExtensionModule(self,filename):
729 def loadTestsFromExtensionModule(self,filename):
714 bpath,mod = os.path.split(filename)
730 bpath,mod = os.path.split(filename)
715 modname = os.path.splitext(mod)[0]
731 modname = os.path.splitext(mod)[0]
716 try:
732 try:
717 sys.path.append(bpath)
733 sys.path.append(bpath)
718 module = __import__(modname)
734 module = __import__(modname)
719 tests = list(self.loadTestsFromModule(module))
735 tests = list(self.loadTestsFromModule(module))
720 finally:
736 finally:
721 sys.path.pop()
737 sys.path.pop()
722 return tests
738 return tests
723
739
724 # NOTE: the method below is almost a copy of the original one in nose, with
740 # NOTE: the method below is almost a copy of the original one in nose, with
725 # a few modifications to control output checking.
741 # a few modifications to control output checking.
726
742
727 def loadTestsFromModule(self, module):
743 def loadTestsFromModule(self, module):
728 #print '*** ipdoctest - lTM',module # dbg
744 #print '*** ipdoctest - lTM',module # dbg
729
745
730 if not self.matches(module.__name__):
746 if not self.matches(module.__name__):
731 log.debug("Doctest doesn't want module %s", module)
747 log.debug("Doctest doesn't want module %s", module)
732 return
748 return
733
749
734 tests = self.finder.find(module,globs=self.globs,
750 tests = self.finder.find(module,globs=self.globs,
735 extraglobs=self.extraglobs)
751 extraglobs=self.extraglobs)
736 if not tests:
752 if not tests:
737 return
753 return
738
754
739 # always use whitespace and ellipsis options
755 # always use whitespace and ellipsis options
740 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
756 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
741
757
742 tests.sort()
758 tests.sort()
743 module_file = module.__file__
759 module_file = module.__file__
744 if module_file[-4:] in ('.pyc', '.pyo'):
760 if module_file[-4:] in ('.pyc', '.pyo'):
745 module_file = module_file[:-1]
761 module_file = module_file[:-1]
746 for test in tests:
762 for test in tests:
747 if not test.examples:
763 if not test.examples:
748 continue
764 continue
749 if not test.filename:
765 if not test.filename:
750 test.filename = module_file
766 test.filename = module_file
751
767
752 yield DocTestCase(test,
768 yield DocTestCase(test,
753 optionflags=optionflags,
769 optionflags=optionflags,
754 checker=self.checker)
770 checker=self.checker)
755
771
756
772
757 def loadTestsFromFile(self, filename):
773 def loadTestsFromFile(self, filename):
758 if is_extension_module(filename):
774 if is_extension_module(filename):
759 for t in self.loadTestsFromExtensionModule(filename):
775 for t in self.loadTestsFromExtensionModule(filename):
760 yield t
776 yield t
761 else:
777 else:
762 if self.extension and anyp(filename.endswith, self.extension):
778 if self.extension and anyp(filename.endswith, self.extension):
763 name = os.path.basename(filename)
779 name = os.path.basename(filename)
764 dh = open(filename)
780 dh = open(filename)
765 try:
781 try:
766 doc = dh.read()
782 doc = dh.read()
767 finally:
783 finally:
768 dh.close()
784 dh.close()
769 test = self.parser.get_doctest(
785 test = self.parser.get_doctest(
770 doc, globs={'__file__': filename}, name=name,
786 doc, globs={'__file__': filename}, name=name,
771 filename=filename, lineno=0)
787 filename=filename, lineno=0)
772 if test.examples:
788 if test.examples:
773 #print 'FileCase:',test.examples # dbg
789 #print 'FileCase:',test.examples # dbg
774 yield DocFileCase(test)
790 yield DocFileCase(test)
775 else:
791 else:
776 yield False # no tests to load
792 yield False # no tests to load
777
793
778 def wantFile(self,filename):
794 def wantFile(self,filename):
779 """Return whether the given filename should be scanned for tests.
795 """Return whether the given filename should be scanned for tests.
780
796
781 Modified version that accepts extension modules as valid containers for
797 Modified version that accepts extension modules as valid containers for
782 doctests.
798 doctests.
783 """
799 """
784 #print '*** ipdoctest- wantFile:',filename # dbg
800 #print '*** ipdoctest- wantFile:',filename # dbg
785
801
786 # XXX - temporarily hardcoded list, will move to driver later
802 for pat in self.exclude_patterns:
787 exclude = ['IPython/external/',
803 if pat.search(filename):
788 'IPython/platutils_win32',
789 'IPython/frontend/cocoa',
790 'IPython_doctest_plugin',
791 'IPython/Gnuplot',
792 'IPython/Extensions/ipy_',
793 'IPython/Extensions/PhysicalQIn',
794 'IPython/Extensions/scitedirector',
795 'IPython/testing/plugin',
796 ]
797
798 for fex in exclude:
799 if fex in filename: # substring
800 #print '###>>> SKIP:',filename # dbg
804 #print '###>>> SKIP:',filename # dbg
801 return False
805 return False
802
806
803 if is_extension_module(filename):
807 if is_extension_module(filename):
804 return True
808 return True
805 else:
809 else:
806 return doctests.Doctest.wantFile(self,filename)
810 return doctests.Doctest.wantFile(self,filename)
807
811
808
812
809 class IPythonDoctest(ExtensionDoctest):
813 class IPythonDoctest(ExtensionDoctest):
810 """Nose Plugin that supports doctests in extension modules.
814 """Nose Plugin that supports doctests in extension modules.
811 """
815 """
812 name = 'ipdoctest' # call nosetests with --with-ipdoctest
816 name = 'ipdoctest' # call nosetests with --with-ipdoctest
813 enabled = True
817 enabled = True
814
818
815 def makeTest(self, obj, parent):
819 def makeTest(self, obj, parent):
816 """Look for doctests in the given object, which will be a
820 """Look for doctests in the given object, which will be a
817 function, method or class.
821 function, method or class.
818 """
822 """
819 # always use whitespace and ellipsis options
823 # always use whitespace and ellipsis options
820 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
824 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
821
825
822 doctests = self.finder.find(obj, module=getmodule(parent))
826 doctests = self.finder.find(obj, module=getmodule(parent))
823 if doctests:
827 if doctests:
824 for test in doctests:
828 for test in doctests:
825 if len(test.examples) == 0:
829 if len(test.examples) == 0:
826 continue
830 continue
827
831
828 yield DocTestCase(test, obj=obj,
832 yield DocTestCase(test, obj=obj,
829 optionflags=optionflags,
833 optionflags=optionflags,
830 checker=self.checker)
834 checker=self.checker)
831
835
832 def configure(self, options, config):
836 def configure(self, options, config):
833
837
834 Plugin.configure(self, options, config)
838 Plugin.configure(self, options, config)
835 self.doctest_tests = options.doctest_tests
839 self.doctest_tests = options.doctest_tests
836 self.extension = tolist(options.doctestExtension)
840 self.extension = tolist(options.doctestExtension)
837
841
838 self.parser = IPDocTestParser()
842 self.parser = IPDocTestParser()
839 self.finder = DocTestFinder(parser=self.parser)
843 self.finder = DocTestFinder(parser=self.parser)
840 self.checker = IPDoctestOutputChecker()
844 self.checker = IPDoctestOutputChecker()
841 self.globs = None
845 self.globs = None
842 self.extraglobs = None
846 self.extraglobs = None
843
847
General Comments 0
You need to be logged in to leave comments. Login now