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