##// END OF EJS Templates
Update nose fix to avoid a python 2.6 deprecation warning on exc.message
Fernando Perez -
Show More
@@ -1,939 +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 super(DocTestCase, self).setUp()
442 super(DocTestCase, self).setUp()
443
443
444 def tearDown(self):
444 def tearDown(self):
445 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
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
446 # it does look like one to me: its tearDown method tries to run
447 #
447 #
448 # delattr(__builtin__, self._result_var)
448 # delattr(__builtin__, self._result_var)
449 #
449 #
450 # without checking that the attribute really is there; it implicitly
450 # without checking that the attribute really is there; it implicitly
451 # assumes it should have been set via displayhook. But if the
451 # assumes it should have been set via displayhook. But if the
452 # displayhook was never called, this doesn't necessarily happen. I
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
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
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.
455 # team, but it does happen a lot in our code.
456 #
456 #
457 # So here, we just protect as narrowly as possible by trapping an
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,
458 # attribute error whose message would be the name of self._result_var,
459 # and letting any other error propagate.
459 # and letting any other error propagate.
460 try:
460 try:
461 super(DocTestCase, self).tearDown()
461 super(DocTestCase, self).tearDown()
462 except AttributeError, exc:
462 except AttributeError, exc:
463 if exc.message != self._result_var:
463 if exc.args[0] != self._result_var:
464 raise
464 raise
465
465
466
466
467 # 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
468 # distinguish and treat differently IPython examples from pure python ones.
468 # distinguish and treat differently IPython examples from pure python ones.
469 class IPExample(doctest.Example): pass
469 class IPExample(doctest.Example): pass
470
470
471
471
472 class IPExternalExample(doctest.Example):
472 class IPExternalExample(doctest.Example):
473 """Doctest examples to be run in an external process."""
473 """Doctest examples to be run in an external process."""
474
474
475 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,
476 options=None):
476 options=None):
477 # Parent constructor
477 # Parent constructor
478 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
478 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
479
479
480 # An EXTRA newline is needed to prevent pexpect hangs
480 # An EXTRA newline is needed to prevent pexpect hangs
481 self.source += '\n'
481 self.source += '\n'
482
482
483
483
484 class IPDocTestParser(doctest.DocTestParser):
484 class IPDocTestParser(doctest.DocTestParser):
485 """
485 """
486 A class used to parse strings containing doctest examples.
486 A class used to parse strings containing doctest examples.
487
487
488 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
489 convert any IPython examples into valid Python ones.
489 convert any IPython examples into valid Python ones.
490 """
490 """
491 # This regular expression is used to find doctest examples in a
491 # This regular expression is used to find doctest examples in a
492 # string. It defines three groups: `source` is the source code
492 # string. It defines three groups: `source` is the source code
493 # (including leading indentation and prompts); `indent` is the
493 # (including leading indentation and prompts); `indent` is the
494 # indentation of the first (PS1) line of the source code; and
494 # indentation of the first (PS1) line of the source code; and
495 # `want` is the expected output (including leading indentation).
495 # `want` is the expected output (including leading indentation).
496
496
497 # Classic Python prompts or default IPython ones
497 # Classic Python prompts or default IPython ones
498 _PS1_PY = r'>>>'
498 _PS1_PY = r'>>>'
499 _PS2_PY = r'\.\.\.'
499 _PS2_PY = r'\.\.\.'
500
500
501 _PS1_IP = r'In\ \[\d+\]:'
501 _PS1_IP = r'In\ \[\d+\]:'
502 _PS2_IP = r'\ \ \ \.\.\.+:'
502 _PS2_IP = r'\ \ \ \.\.\.+:'
503
503
504 _RE_TPL = r'''
504 _RE_TPL = r'''
505 # 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.
506 (?P<source>
506 (?P<source>
507 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
507 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
508 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
508 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
509 \n? # a newline
509 \n? # a newline
510 # 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.
511 (?P<want> (?:(?![ ]*$) # Not a blank line
511 (?P<want> (?:(?![ ]*$) # Not a blank line
512 (?![ ]*%s) # Not a line starting with PS1
512 (?![ ]*%s) # Not a line starting with PS1
513 (?![ ]*%s) # Not a line starting with PS2
513 (?![ ]*%s) # Not a line starting with PS2
514 .*$\n? # But any other line
514 .*$\n? # But any other line
515 )*)
515 )*)
516 '''
516 '''
517
517
518 _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),
519 re.MULTILINE | re.VERBOSE)
519 re.MULTILINE | re.VERBOSE)
520
520
521 _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),
522 re.MULTILINE | re.VERBOSE)
522 re.MULTILINE | re.VERBOSE)
523
523
524 # 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
525 # random marker ('#random') to each individual example's output. This way
525 # random marker ('#random') to each individual example's output. This way
526 # we don't need to modify any other code.
526 # we don't need to modify any other code.
527 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
527 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
528
528
529 # Mark tests to be executed in an external process - currently unsupported.
529 # Mark tests to be executed in an external process - currently unsupported.
530 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
530 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
531
531
532 def ip2py(self,source):
532 def ip2py(self,source):
533 """Convert input IPython source into valid Python."""
533 """Convert input IPython source into valid Python."""
534 out = []
534 out = []
535 newline = out.append
535 newline = out.append
536 #print 'IPSRC:\n',source,'\n###' # dbg
536 #print 'IPSRC:\n',source,'\n###' # dbg
537 # The input source must be first stripped of all bracketing whitespace
537 # The input source must be first stripped of all bracketing whitespace
538 # 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
539 # input
539 # input
540 for lnum,line in enumerate(source.strip().splitlines()):
540 for lnum,line in enumerate(source.strip().splitlines()):
541 newline(_ip.IP.prefilter(line,lnum>0))
541 newline(_ip.IP.prefilter(line,lnum>0))
542 newline('') # ensure a closing newline, needed by doctest
542 newline('') # ensure a closing newline, needed by doctest
543 #print "PYSRC:", '\n'.join(out) # dbg
543 #print "PYSRC:", '\n'.join(out) # dbg
544 return '\n'.join(out)
544 return '\n'.join(out)
545
545
546 def parse(self, string, name='<string>'):
546 def parse(self, string, name='<string>'):
547 """
547 """
548 Divide the given string into examples and intervening text,
548 Divide the given string into examples and intervening text,
549 and return them as a list of alternating Examples and strings.
549 and return them as a list of alternating Examples and strings.
550 Line numbers for the Examples are 0-based. The optional
550 Line numbers for the Examples are 0-based. The optional
551 argument `name` is a name identifying this string, and is only
551 argument `name` is a name identifying this string, and is only
552 used for error messages.
552 used for error messages.
553 """
553 """
554
554
555 #print 'Parse string:\n',string # dbg
555 #print 'Parse string:\n',string # dbg
556
556
557 string = string.expandtabs()
557 string = string.expandtabs()
558 # If all lines begin with the same indentation, then strip it.
558 # If all lines begin with the same indentation, then strip it.
559 min_indent = self._min_indent(string)
559 min_indent = self._min_indent(string)
560 if min_indent > 0:
560 if min_indent > 0:
561 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')])
562
562
563 output = []
563 output = []
564 charno, lineno = 0, 0
564 charno, lineno = 0, 0
565
565
566 # 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
567 # block of output in the test.
567 # block of output in the test.
568 if self._RANDOM_TEST.search(string):
568 if self._RANDOM_TEST.search(string):
569 random_marker = '\n# random'
569 random_marker = '\n# random'
570 else:
570 else:
571 random_marker = ''
571 random_marker = ''
572
572
573 # Whether to convert the input from ipython to python syntax
573 # Whether to convert the input from ipython to python syntax
574 ip2py = False
574 ip2py = False
575 # 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
576 # examples, then as IPython ones
576 # examples, then as IPython ones
577 terms = list(self._EXAMPLE_RE_PY.finditer(string))
577 terms = list(self._EXAMPLE_RE_PY.finditer(string))
578 if terms:
578 if terms:
579 # Normal Python example
579 # Normal Python example
580 #print '-'*70 # dbg
580 #print '-'*70 # dbg
581 #print 'PyExample, Source:\n',string # dbg
581 #print 'PyExample, Source:\n',string # dbg
582 #print '-'*70 # dbg
582 #print '-'*70 # dbg
583 Example = doctest.Example
583 Example = doctest.Example
584 else:
584 else:
585 # It's an ipython example. Note that IPExamples are run
585 # It's an ipython example. Note that IPExamples are run
586 # in-process, so their syntax must be turned into valid python.
586 # in-process, so their syntax must be turned into valid python.
587 # IPExternalExamples are run out-of-process (via pexpect) so they
587 # IPExternalExamples are run out-of-process (via pexpect) so they
588 # 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).
589 terms = list(self._EXAMPLE_RE_IP.finditer(string))
589 terms = list(self._EXAMPLE_RE_IP.finditer(string))
590 if self._EXTERNAL_IP.search(string):
590 if self._EXTERNAL_IP.search(string):
591 #print '-'*70 # dbg
591 #print '-'*70 # dbg
592 #print 'IPExternalExample, Source:\n',string # dbg
592 #print 'IPExternalExample, Source:\n',string # dbg
593 #print '-'*70 # dbg
593 #print '-'*70 # dbg
594 Example = IPExternalExample
594 Example = IPExternalExample
595 else:
595 else:
596 #print '-'*70 # dbg
596 #print '-'*70 # dbg
597 #print 'IPExample, Source:\n',string # dbg
597 #print 'IPExample, Source:\n',string # dbg
598 #print '-'*70 # dbg
598 #print '-'*70 # dbg
599 Example = IPExample
599 Example = IPExample
600 ip2py = True
600 ip2py = True
601
601
602 for m in terms:
602 for m in terms:
603 # Add the pre-example text to `output`.
603 # Add the pre-example text to `output`.
604 output.append(string[charno:m.start()])
604 output.append(string[charno:m.start()])
605 # Update lineno (lines before this example)
605 # Update lineno (lines before this example)
606 lineno += string.count('\n', charno, m.start())
606 lineno += string.count('\n', charno, m.start())
607 # Extract info from the regexp match.
607 # Extract info from the regexp match.
608 (source, options, want, exc_msg) = \
608 (source, options, want, exc_msg) = \
609 self._parse_example(m, name, lineno,ip2py)
609 self._parse_example(m, name, lineno,ip2py)
610
610
611 # Append the random-output marker (it defaults to empty in most
611 # Append the random-output marker (it defaults to empty in most
612 # cases, it's only non-empty for 'all-random' tests):
612 # cases, it's only non-empty for 'all-random' tests):
613 want += random_marker
613 want += random_marker
614
614
615 if Example is IPExternalExample:
615 if Example is IPExternalExample:
616 options[doctest.NORMALIZE_WHITESPACE] = True
616 options[doctest.NORMALIZE_WHITESPACE] = True
617 want += '\n'
617 want += '\n'
618
618
619 # Create an Example, and add it to the list.
619 # Create an Example, and add it to the list.
620 if not self._IS_BLANK_OR_COMMENT(source):
620 if not self._IS_BLANK_OR_COMMENT(source):
621 output.append(Example(source, want, exc_msg,
621 output.append(Example(source, want, exc_msg,
622 lineno=lineno,
622 lineno=lineno,
623 indent=min_indent+len(m.group('indent')),
623 indent=min_indent+len(m.group('indent')),
624 options=options))
624 options=options))
625 # Update lineno (lines inside this example)
625 # Update lineno (lines inside this example)
626 lineno += string.count('\n', m.start(), m.end())
626 lineno += string.count('\n', m.start(), m.end())
627 # Update charno.
627 # Update charno.
628 charno = m.end()
628 charno = m.end()
629 # Add any remaining post-example text to `output`.
629 # Add any remaining post-example text to `output`.
630 output.append(string[charno:])
630 output.append(string[charno:])
631 return output
631 return output
632
632
633 def _parse_example(self, m, name, lineno,ip2py=False):
633 def _parse_example(self, m, name, lineno,ip2py=False):
634 """
634 """
635 Given a regular expression match from `_EXAMPLE_RE` (`m`),
635 Given a regular expression match from `_EXAMPLE_RE` (`m`),
636 return a pair `(source, want)`, where `source` is the matched
636 return a pair `(source, want)`, where `source` is the matched
637 example's source code (with prompts and indentation stripped);
637 example's source code (with prompts and indentation stripped);
638 and `want` is the example's expected output (with indentation
638 and `want` is the example's expected output (with indentation
639 stripped).
639 stripped).
640
640
641 `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
642 where the example starts; both are used for error messages.
642 where the example starts; both are used for error messages.
643
643
644 Optional:
644 Optional:
645 `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
646 into valid python.
646 into valid python.
647 """
647 """
648
648
649 # Get the example's indentation level.
649 # Get the example's indentation level.
650 indent = len(m.group('indent'))
650 indent = len(m.group('indent'))
651
651
652 # Divide source into lines; check that they're properly
652 # Divide source into lines; check that they're properly
653 # indented; and then strip their indentation & prompts.
653 # indented; and then strip their indentation & prompts.
654 source_lines = m.group('source').split('\n')
654 source_lines = m.group('source').split('\n')
655
655
656 # We're using variable-length input prompts
656 # We're using variable-length input prompts
657 ps1 = m.group('ps1')
657 ps1 = m.group('ps1')
658 ps2 = m.group('ps2')
658 ps2 = m.group('ps2')
659 ps1_len = len(ps1)
659 ps1_len = len(ps1)
660
660
661 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
661 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
662 if ps2:
662 if ps2:
663 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
663 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
664
664
665 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])
666
666
667 if ip2py:
667 if ip2py:
668 # Convert source input from IPython into valid Python syntax
668 # Convert source input from IPython into valid Python syntax
669 source = self.ip2py(source)
669 source = self.ip2py(source)
670
670
671 # Divide want into lines; check that it's properly indented; and
671 # Divide want into lines; check that it's properly indented; and
672 # then strip the indentation. Spaces before the last newline should
672 # then strip the indentation. Spaces before the last newline should
673 # be preserved, so plain rstrip() isn't good enough.
673 # be preserved, so plain rstrip() isn't good enough.
674 want = m.group('want')
674 want = m.group('want')
675 want_lines = want.split('\n')
675 want_lines = want.split('\n')
676 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]):
677 del want_lines[-1] # forget final newline & spaces after it
677 del want_lines[-1] # forget final newline & spaces after it
678 self._check_prefix(want_lines, ' '*indent, name,
678 self._check_prefix(want_lines, ' '*indent, name,
679 lineno + len(source_lines))
679 lineno + len(source_lines))
680
680
681 # 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
682 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])
683
683
684 want = '\n'.join([wl[indent:] for wl in want_lines])
684 want = '\n'.join([wl[indent:] for wl in want_lines])
685
685
686 # If `want` contains a traceback message, then extract it.
686 # If `want` contains a traceback message, then extract it.
687 m = self._EXCEPTION_RE.match(want)
687 m = self._EXCEPTION_RE.match(want)
688 if m:
688 if m:
689 exc_msg = m.group('msg')
689 exc_msg = m.group('msg')
690 else:
690 else:
691 exc_msg = None
691 exc_msg = None
692
692
693 # Extract options from the source.
693 # Extract options from the source.
694 options = self._find_options(source, name, lineno)
694 options = self._find_options(source, name, lineno)
695
695
696 return source, options, want, exc_msg
696 return source, options, want, exc_msg
697
697
698 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
698 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
699 """
699 """
700 Given the lines of a source string (including prompts and
700 Given the lines of a source string (including prompts and
701 leading indentation), check to make sure that every prompt is
701 leading indentation), check to make sure that every prompt is
702 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
703 a space character, then raise ValueError.
703 a space character, then raise ValueError.
704
704
705 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
706 parameter, so that prompts of variable length can be dealt with.
706 parameter, so that prompts of variable length can be dealt with.
707 """
707 """
708 space_idx = indent+ps1_len
708 space_idx = indent+ps1_len
709 min_len = space_idx+1
709 min_len = space_idx+1
710 for i, line in enumerate(lines):
710 for i, line in enumerate(lines):
711 if len(line) >= min_len and line[space_idx] != ' ':
711 if len(line) >= min_len and line[space_idx] != ' ':
712 raise ValueError('line %r of the docstring for %s '
712 raise ValueError('line %r of the docstring for %s '
713 'lacks blank after %s: %r' %
713 'lacks blank after %s: %r' %
714 (lineno+i+1, name,
714 (lineno+i+1, name,
715 line[indent:space_idx], line))
715 line[indent:space_idx], line))
716
716
717
717
718 SKIP = doctest.register_optionflag('SKIP')
718 SKIP = doctest.register_optionflag('SKIP')
719
719
720
720
721 class IPDocTestRunner(doctest.DocTestRunner,object):
721 class IPDocTestRunner(doctest.DocTestRunner,object):
722 """Test runner that synchronizes the IPython namespace with test globals.
722 """Test runner that synchronizes the IPython namespace with test globals.
723 """
723 """
724
724
725 def run(self, test, compileflags=None, out=None, clear_globs=True):
725 def run(self, test, compileflags=None, out=None, clear_globs=True):
726
726
727 # Hack: ipython needs access to the execution context of the example,
727 # Hack: ipython needs access to the execution context of the example,
728 # so that it can propagate user variables loaded by %run into
728 # so that it can propagate user variables loaded by %run into
729 # 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
730 # attribute. Our new %run will then only make the namespace update
730 # attribute. Our new %run will then only make the namespace update
731 # when called (rather than unconconditionally updating test.globs here
731 # when called (rather than unconconditionally updating test.globs here
732 # 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).
733 _run_ns_sync.test_globs = test.globs
733 _run_ns_sync.test_globs = test.globs
734 _run_ns_sync.test_filename = test.filename
734 _run_ns_sync.test_filename = test.filename
735
735
736 return super(IPDocTestRunner,self).run(test,
736 return super(IPDocTestRunner,self).run(test,
737 compileflags,out,clear_globs)
737 compileflags,out,clear_globs)
738
738
739
739
740 class DocFileCase(doctest.DocFileCase):
740 class DocFileCase(doctest.DocFileCase):
741 """Overrides to provide filename
741 """Overrides to provide filename
742 """
742 """
743 def address(self):
743 def address(self):
744 return (self._dt_test.filename, None, None)
744 return (self._dt_test.filename, None, None)
745
745
746
746
747 class ExtensionDoctest(doctests.Doctest):
747 class ExtensionDoctest(doctests.Doctest):
748 """Nose Plugin that supports doctests in extension modules.
748 """Nose Plugin that supports doctests in extension modules.
749 """
749 """
750 name = 'extdoctest' # call nosetests with --with-extdoctest
750 name = 'extdoctest' # call nosetests with --with-extdoctest
751 enabled = True
751 enabled = True
752
752
753 def __init__(self,exclude_patterns=None):
753 def __init__(self,exclude_patterns=None):
754 """Create a new ExtensionDoctest plugin.
754 """Create a new ExtensionDoctest plugin.
755
755
756 Parameters
756 Parameters
757 ----------
757 ----------
758
758
759 exclude_patterns : sequence of strings, optional
759 exclude_patterns : sequence of strings, optional
760 These patterns are compiled as regular expressions, subsequently used
760 These patterns are compiled as regular expressions, subsequently used
761 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
762 suite (using pattern.search(), NOT pattern.match() ).
762 suite (using pattern.search(), NOT pattern.match() ).
763 """
763 """
764
764
765 if exclude_patterns is None:
765 if exclude_patterns is None:
766 exclude_patterns = []
766 exclude_patterns = []
767 self.exclude_patterns = map(re.compile,exclude_patterns)
767 self.exclude_patterns = map(re.compile,exclude_patterns)
768 doctests.Doctest.__init__(self)
768 doctests.Doctest.__init__(self)
769
769
770 def options(self, parser, env=os.environ):
770 def options(self, parser, env=os.environ):
771 Plugin.options(self, parser, env)
771 Plugin.options(self, parser, env)
772 parser.add_option('--doctest-tests', action='store_true',
772 parser.add_option('--doctest-tests', action='store_true',
773 dest='doctest_tests',
773 dest='doctest_tests',
774 default=env.get('NOSE_DOCTEST_TESTS',True),
774 default=env.get('NOSE_DOCTEST_TESTS',True),
775 help="Also look for doctests in test modules. "
775 help="Also look for doctests in test modules. "
776 "Note that classes, methods and functions should "
776 "Note that classes, methods and functions should "
777 "have either doctests or non-doctest tests, "
777 "have either doctests or non-doctest tests, "
778 "not both. [NOSE_DOCTEST_TESTS]")
778 "not both. [NOSE_DOCTEST_TESTS]")
779 parser.add_option('--doctest-extension', action="append",
779 parser.add_option('--doctest-extension', action="append",
780 dest="doctestExtension",
780 dest="doctestExtension",
781 help="Also look for doctests in files with "
781 help="Also look for doctests in files with "
782 "this extension [NOSE_DOCTEST_EXTENSION]")
782 "this extension [NOSE_DOCTEST_EXTENSION]")
783 # Set the default as a list, if given in env; otherwise
783 # Set the default as a list, if given in env; otherwise
784 # an additional value set on the command line will cause
784 # an additional value set on the command line will cause
785 # an error.
785 # an error.
786 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
786 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
787 if env_setting is not None:
787 if env_setting is not None:
788 parser.set_defaults(doctestExtension=tolist(env_setting))
788 parser.set_defaults(doctestExtension=tolist(env_setting))
789
789
790
790
791 def configure(self, options, config):
791 def configure(self, options, config):
792 Plugin.configure(self, options, config)
792 Plugin.configure(self, options, config)
793 self.doctest_tests = options.doctest_tests
793 self.doctest_tests = options.doctest_tests
794 self.extension = tolist(options.doctestExtension)
794 self.extension = tolist(options.doctestExtension)
795
795
796 self.parser = doctest.DocTestParser()
796 self.parser = doctest.DocTestParser()
797 self.finder = DocTestFinder()
797 self.finder = DocTestFinder()
798 self.checker = IPDoctestOutputChecker()
798 self.checker = IPDoctestOutputChecker()
799 self.globs = None
799 self.globs = None
800 self.extraglobs = None
800 self.extraglobs = None
801
801
802
802
803 def loadTestsFromExtensionModule(self,filename):
803 def loadTestsFromExtensionModule(self,filename):
804 bpath,mod = os.path.split(filename)
804 bpath,mod = os.path.split(filename)
805 modname = os.path.splitext(mod)[0]
805 modname = os.path.splitext(mod)[0]
806 try:
806 try:
807 sys.path.append(bpath)
807 sys.path.append(bpath)
808 module = __import__(modname)
808 module = __import__(modname)
809 tests = list(self.loadTestsFromModule(module))
809 tests = list(self.loadTestsFromModule(module))
810 finally:
810 finally:
811 sys.path.pop()
811 sys.path.pop()
812 return tests
812 return tests
813
813
814 # 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
815 # a few modifications to control output checking.
815 # a few modifications to control output checking.
816
816
817 def loadTestsFromModule(self, module):
817 def loadTestsFromModule(self, module):
818 #print '*** ipdoctest - lTM',module # dbg
818 #print '*** ipdoctest - lTM',module # dbg
819
819
820 if not self.matches(module.__name__):
820 if not self.matches(module.__name__):
821 log.debug("Doctest doesn't want module %s", module)
821 log.debug("Doctest doesn't want module %s", module)
822 return
822 return
823
823
824 tests = self.finder.find(module,globs=self.globs,
824 tests = self.finder.find(module,globs=self.globs,
825 extraglobs=self.extraglobs)
825 extraglobs=self.extraglobs)
826 if not tests:
826 if not tests:
827 return
827 return
828
828
829 # always use whitespace and ellipsis options
829 # always use whitespace and ellipsis options
830 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
830 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
831
831
832 tests.sort()
832 tests.sort()
833 module_file = module.__file__
833 module_file = module.__file__
834 if module_file[-4:] in ('.pyc', '.pyo'):
834 if module_file[-4:] in ('.pyc', '.pyo'):
835 module_file = module_file[:-1]
835 module_file = module_file[:-1]
836 for test in tests:
836 for test in tests:
837 if not test.examples:
837 if not test.examples:
838 continue
838 continue
839 if not test.filename:
839 if not test.filename:
840 test.filename = module_file
840 test.filename = module_file
841
841
842 yield DocTestCase(test,
842 yield DocTestCase(test,
843 optionflags=optionflags,
843 optionflags=optionflags,
844 checker=self.checker)
844 checker=self.checker)
845
845
846
846
847 def loadTestsFromFile(self, filename):
847 def loadTestsFromFile(self, filename):
848 if is_extension_module(filename):
848 if is_extension_module(filename):
849 for t in self.loadTestsFromExtensionModule(filename):
849 for t in self.loadTestsFromExtensionModule(filename):
850 yield t
850 yield t
851 else:
851 else:
852 if self.extension and anyp(filename.endswith, self.extension):
852 if self.extension and anyp(filename.endswith, self.extension):
853 name = os.path.basename(filename)
853 name = os.path.basename(filename)
854 dh = open(filename)
854 dh = open(filename)
855 try:
855 try:
856 doc = dh.read()
856 doc = dh.read()
857 finally:
857 finally:
858 dh.close()
858 dh.close()
859 test = self.parser.get_doctest(
859 test = self.parser.get_doctest(
860 doc, globs={'__file__': filename}, name=name,
860 doc, globs={'__file__': filename}, name=name,
861 filename=filename, lineno=0)
861 filename=filename, lineno=0)
862 if test.examples:
862 if test.examples:
863 #print 'FileCase:',test.examples # dbg
863 #print 'FileCase:',test.examples # dbg
864 yield DocFileCase(test)
864 yield DocFileCase(test)
865 else:
865 else:
866 yield False # no tests to load
866 yield False # no tests to load
867
867
868 def wantFile(self,filename):
868 def wantFile(self,filename):
869 """Return whether the given filename should be scanned for tests.
869 """Return whether the given filename should be scanned for tests.
870
870
871 Modified version that accepts extension modules as valid containers for
871 Modified version that accepts extension modules as valid containers for
872 doctests.
872 doctests.
873 """
873 """
874 # print '*** ipdoctest- wantFile:',filename # dbg
874 # print '*** ipdoctest- wantFile:',filename # dbg
875
875
876 for pat in self.exclude_patterns:
876 for pat in self.exclude_patterns:
877 if pat.search(filename):
877 if pat.search(filename):
878 # print '###>>> SKIP:',filename # dbg
878 # print '###>>> SKIP:',filename # dbg
879 return False
879 return False
880
880
881 if is_extension_module(filename):
881 if is_extension_module(filename):
882 return True
882 return True
883 else:
883 else:
884 return doctests.Doctest.wantFile(self,filename)
884 return doctests.Doctest.wantFile(self,filename)
885
885
886
886
887 class IPythonDoctest(ExtensionDoctest):
887 class IPythonDoctest(ExtensionDoctest):
888 """Nose Plugin that supports doctests in extension modules.
888 """Nose Plugin that supports doctests in extension modules.
889 """
889 """
890 name = 'ipdoctest' # call nosetests with --with-ipdoctest
890 name = 'ipdoctest' # call nosetests with --with-ipdoctest
891 enabled = True
891 enabled = True
892
892
893 def makeTest(self, obj, parent):
893 def makeTest(self, obj, parent):
894 """Look for doctests in the given object, which will be a
894 """Look for doctests in the given object, which will be a
895 function, method or class.
895 function, method or class.
896 """
896 """
897 # always use whitespace and ellipsis options
897 # always use whitespace and ellipsis options
898 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
898 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
899
899
900 doctests = self.finder.find(obj, module=getmodule(parent))
900 doctests = self.finder.find(obj, module=getmodule(parent))
901 if doctests:
901 if doctests:
902 for test in doctests:
902 for test in doctests:
903 if len(test.examples) == 0:
903 if len(test.examples) == 0:
904 continue
904 continue
905
905
906 yield DocTestCase(test, obj=obj,
906 yield DocTestCase(test, obj=obj,
907 optionflags=optionflags,
907 optionflags=optionflags,
908 checker=self.checker)
908 checker=self.checker)
909
909
910 def options(self, parser, env=os.environ):
910 def options(self, parser, env=os.environ):
911 Plugin.options(self, parser, env)
911 Plugin.options(self, parser, env)
912 parser.add_option('--ipdoctest-tests', action='store_true',
912 parser.add_option('--ipdoctest-tests', action='store_true',
913 dest='ipdoctest_tests',
913 dest='ipdoctest_tests',
914 default=env.get('NOSE_IPDOCTEST_TESTS',True),
914 default=env.get('NOSE_IPDOCTEST_TESTS',True),
915 help="Also look for doctests in test modules. "
915 help="Also look for doctests in test modules. "
916 "Note that classes, methods and functions should "
916 "Note that classes, methods and functions should "
917 "have either doctests or non-doctest tests, "
917 "have either doctests or non-doctest tests, "
918 "not both. [NOSE_IPDOCTEST_TESTS]")
918 "not both. [NOSE_IPDOCTEST_TESTS]")
919 parser.add_option('--ipdoctest-extension', action="append",
919 parser.add_option('--ipdoctest-extension', action="append",
920 dest="ipdoctest_extension",
920 dest="ipdoctest_extension",
921 help="Also look for doctests in files with "
921 help="Also look for doctests in files with "
922 "this extension [NOSE_IPDOCTEST_EXTENSION]")
922 "this extension [NOSE_IPDOCTEST_EXTENSION]")
923 # Set the default as a list, if given in env; otherwise
923 # Set the default as a list, if given in env; otherwise
924 # an additional value set on the command line will cause
924 # an additional value set on the command line will cause
925 # an error.
925 # an error.
926 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
926 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
927 if env_setting is not None:
927 if env_setting is not None:
928 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
928 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
929
929
930 def configure(self, options, config):
930 def configure(self, options, config):
931 Plugin.configure(self, options, config)
931 Plugin.configure(self, options, config)
932 self.doctest_tests = options.ipdoctest_tests
932 self.doctest_tests = options.ipdoctest_tests
933 self.extension = tolist(options.ipdoctest_extension)
933 self.extension = tolist(options.ipdoctest_extension)
934
934
935 self.parser = IPDocTestParser()
935 self.parser = IPDocTestParser()
936 self.finder = DocTestFinder(parser=self.parser)
936 self.finder = DocTestFinder(parser=self.parser)
937 self.checker = IPDoctestOutputChecker()
937 self.checker = IPDoctestOutputChecker()
938 self.globs = None
938 self.globs = None
939 self.extraglobs = None
939 self.extraglobs = None
General Comments 0
You need to be logged in to leave comments. Login now