##// END OF EJS Templates
Vastly cleaner solution for the namespace problem!
Fernando Perez -
Show More
@@ -1,955 +1,738 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 - IPython functions that produce output as a side-effect of calling a system
17 - IPython functions that produce output as a side-effect of calling a system
18 process (e.g. 'ls') can be doc-tested, but they must be handled in an
18 process (e.g. 'ls') can be doc-tested, but they must be handled in an
19 external IPython process. Such doctests must be tagged with:
19 external IPython process. Such doctests must be tagged with:
20
20
21 # ipdoctest: EXTERNAL
21 # ipdoctest: EXTERNAL
22
22
23 so that the testing machinery handles them differently. Since these are run
23 so that the testing machinery handles them differently. Since these are run
24 via pexpect in an external process, they can't deal with exceptions or other
24 via pexpect in an external process, they can't deal with exceptions or other
25 fancy featurs of regular doctests. You must limit such tests to simple
25 fancy featurs of regular doctests. You must limit such tests to simple
26 matching of the output. For this reason, I recommend you limit these kinds
26 matching of the output. For this reason, I recommend you limit these kinds
27 of doctests to features that truly require a separate process, and use the
27 of doctests to features that truly require a separate process, and use the
28 normal IPython ones (which have all the features of normal doctests) for
28 normal IPython ones (which have all the features of normal doctests) for
29 everything else. See the examples at the bottom of this file for a
29 everything else. See the examples at the bottom of this file for a
30 comparison of what can be done with both types.
30 comparison of what can be done with both types.
31 """
31 """
32
32
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Module imports
35 # Module imports
36
36
37 # From the standard library
37 # From the standard library
38 import __builtin__
38 import __builtin__
39 import commands
39 import commands
40 import doctest
40 import doctest
41 import inspect
41 import inspect
42 import logging
42 import logging
43 import os
43 import os
44 import re
44 import re
45 import sys
45 import sys
46 import traceback
46 import traceback
47 import unittest
47 import unittest
48
48
49 from inspect import getmodule
49 from inspect import getmodule
50 from StringIO import StringIO
50 from StringIO import StringIO
51
51
52 # We are overriding the default doctest runner, so we need to import a few
52 # We are overriding the default doctest runner, so we need to import a few
53 # things from doctest directly
53 # things from doctest directly
54 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
54 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
55 _unittest_reportflags, DocTestRunner,
55 _unittest_reportflags, DocTestRunner,
56 _extract_future_flags, pdb, _OutputRedirectingPdb,
56 _extract_future_flags, pdb, _OutputRedirectingPdb,
57 _exception_traceback,
57 _exception_traceback,
58 linecache)
58 linecache)
59
59
60 # Third-party modules
60 # Third-party modules
61 import nose.core
61 import nose.core
62
62
63 from nose.plugins import doctests, Plugin
63 from nose.plugins import doctests, Plugin
64 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
64 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
65
65
66 # Our own imports
66 # Our own imports
67 #from extdoctest import ExtensionDoctest, DocTestFinder
67 #from extdoctest import ExtensionDoctest, DocTestFinder
68 #from dttools import DocTestFinder, DocTestCase
68 #from dttools import DocTestFinder, DocTestCase
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70 # Module globals and other constants
70 # Module globals and other constants
71
71
72 log = logging.getLogger(__name__)
72 log = logging.getLogger(__name__)
73
73
74 ###########################################################################
74 ###########################################################################
75 # *** HACK ***
75 # *** HACK ***
76 # We must start our own ipython object and heavily muck with it so that all the
76 # We must start our own ipython object and heavily muck with it so that all the
77 # modifications IPython makes to system behavior don't send the doctest
77 # modifications IPython makes to system behavior don't send the doctest
78 # machinery into a fit. This code should be considered a gross hack, but it
78 # machinery into a fit. This code should be considered a gross hack, but it
79 # gets the job done.
79 # gets the job done.
80
80
81 class ncdict(dict):
81 class ncdict(dict):
82 """Non-copying dict class.
82 """Non-copying dict class.
83
83
84 This is a special-purpose dict subclass that overrides the .copy() method
84 This is a special-purpose dict subclass that overrides the .copy() method
85 to return the original object itself. We need it to ensure that doctests
85 to return the original object itself. We need it to ensure that doctests
86 happen in the IPython namespace, but doctest always makes a shallow copy of
86 happen in the IPython namespace, but doctest always makes a shallow copy of
87 the given globals for execution. Since we actually *want* this namespace
87 the given globals for execution. Since we actually *want* this namespace
88 to be persistent (this is how the user's session maintains state), we
88 to be persistent (this is how the user's session maintains state), we
89 simply fool doctest by returning the original object upoon copy.
89 simply fool doctest by returning the original object upoon copy.
90 """
90 """
91
91
92 def copy(self):
92 def copy(self):
93 return self
93 return self
94
94
95
95
96 # XXX - Hack to modify the %run command so we can sync the user's namespace
96 # XXX - Hack to modify the %run command so we can sync the user's namespace
97 # with the test globals. Once we move over to a clean magic system, this will
97 # with the test globals. Once we move over to a clean magic system, this will
98 # be done with much less ugliness.
98 # be done with much less ugliness.
99
99
100 def _run_ns_sync(self,arg_s,runner=None):
100 def _run_ns_sync(self,arg_s,runner=None):
101 """Modified version of %run that syncs testing namespaces.
101 """Modified version of %run that syncs testing namespaces.
102
102
103 This is strictly needed for running doctests that call %run.
103 This is strictly needed for running doctests that call %run.
104 """
104 """
105
105
106 out = _ip.IP.magic_run_ori(arg_s,runner)
106 out = _ip.IP.magic_run_ori(arg_s,runner)
107 _run_ns_sync.test_globs.update(_ip.user_ns)
107 _run_ns_sync.test_globs.update(_ip.user_ns)
108 return out
108 return out
109
109
110
110
111 def start_ipython():
111 def start_ipython():
112 """Start a global IPython shell, which we need for IPython-specific syntax.
112 """Start a global IPython shell, which we need for IPython-specific syntax.
113 """
113 """
114 import new
114 import new
115
115
116 import IPython
116 import IPython
117
117
118 def xsys(cmd):
118 def xsys(cmd):
119 """Execute a command and print its output.
119 """Execute a command and print its output.
120
120
121 This is just a convenience function to replace the IPython system call
121 This is just a convenience function to replace the IPython system call
122 with one that is more doctest-friendly.
122 with one that is more doctest-friendly.
123 """
123 """
124 cmd = _ip.IP.var_expand(cmd,depth=1)
124 cmd = _ip.IP.var_expand(cmd,depth=1)
125 sys.stdout.write(commands.getoutput(cmd))
125 sys.stdout.write(commands.getoutput(cmd))
126 sys.stdout.flush()
126 sys.stdout.flush()
127
127
128 # Store certain global objects that IPython modifies
128 # Store certain global objects that IPython modifies
129 _displayhook = sys.displayhook
129 _displayhook = sys.displayhook
130 _excepthook = sys.excepthook
130 _excepthook = sys.excepthook
131 _main = sys.modules.get('__main__')
131 _main = sys.modules.get('__main__')
132
132
133 # Start IPython instance. We customize it to start with minimal frills and
133 # Start IPython instance. We customize it to start with minimal frills and
134 # with our own namespace.
134 # with our own namespace.
135 argv = ['--classic','--noterm_title']
135 argv = ['--classic','--noterm_title']
136 user_ns = ncdict()
136 user_ns = ncdict()
137 IPython.Shell.IPShell(argv,user_ns)
137 IPython.Shell.IPShell(argv,user_ns)
138
138
139 # Deactivate the various python system hooks added by ipython for
139 # Deactivate the various python system hooks added by ipython for
140 # interactive convenience so we don't confuse the doctest system
140 # interactive convenience so we don't confuse the doctest system
141 sys.modules['__main__'] = _main
141 sys.modules['__main__'] = _main
142 sys.displayhook = _displayhook
142 sys.displayhook = _displayhook
143 sys.excepthook = _excepthook
143 sys.excepthook = _excepthook
144
144
145 # So that ipython magics and aliases can be doctested (they work by making
145 # So that ipython magics and aliases can be doctested (they work by making
146 # a call into a global _ip object)
146 # a call into a global _ip object)
147 _ip = IPython.ipapi.get()
147 _ip = IPython.ipapi.get()
148 __builtin__._ip = _ip
148 __builtin__._ip = _ip
149
149
150 # Modify the IPython system call with one that uses getoutput, so that we
150 # Modify the IPython system call with one that uses getoutput, so that we
151 # can capture subcommands and print them to Python's stdout, otherwise the
151 # can capture subcommands and print them to Python's stdout, otherwise the
152 # doctest machinery would miss them.
152 # doctest machinery would miss them.
153 _ip.system = xsys
153 _ip.system = xsys
154
154
155 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
155 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
156 _ip.IP.magic_run_ori = _ip.IP.magic_run
156 _ip.IP.magic_run_ori = _ip.IP.magic_run
157 _ip.IP.magic_run = im
157 _ip.IP.magic_run = im
158
158
159 # The start call MUST be made here. I'm not sure yet why it doesn't work if
159 # The start call MUST be made here. I'm not sure yet why it doesn't work if
160 # it is made later, at plugin initialization time, but in all my tests, that's
160 # it is made later, at plugin initialization time, but in all my tests, that's
161 # the case.
161 # the case.
162 start_ipython()
162 start_ipython()
163
163
164 # *** END HACK ***
164 # *** END HACK ***
165 ###########################################################################
165 ###########################################################################
166
166
167 # Classes and functions
167 # Classes and functions
168
168
169 def is_extension_module(filename):
169 def is_extension_module(filename):
170 """Return whether the given filename is an extension module.
170 """Return whether the given filename is an extension module.
171
171
172 This simply checks that the extension is either .so or .pyd.
172 This simply checks that the extension is either .so or .pyd.
173 """
173 """
174 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
174 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
175
175
176
176
177 # Modified version of the one in the stdlib, that fixes a python bug (doctests
177 # Modified version of the one in the stdlib, that fixes a python bug (doctests
178 # not found in extension modules, http://bugs.python.org/issue3158)
178 # not found in extension modules, http://bugs.python.org/issue3158)
179 class DocTestFinder(doctest.DocTestFinder):
179 class DocTestFinder(doctest.DocTestFinder):
180
180
181 def _from_module(self, module, object):
181 def _from_module(self, module, object):
182 """
182 """
183 Return true if the given object is defined in the given
183 Return true if the given object is defined in the given
184 module.
184 module.
185 """
185 """
186 if module is None:
186 if module is None:
187 #print '_fm C1' # dbg
187 #print '_fm C1' # dbg
188 return True
188 return True
189 elif inspect.isfunction(object):
189 elif inspect.isfunction(object):
190 #print '_fm C2' # dbg
190 #print '_fm C2' # dbg
191 return module.__dict__ is object.func_globals
191 return module.__dict__ is object.func_globals
192 elif inspect.isbuiltin(object):
192 elif inspect.isbuiltin(object):
193 #print '_fm C2-1' # dbg
193 #print '_fm C2-1' # dbg
194 return module.__name__ == object.__module__
194 return module.__name__ == object.__module__
195 elif inspect.isclass(object):
195 elif inspect.isclass(object):
196 #print '_fm C3' # dbg
196 #print '_fm C3' # dbg
197 return module.__name__ == object.__module__
197 return module.__name__ == object.__module__
198 elif inspect.ismethod(object):
198 elif inspect.ismethod(object):
199 # This one may be a bug in cython that fails to correctly set the
199 # This one may be a bug in cython that fails to correctly set the
200 # __module__ attribute of methods, but since the same error is easy
200 # __module__ attribute of methods, but since the same error is easy
201 # to make by extension code writers, having this safety in place
201 # to make by extension code writers, having this safety in place
202 # isn't such a bad idea
202 # isn't such a bad idea
203 #print '_fm C3-1' # dbg
203 #print '_fm C3-1' # dbg
204 return module.__name__ == object.im_class.__module__
204 return module.__name__ == object.im_class.__module__
205 elif inspect.getmodule(object) is not None:
205 elif inspect.getmodule(object) is not None:
206 #print '_fm C4' # dbg
206 #print '_fm C4' # dbg
207 #print 'C4 mod',module,'obj',object # dbg
207 #print 'C4 mod',module,'obj',object # dbg
208 return module is inspect.getmodule(object)
208 return module is inspect.getmodule(object)
209 elif hasattr(object, '__module__'):
209 elif hasattr(object, '__module__'):
210 #print '_fm C5' # dbg
210 #print '_fm C5' # dbg
211 return module.__name__ == object.__module__
211 return module.__name__ == object.__module__
212 elif isinstance(object, property):
212 elif isinstance(object, property):
213 #print '_fm C6' # dbg
213 #print '_fm C6' # dbg
214 return True # [XX] no way not be sure.
214 return True # [XX] no way not be sure.
215 else:
215 else:
216 raise ValueError("object must be a class or function")
216 raise ValueError("object must be a class or function")
217
217
218 def _find(self, tests, obj, name, module, source_lines, globs, seen):
218 def _find(self, tests, obj, name, module, source_lines, globs, seen):
219 """
219 """
220 Find tests for the given object and any contained objects, and
220 Find tests for the given object and any contained objects, and
221 add them to `tests`.
221 add them to `tests`.
222 """
222 """
223
223
224 doctest.DocTestFinder._find(self,tests, obj, name, module,
224 doctest.DocTestFinder._find(self,tests, obj, name, module,
225 source_lines, globs, seen)
225 source_lines, globs, seen)
226
226
227 # Below we re-run pieces of the above method with manual modifications,
227 # Below we re-run pieces of the above method with manual modifications,
228 # because the original code is buggy and fails to correctly identify
228 # because the original code is buggy and fails to correctly identify
229 # doctests in extension modules.
229 # doctests in extension modules.
230
230
231 # Local shorthands
231 # Local shorthands
232 from inspect import isroutine, isclass, ismodule
232 from inspect import isroutine, isclass, ismodule
233
233
234 # Look for tests in a module's contained objects.
234 # Look for tests in a module's contained objects.
235 if inspect.ismodule(obj) and self._recurse:
235 if inspect.ismodule(obj) and self._recurse:
236 for valname, val in obj.__dict__.items():
236 for valname, val in obj.__dict__.items():
237 valname1 = '%s.%s' % (name, valname)
237 valname1 = '%s.%s' % (name, valname)
238 if ( (isroutine(val) or isclass(val))
238 if ( (isroutine(val) or isclass(val))
239 and self._from_module(module, val) ):
239 and self._from_module(module, val) ):
240
240
241 self._find(tests, val, valname1, module, source_lines,
241 self._find(tests, val, valname1, module, source_lines,
242 globs, seen)
242 globs, seen)
243
243
244 # Look for tests in a class's contained objects.
244 # Look for tests in a class's contained objects.
245 if inspect.isclass(obj) and self._recurse:
245 if inspect.isclass(obj) and self._recurse:
246 #print 'RECURSE into class:',obj # dbg
246 #print 'RECURSE into class:',obj # dbg
247 for valname, val in obj.__dict__.items():
247 for valname, val in obj.__dict__.items():
248 #valname1 = '%s.%s' % (name, valname) # dbg
248 #valname1 = '%s.%s' % (name, valname) # dbg
249 #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg
249 #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg
250 # Special handling for staticmethod/classmethod.
250 # Special handling for staticmethod/classmethod.
251 if isinstance(val, staticmethod):
251 if isinstance(val, staticmethod):
252 val = getattr(obj, valname)
252 val = getattr(obj, valname)
253 if isinstance(val, classmethod):
253 if isinstance(val, classmethod):
254 val = getattr(obj, valname).im_func
254 val = getattr(obj, valname).im_func
255
255
256 # Recurse to methods, properties, and nested classes.
256 # Recurse to methods, properties, and nested classes.
257 if ((inspect.isfunction(val) or inspect.isclass(val) or
257 if ((inspect.isfunction(val) or inspect.isclass(val) or
258 inspect.ismethod(val) or
258 inspect.ismethod(val) or
259 isinstance(val, property)) and
259 isinstance(val, property)) and
260 self._from_module(module, val)):
260 self._from_module(module, val)):
261 valname = '%s.%s' % (name, valname)
261 valname = '%s.%s' % (name, valname)
262 self._find(tests, val, valname, module, source_lines,
262 self._find(tests, val, valname, module, source_lines,
263 globs, seen)
263 globs, seen)
264
264
265
265
266 # second-chance checker; if the default comparison doesn't
266 # second-chance checker; if the default comparison doesn't
267 # pass, then see if the expected output string contains flags that
267 # pass, then see if the expected output string contains flags that
268 # tell us to ignore the output
268 # tell us to ignore the output
269 class IPDoctestOutputChecker(doctest.OutputChecker):
269 class IPDoctestOutputChecker(doctest.OutputChecker):
270 def check_output(self, want, got, optionflags):
270 def check_output(self, want, got, optionflags):
271 #print '*** My Checker!' # dbg
271 #print '*** My Checker!' # dbg
272
272
273 ret = doctest.OutputChecker.check_output(self, want, got,
273 ret = doctest.OutputChecker.check_output(self, want, got,
274 optionflags)
274 optionflags)
275 if not ret:
275 if not ret:
276 if "#random" in want:
276 if "#random" in want:
277 return True
277 return True
278
278
279 return ret
279 return ret
280
280
281
281
282 class DocTestCase(doctests.DocTestCase):
282 class DocTestCase(doctests.DocTestCase):
283 """Proxy for DocTestCase: provides an address() method that
283 """Proxy for DocTestCase: provides an address() method that
284 returns the correct address for the doctest case. Otherwise
284 returns the correct address for the doctest case. Otherwise
285 acts as a proxy to the test case. To provide hints for address(),
285 acts as a proxy to the test case. To provide hints for address(),
286 an obj may also be passed -- this will be used as the test object
286 an obj may also be passed -- this will be used as the test object
287 for purposes of determining the test address, if it is provided.
287 for purposes of determining the test address, if it is provided.
288 """
288 """
289
289
290 # Note: this method was taken from numpy's nosetester module.
290 # Note: this method was taken from numpy's nosetester module.
291
291
292 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
292 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
293 # its constructor that blocks non-default arguments from being passed
293 # its constructor that blocks non-default arguments from being passed
294 # down into doctest.DocTestCase
294 # down into doctest.DocTestCase
295
295
296 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
296 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
297 checker=None, obj=None, result_var='_'):
297 checker=None, obj=None, result_var='_'):
298 self._result_var = result_var
298 self._result_var = result_var
299 doctests.DocTestCase.__init__(self, test,
299 doctests.DocTestCase.__init__(self, test,
300 optionflags=optionflags,
300 optionflags=optionflags,
301 setUp=setUp, tearDown=tearDown,
301 setUp=setUp, tearDown=tearDown,
302 checker=checker)
302 checker=checker)
303 # Now we must actually copy the original constructor from the stdlib
303 # Now we must actually copy the original constructor from the stdlib
304 # doctest class, because we can't call it directly and a bug in nose
304 # doctest class, because we can't call it directly and a bug in nose
305 # means it never gets passed the right arguments.
305 # means it never gets passed the right arguments.
306
306
307 self._dt_optionflags = optionflags
307 self._dt_optionflags = optionflags
308 self._dt_checker = checker
308 self._dt_checker = checker
309 self._dt_test = test
309 self._dt_test = test
310 self._dt_setUp = setUp
310 self._dt_setUp = setUp
311 self._dt_tearDown = tearDown
311 self._dt_tearDown = tearDown
312
312
313 # Modified runTest from the default stdlib
313 # Modified runTest from the default stdlib
314 def runTest(self):
314 def runTest(self):
315 #print 'HERE!' # dbg
315 #print 'HERE!' # dbg
316
316
317 test = self._dt_test
317 test = self._dt_test
318 old = sys.stdout
318 old = sys.stdout
319 new = StringIO()
319 new = StringIO()
320 optionflags = self._dt_optionflags
320 optionflags = self._dt_optionflags
321
321
322 if not (optionflags & REPORTING_FLAGS):
322 if not (optionflags & REPORTING_FLAGS):
323 # The option flags don't include any reporting flags,
323 # The option flags don't include any reporting flags,
324 # so add the default reporting flags
324 # so add the default reporting flags
325 optionflags |= _unittest_reportflags
325 optionflags |= _unittest_reportflags
326
326
327 runner = IPDocTestRunner(optionflags=optionflags,
327 runner = IPDocTestRunner(optionflags=optionflags,
328 checker=self._dt_checker, verbose=False)
328 checker=self._dt_checker, verbose=False)
329
329
330 try:
330 try:
331 runner.DIVIDER = "-"*70
331 runner.DIVIDER = "-"*70
332 failures, tries = runner.run(
332 failures, tries = runner.run(
333 test, out=new.write, clear_globs=False)
333 test, out=new.write, clear_globs=False)
334 finally:
334 finally:
335 sys.stdout = old
335 sys.stdout = old
336
336
337 if failures:
337 if failures:
338 raise self.failureException(self.format_failure(new.getvalue()))
338 raise self.failureException(self.format_failure(new.getvalue()))
339
339
340
340
341 # A simple subclassing of the original with a different class name, so we can
341 # A simple subclassing of the original with a different class name, so we can
342 # distinguish and treat differently IPython examples from pure python ones.
342 # distinguish and treat differently IPython examples from pure python ones.
343 class IPExample(doctest.Example): pass
343 class IPExample(doctest.Example): pass
344
344
345
345
346 class IPExternalExample(doctest.Example):
346 class IPExternalExample(doctest.Example):
347 """Doctest examples to be run in an external process."""
347 """Doctest examples to be run in an external process."""
348
348
349 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
349 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
350 options=None):
350 options=None):
351 # Parent constructor
351 # Parent constructor
352 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
352 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
353
353
354 # An EXTRA newline is needed to prevent pexpect hangs
354 # An EXTRA newline is needed to prevent pexpect hangs
355 self.source += '\n'
355 self.source += '\n'
356
356
357
357
358 class IPDocTestParser(doctest.DocTestParser):
358 class IPDocTestParser(doctest.DocTestParser):
359 """
359 """
360 A class used to parse strings containing doctest examples.
360 A class used to parse strings containing doctest examples.
361
361
362 Note: This is a version modified to properly recognize IPython input and
362 Note: This is a version modified to properly recognize IPython input and
363 convert any IPython examples into valid Python ones.
363 convert any IPython examples into valid Python ones.
364 """
364 """
365 # This regular expression is used to find doctest examples in a
365 # This regular expression is used to find doctest examples in a
366 # string. It defines three groups: `source` is the source code
366 # string. It defines three groups: `source` is the source code
367 # (including leading indentation and prompts); `indent` is the
367 # (including leading indentation and prompts); `indent` is the
368 # indentation of the first (PS1) line of the source code; and
368 # indentation of the first (PS1) line of the source code; and
369 # `want` is the expected output (including leading indentation).
369 # `want` is the expected output (including leading indentation).
370
370
371 # Classic Python prompts or default IPython ones
371 # Classic Python prompts or default IPython ones
372 _PS1_PY = r'>>>'
372 _PS1_PY = r'>>>'
373 _PS2_PY = r'\.\.\.'
373 _PS2_PY = r'\.\.\.'
374
374
375 _PS1_IP = r'In\ \[\d+\]:'
375 _PS1_IP = r'In\ \[\d+\]:'
376 _PS2_IP = r'\ \ \ \.\.\.+:'
376 _PS2_IP = r'\ \ \ \.\.\.+:'
377
377
378 _RE_TPL = r'''
378 _RE_TPL = r'''
379 # Source consists of a PS1 line followed by zero or more PS2 lines.
379 # Source consists of a PS1 line followed by zero or more PS2 lines.
380 (?P<source>
380 (?P<source>
381 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
381 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
382 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
382 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
383 \n? # a newline
383 \n? # a newline
384 # Want consists of any non-blank lines that do not start with PS1.
384 # Want consists of any non-blank lines that do not start with PS1.
385 (?P<want> (?:(?![ ]*$) # Not a blank line
385 (?P<want> (?:(?![ ]*$) # Not a blank line
386 (?![ ]*%s) # Not a line starting with PS1
386 (?![ ]*%s) # Not a line starting with PS1
387 (?![ ]*%s) # Not a line starting with PS2
387 (?![ ]*%s) # Not a line starting with PS2
388 .*$\n? # But any other line
388 .*$\n? # But any other line
389 )*)
389 )*)
390 '''
390 '''
391
391
392 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
392 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
393 re.MULTILINE | re.VERBOSE)
393 re.MULTILINE | re.VERBOSE)
394
394
395 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
395 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
396 re.MULTILINE | re.VERBOSE)
396 re.MULTILINE | re.VERBOSE)
397
397
398 def ip2py(self,source):
398 def ip2py(self,source):
399 """Convert input IPython source into valid Python."""
399 """Convert input IPython source into valid Python."""
400 out = []
400 out = []
401 newline = out.append
401 newline = out.append
402 for lnum,line in enumerate(source.splitlines()):
402 for lnum,line in enumerate(source.splitlines()):
403 newline(_ip.IP.prefilter(line,lnum>0))
403 newline(_ip.IP.prefilter(line,lnum>0))
404 newline('') # ensure a closing newline, needed by doctest
404 newline('') # ensure a closing newline, needed by doctest
405 #print "PYSRC:", '\n'.join(out) # dbg
405 #print "PYSRC:", '\n'.join(out) # dbg
406 return '\n'.join(out)
406 return '\n'.join(out)
407
407
408 def parse(self, string, name='<string>'):
408 def parse(self, string, name='<string>'):
409 """
409 """
410 Divide the given string into examples and intervening text,
410 Divide the given string into examples and intervening text,
411 and return them as a list of alternating Examples and strings.
411 and return them as a list of alternating Examples and strings.
412 Line numbers for the Examples are 0-based. The optional
412 Line numbers for the Examples are 0-based. The optional
413 argument `name` is a name identifying this string, and is only
413 argument `name` is a name identifying this string, and is only
414 used for error messages.
414 used for error messages.
415 """
415 """
416
416
417 #print 'Parse string:\n',string # dbg
417 #print 'Parse string:\n',string # dbg
418
418
419 string = string.expandtabs()
419 string = string.expandtabs()
420 # If all lines begin with the same indentation, then strip it.
420 # If all lines begin with the same indentation, then strip it.
421 min_indent = self._min_indent(string)
421 min_indent = self._min_indent(string)
422 if min_indent > 0:
422 if min_indent > 0:
423 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
423 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
424
424
425 output = []
425 output = []
426 charno, lineno = 0, 0
426 charno, lineno = 0, 0
427
427
428 # Whether to convert the input from ipython to python syntax
428 # Whether to convert the input from ipython to python syntax
429 ip2py = False
429 ip2py = False
430 # Find all doctest examples in the string. First, try them as Python
430 # Find all doctest examples in the string. First, try them as Python
431 # examples, then as IPython ones
431 # examples, then as IPython ones
432 terms = list(self._EXAMPLE_RE_PY.finditer(string))
432 terms = list(self._EXAMPLE_RE_PY.finditer(string))
433 if terms:
433 if terms:
434 # Normal Python example
434 # Normal Python example
435 #print '-'*70 # dbg
435 #print '-'*70 # dbg
436 #print 'PyExample, Source:\n',string # dbg
436 #print 'PyExample, Source:\n',string # dbg
437 #print '-'*70 # dbg
437 #print '-'*70 # dbg
438 Example = doctest.Example
438 Example = doctest.Example
439 else:
439 else:
440 # It's an ipython example. Note that IPExamples are run
440 # It's an ipython example. Note that IPExamples are run
441 # in-process, so their syntax must be turned into valid python.
441 # in-process, so their syntax must be turned into valid python.
442 # IPExternalExamples are run out-of-process (via pexpect) so they
442 # IPExternalExamples are run out-of-process (via pexpect) so they
443 # don't need any filtering (a real ipython will be executing them).
443 # don't need any filtering (a real ipython will be executing them).
444 terms = list(self._EXAMPLE_RE_IP.finditer(string))
444 terms = list(self._EXAMPLE_RE_IP.finditer(string))
445 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
445 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
446 #print '-'*70 # dbg
446 #print '-'*70 # dbg
447 #print 'IPExternalExample, Source:\n',string # dbg
447 #print 'IPExternalExample, Source:\n',string # dbg
448 #print '-'*70 # dbg
448 #print '-'*70 # dbg
449 Example = IPExternalExample
449 Example = IPExternalExample
450 else:
450 else:
451 #print '-'*70 # dbg
451 #print '-'*70 # dbg
452 #print 'IPExample, Source:\n',string # dbg
452 #print 'IPExample, Source:\n',string # dbg
453 #print '-'*70 # dbg
453 #print '-'*70 # dbg
454 Example = IPExample
454 Example = IPExample
455 ip2py = True
455 ip2py = True
456
456
457 for m in terms:
457 for m in terms:
458 # Add the pre-example text to `output`.
458 # Add the pre-example text to `output`.
459 output.append(string[charno:m.start()])
459 output.append(string[charno:m.start()])
460 # Update lineno (lines before this example)
460 # Update lineno (lines before this example)
461 lineno += string.count('\n', charno, m.start())
461 lineno += string.count('\n', charno, m.start())
462 # Extract info from the regexp match.
462 # Extract info from the regexp match.
463 (source, options, want, exc_msg) = \
463 (source, options, want, exc_msg) = \
464 self._parse_example(m, name, lineno,ip2py)
464 self._parse_example(m, name, lineno,ip2py)
465 if Example is IPExternalExample:
465 if Example is IPExternalExample:
466 options[doctest.NORMALIZE_WHITESPACE] = True
466 options[doctest.NORMALIZE_WHITESPACE] = True
467 want += '\n'
467 want += '\n'
468 # Create an Example, and add it to the list.
468 # Create an Example, and add it to the list.
469 if not self._IS_BLANK_OR_COMMENT(source):
469 if not self._IS_BLANK_OR_COMMENT(source):
470 #print 'Example source:', source # dbg
470 #print 'Example source:', source # dbg
471 output.append(Example(source, want, exc_msg,
471 output.append(Example(source, want, exc_msg,
472 lineno=lineno,
472 lineno=lineno,
473 indent=min_indent+len(m.group('indent')),
473 indent=min_indent+len(m.group('indent')),
474 options=options))
474 options=options))
475 # Update lineno (lines inside this example)
475 # Update lineno (lines inside this example)
476 lineno += string.count('\n', m.start(), m.end())
476 lineno += string.count('\n', m.start(), m.end())
477 # Update charno.
477 # Update charno.
478 charno = m.end()
478 charno = m.end()
479 # Add any remaining post-example text to `output`.
479 # Add any remaining post-example text to `output`.
480 output.append(string[charno:])
480 output.append(string[charno:])
481 return output
481 return output
482
482
483 def _parse_example(self, m, name, lineno,ip2py=False):
483 def _parse_example(self, m, name, lineno,ip2py=False):
484 """
484 """
485 Given a regular expression match from `_EXAMPLE_RE` (`m`),
485 Given a regular expression match from `_EXAMPLE_RE` (`m`),
486 return a pair `(source, want)`, where `source` is the matched
486 return a pair `(source, want)`, where `source` is the matched
487 example's source code (with prompts and indentation stripped);
487 example's source code (with prompts and indentation stripped);
488 and `want` is the example's expected output (with indentation
488 and `want` is the example's expected output (with indentation
489 stripped).
489 stripped).
490
490
491 `name` is the string's name, and `lineno` is the line number
491 `name` is the string's name, and `lineno` is the line number
492 where the example starts; both are used for error messages.
492 where the example starts; both are used for error messages.
493
493
494 Optional:
494 Optional:
495 `ip2py`: if true, filter the input via IPython to convert the syntax
495 `ip2py`: if true, filter the input via IPython to convert the syntax
496 into valid python.
496 into valid python.
497 """
497 """
498
498
499 # Get the example's indentation level.
499 # Get the example's indentation level.
500 indent = len(m.group('indent'))
500 indent = len(m.group('indent'))
501
501
502 # Divide source into lines; check that they're properly
502 # Divide source into lines; check that they're properly
503 # indented; and then strip their indentation & prompts.
503 # indented; and then strip their indentation & prompts.
504 source_lines = m.group('source').split('\n')
504 source_lines = m.group('source').split('\n')
505
505
506 # We're using variable-length input prompts
506 # We're using variable-length input prompts
507 ps1 = m.group('ps1')
507 ps1 = m.group('ps1')
508 ps2 = m.group('ps2')
508 ps2 = m.group('ps2')
509 ps1_len = len(ps1)
509 ps1_len = len(ps1)
510
510
511 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
511 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
512 if ps2:
512 if ps2:
513 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
513 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
514
514
515 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
515 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
516
516
517 if ip2py:
517 if ip2py:
518 # Convert source input from IPython into valid Python syntax
518 # Convert source input from IPython into valid Python syntax
519 source = self.ip2py(source)
519 source = self.ip2py(source)
520
520
521 # Divide want into lines; check that it's properly indented; and
521 # Divide want into lines; check that it's properly indented; and
522 # then strip the indentation. Spaces before the last newline should
522 # then strip the indentation. Spaces before the last newline should
523 # be preserved, so plain rstrip() isn't good enough.
523 # be preserved, so plain rstrip() isn't good enough.
524 want = m.group('want')
524 want = m.group('want')
525 want_lines = want.split('\n')
525 want_lines = want.split('\n')
526 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
526 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
527 del want_lines[-1] # forget final newline & spaces after it
527 del want_lines[-1] # forget final newline & spaces after it
528 self._check_prefix(want_lines, ' '*indent, name,
528 self._check_prefix(want_lines, ' '*indent, name,
529 lineno + len(source_lines))
529 lineno + len(source_lines))
530
530
531 # Remove ipython output prompt that might be present in the first line
531 # Remove ipython output prompt that might be present in the first line
532 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
532 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
533
533
534 want = '\n'.join([wl[indent:] for wl in want_lines])
534 want = '\n'.join([wl[indent:] for wl in want_lines])
535
535
536 # If `want` contains a traceback message, then extract it.
536 # If `want` contains a traceback message, then extract it.
537 m = self._EXCEPTION_RE.match(want)
537 m = self._EXCEPTION_RE.match(want)
538 if m:
538 if m:
539 exc_msg = m.group('msg')
539 exc_msg = m.group('msg')
540 else:
540 else:
541 exc_msg = None
541 exc_msg = None
542
542
543 # Extract options from the source.
543 # Extract options from the source.
544 options = self._find_options(source, name, lineno)
544 options = self._find_options(source, name, lineno)
545
545
546 return source, options, want, exc_msg
546 return source, options, want, exc_msg
547
547
548 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
548 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
549 """
549 """
550 Given the lines of a source string (including prompts and
550 Given the lines of a source string (including prompts and
551 leading indentation), check to make sure that every prompt is
551 leading indentation), check to make sure that every prompt is
552 followed by a space character. If any line is not followed by
552 followed by a space character. If any line is not followed by
553 a space character, then raise ValueError.
553 a space character, then raise ValueError.
554
554
555 Note: IPython-modified version which takes the input prompt length as a
555 Note: IPython-modified version which takes the input prompt length as a
556 parameter, so that prompts of variable length can be dealt with.
556 parameter, so that prompts of variable length can be dealt with.
557 """
557 """
558 space_idx = indent+ps1_len
558 space_idx = indent+ps1_len
559 min_len = space_idx+1
559 min_len = space_idx+1
560 for i, line in enumerate(lines):
560 for i, line in enumerate(lines):
561 if len(line) >= min_len and line[space_idx] != ' ':
561 if len(line) >= min_len and line[space_idx] != ' ':
562 raise ValueError('line %r of the docstring for %s '
562 raise ValueError('line %r of the docstring for %s '
563 'lacks blank after %s: %r' %
563 'lacks blank after %s: %r' %
564 (lineno+i+1, name,
564 (lineno+i+1, name,
565 line[indent:space_idx], line))
565 line[indent:space_idx], line))
566
566
567
567
568 SKIP = doctest.register_optionflag('SKIP')
568 SKIP = doctest.register_optionflag('SKIP')
569
569
570
570
571 class IPDocTestRunner(doctest.DocTestRunner):
571 class IPDocTestRunner(doctest.DocTestRunner,object):
572
572 """Test runner that synchronizes the IPython namespace with test globals.
573 # Unfortunately, doctest uses a private method (__run) for the actual run
573 """
574 # execution, so we can't cleanly override just that part. Instead, we have
575 # to copy/paste the entire run() implementation so we can call our own
576 # customized runner.
577
578 #/////////////////////////////////////////////////////////////////
579 # DocTest Running
580 #/////////////////////////////////////////////////////////////////
581
582 __LINECACHE_FILENAME_RE = re.compile(r'<doctest '
583 r'(?P<name>[\w\.]+)'
584 r'\[(?P<examplenum>\d+)\]>$')
585
586 def __patched_linecache_getlines(self, filename, module_globals=None):
587 m = self.__LINECACHE_FILENAME_RE.match(filename)
588 if m and m.group('name') == self.test.name:
589 example = self.test.examples[int(m.group('examplenum'))]
590 return example.source.splitlines(True)
591 else:
592 return self.save_linecache_getlines(filename, module_globals)
593
594
595 def _run_ip(self, test, compileflags, out):
596 """
597 Run the examples in `test`. Write the outcome of each example
598 with one of the `DocTestRunner.report_*` methods, using the
599 writer function `out`. `compileflags` is the set of compiler
600 flags that should be used to execute examples. Return a tuple
601 `(f, t)`, where `t` is the number of examples tried, and `f`
602 is the number of examples that failed. The examples are run
603 in the namespace `test.globs`.
604 """
605
606 #print 'Custom ip runner! __run' # dbg
607
608 # Keep track of the number of failures and tries.
609 failures = tries = 0
610
611 # Save the option flags (since option directives can be used
612 # to modify them).
613 original_optionflags = self.optionflags
614
615 SUCCESS, FAILURE, BOOM = range(3) # `outcome` state
616
617 check = self._checker.check_output
618
619 # Process each example.
620 for examplenum, example in enumerate(test.examples):
621
622 # If REPORT_ONLY_FIRST_FAILURE is set, then supress
623 # reporting after the first failure.
624 quiet = (self.optionflags & REPORT_ONLY_FIRST_FAILURE and
625 failures > 0)
626
627 # Merge in the example's options.
628 self.optionflags = original_optionflags
629 if example.options:
630 for (optionflag, val) in example.options.items():
631 if val:
632 self.optionflags |= optionflag
633 else:
634 self.optionflags &= ~optionflag
635
636 # If 'SKIP' is set, then skip this example.
637 if self.optionflags & SKIP:
638 continue
639
640 # Record that we started this example.
641 tries += 1
642 if not quiet:
643 self.report_start(out, test, example)
644
645 # Use a special filename for compile(), so we can retrieve
646 # the source code during interactive debugging (see
647 # __patched_linecache_getlines).
648 filename = '<doctest %s[%d]>' % (test.name, examplenum)
649
650 # Run the example in the given context (globs), and record
651 # any exception that gets raised. (But don't intercept
652 # keyboard interrupts.)
653 try:
654 # Don't blink! This is where the user's code gets run.
655
656 # Hack: ipython needs access to the execution context of the
657 # example, so that it can propagate user variables loaded by
658 # %run into test.globs. We put them here into our modified
659 # %run as a function attribute. Our new %run will then only
660 # make the namespace update when called (rather than
661 # unconconditionally updating test.globs here for all examples,
662 # most of which won't be calling %run anyway).
663 _run_ns_sync.test_globs = test.globs
664
665 exec compile(example.source, filename, "single",
666 compileflags, 1) in test.globs
667 self.debugger.set_continue() # ==== Example Finished ====
668 exception = None
669 except KeyboardInterrupt:
670 raise
671 except:
672 exception = sys.exc_info()
673 self.debugger.set_continue() # ==== Example Finished ====
674
675 got = self._fakeout.getvalue() # the actual output
676 self._fakeout.truncate(0)
677 outcome = FAILURE # guilty until proved innocent or insane
678
679 # If the example executed without raising any exceptions,
680 # verify its output.
681 if exception is None:
682 if check(example.want, got, self.optionflags):
683 outcome = SUCCESS
684
685 # The example raised an exception: check if it was expected.
686 else:
687 exc_info = sys.exc_info()
688 exc_msg = traceback.format_exception_only(*exc_info[:2])[-1]
689 if not quiet:
690 got += _exception_traceback(exc_info)
691
692 # If `example.exc_msg` is None, then we weren't expecting
693 # an exception.
694 if example.exc_msg is None:
695 outcome = BOOM
696
697 # We expected an exception: see whether it matches.
698 elif check(example.exc_msg, exc_msg, self.optionflags):
699 outcome = SUCCESS
700
701 # Another chance if they didn't care about the detail.
702 elif self.optionflags & IGNORE_EXCEPTION_DETAIL:
703 m1 = re.match(r'[^:]*:', example.exc_msg)
704 m2 = re.match(r'[^:]*:', exc_msg)
705 if m1 and m2 and check(m1.group(0), m2.group(0),
706 self.optionflags):
707 outcome = SUCCESS
708
709 # Report the outcome.
710 if outcome is SUCCESS:
711 if not quiet:
712 self.report_success(out, test, example, got)
713 elif outcome is FAILURE:
714 if not quiet:
715 self.report_failure(out, test, example, got)
716 failures += 1
717 elif outcome is BOOM:
718 if not quiet:
719 self.report_unexpected_exception(out, test, example,
720 exc_info)
721 failures += 1
722 else:
723 assert False, ("unknown outcome", outcome)
724
725 # Restore the option flags (in case they were modified)
726 self.optionflags = original_optionflags
727
728 # Record and return the number of failures and tries.
729
730 # Hack to access a parent private method by working around Python's
731 # name mangling (which is fortunately simple).
732 #self.__record_outcome(test, failures, tries)
733 doctest.DocTestRunner._DocTestRunner__record_outcome(self,test,
734 failures, tries)
735
736 return failures, tries
737
738
739 # Unfortunately doctest has chosen to implement a couple of key methods as
740 # private (__run, in particular). We are forced to copy the entire run
741 # method here just so we can override that one. Ugh.
742
574
743 def run(self, test, compileflags=None, out=None, clear_globs=True):
575 def run(self, test, compileflags=None, out=None, clear_globs=True):
744 """
745 Run the examples in `test`, and display the results using the
746 writer function `out`.
747
748 The examples are run in the namespace `test.globs`. If
749 `clear_globs` is true (the default), then this namespace will
750 be cleared after the test runs, to help with garbage
751 collection. If you would like to examine the namespace after
752 the test completes, then use `clear_globs=False`.
753
754 `compileflags` gives the set of flags that should be used by
755 the Python compiler when running the examples. If not
756 specified, then it will default to the set of future-import
757 flags that apply to `globs`.
758
759 The output of each example is checked using
760 `DocTestRunner.check_output`, and the results are formatted by
761 the `DocTestRunner.report_*` methods.
762 """
763 #print 'Custom ip runner!' # dbg
764
765 self.test = test
766
767 if compileflags is None:
768 compileflags = _extract_future_flags(test.globs)
769
770 save_stdout = sys.stdout
771 if out is None:
772 out = save_stdout.write
773 sys.stdout = self._fakeout
774
775 # Patch pdb.set_trace to restore sys.stdout during interactive
776 # debugging (so it's not still redirected to self._fakeout).
777 # Note that the interactive output will go to *our*
778 # save_stdout, even if that's not the real sys.stdout; this
779 # allows us to write test cases for the set_trace behavior.
780 save_set_trace = pdb.set_trace
781 self.debugger = _OutputRedirectingPdb(save_stdout)
782 self.debugger.reset()
783 pdb.set_trace = self.debugger.set_trace
784
785 # Patch linecache.getlines, so we can see the example's source
786 # when we're inside the debugger.
787 self.save_linecache_getlines = linecache.getlines
788 linecache.getlines = self.__patched_linecache_getlines
789
576
790 try:
577 # Hack: ipython needs access to the execution context of the example,
791 # Hack to access a parent private method by working around Python's
578 # so that it can propagate user variables loaded by %run into
792 # name mangling (which is fortunately simple).
579 # test.globs. We put them here into our modified %run as a function
793 #return self.__run(test, compileflags, out)
580 # attribute. Our new %run will then only make the namespace update
794 return self._run_ip(test, compileflags, out)
581 # when called (rather than unconconditionally updating test.globs here
795 #return doctest.DocTestRunner._DocTestRunner__run(self,test,
582 # for all examples, most of which won't be calling %run anyway).
796 # compileflags, out)
583 _run_ns_sync.test_globs = test.globs
797 finally:
584
798 _ip.user_ns.update(test.globs)
585 return super(IPDocTestRunner,self).run(test,
799 sys.stdout = save_stdout
586 compileflags,out,clear_globs)
800 pdb.set_trace = save_set_trace
801 linecache.getlines = self.save_linecache_getlines
802 if clear_globs:
803 test.globs.clear()
804
587
805
588
806 class DocFileCase(doctest.DocFileCase):
589 class DocFileCase(doctest.DocFileCase):
807 """Overrides to provide filename
590 """Overrides to provide filename
808 """
591 """
809 def address(self):
592 def address(self):
810 return (self._dt_test.filename, None, None)
593 return (self._dt_test.filename, None, None)
811
594
812
595
813 class ExtensionDoctest(doctests.Doctest):
596 class ExtensionDoctest(doctests.Doctest):
814 """Nose Plugin that supports doctests in extension modules.
597 """Nose Plugin that supports doctests in extension modules.
815 """
598 """
816 name = 'extdoctest' # call nosetests with --with-extdoctest
599 name = 'extdoctest' # call nosetests with --with-extdoctest
817 enabled = True
600 enabled = True
818
601
819 def options(self, parser, env=os.environ):
602 def options(self, parser, env=os.environ):
820 Plugin.options(self, parser, env)
603 Plugin.options(self, parser, env)
821
604
822 def configure(self, options, config):
605 def configure(self, options, config):
823 Plugin.configure(self, options, config)
606 Plugin.configure(self, options, config)
824 self.doctest_tests = options.doctest_tests
607 self.doctest_tests = options.doctest_tests
825 self.extension = tolist(options.doctestExtension)
608 self.extension = tolist(options.doctestExtension)
826 self.finder = DocTestFinder()
609 self.finder = DocTestFinder()
827 self.parser = doctest.DocTestParser()
610 self.parser = doctest.DocTestParser()
828 self.globs = None
611 self.globs = None
829 self.extraglobs = None
612 self.extraglobs = None
830
613
831 def loadTestsFromExtensionModule(self,filename):
614 def loadTestsFromExtensionModule(self,filename):
832 bpath,mod = os.path.split(filename)
615 bpath,mod = os.path.split(filename)
833 modname = os.path.splitext(mod)[0]
616 modname = os.path.splitext(mod)[0]
834 try:
617 try:
835 sys.path.append(bpath)
618 sys.path.append(bpath)
836 module = __import__(modname)
619 module = __import__(modname)
837 tests = list(self.loadTestsFromModule(module))
620 tests = list(self.loadTestsFromModule(module))
838 finally:
621 finally:
839 sys.path.pop()
622 sys.path.pop()
840 return tests
623 return tests
841
624
842 # NOTE: the method below is almost a copy of the original one in nose, with
625 # NOTE: the method below is almost a copy of the original one in nose, with
843 # a few modifications to control output checking.
626 # a few modifications to control output checking.
844
627
845 def loadTestsFromModule(self, module):
628 def loadTestsFromModule(self, module):
846 #print 'lTM',module # dbg
629 #print 'lTM',module # dbg
847
630
848 if not self.matches(module.__name__):
631 if not self.matches(module.__name__):
849 log.debug("Doctest doesn't want module %s", module)
632 log.debug("Doctest doesn't want module %s", module)
850 return
633 return
851
634
852 ## try:
635 ## try:
853 ## print 'Globs:',self.globs.keys() # dbg
636 ## print 'Globs:',self.globs.keys() # dbg
854 ## except:
637 ## except:
855 ## pass
638 ## pass
856
639
857 tests = self.finder.find(module,globs=self.globs,
640 tests = self.finder.find(module,globs=self.globs,
858 extraglobs=self.extraglobs)
641 extraglobs=self.extraglobs)
859 if not tests:
642 if not tests:
860 return
643 return
861 tests.sort()
644 tests.sort()
862 module_file = module.__file__
645 module_file = module.__file__
863 if module_file[-4:] in ('.pyc', '.pyo'):
646 if module_file[-4:] in ('.pyc', '.pyo'):
864 module_file = module_file[:-1]
647 module_file = module_file[:-1]
865 for test in tests:
648 for test in tests:
866 if not test.examples:
649 if not test.examples:
867 continue
650 continue
868 if not test.filename:
651 if not test.filename:
869 test.filename = module_file
652 test.filename = module_file
870
653
871 # xxx - checker and options may be ok instantiated once outside loop
654 # xxx - checker and options may be ok instantiated once outside loop
872
655
873 # always use whitespace and ellipsis options
656 # always use whitespace and ellipsis options
874 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
657 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
875 checker = IPDoctestOutputChecker()
658 checker = IPDoctestOutputChecker()
876
659
877 yield DocTestCase(test,
660 yield DocTestCase(test,
878 optionflags=optionflags,
661 optionflags=optionflags,
879 checker=checker)
662 checker=checker)
880
663
881 def loadTestsFromFile(self, filename):
664 def loadTestsFromFile(self, filename):
882 #print 'lTF',filename # dbg
665 #print 'lTF',filename # dbg
883
666
884 if is_extension_module(filename):
667 if is_extension_module(filename):
885 for t in self.loadTestsFromExtensionModule(filename):
668 for t in self.loadTestsFromExtensionModule(filename):
886 yield t
669 yield t
887 else:
670 else:
888 if self.extension and anyp(filename.endswith, self.extension):
671 if self.extension and anyp(filename.endswith, self.extension):
889 name = os.path.basename(filename)
672 name = os.path.basename(filename)
890 dh = open(filename)
673 dh = open(filename)
891 try:
674 try:
892 doc = dh.read()
675 doc = dh.read()
893 finally:
676 finally:
894 dh.close()
677 dh.close()
895 test = self.parser.get_doctest(
678 test = self.parser.get_doctest(
896 doc, globs={'__file__': filename}, name=name,
679 doc, globs={'__file__': filename}, name=name,
897 filename=filename, lineno=0)
680 filename=filename, lineno=0)
898 if test.examples:
681 if test.examples:
899 #print 'FileCase:',test.examples # dbg
682 #print 'FileCase:',test.examples # dbg
900 yield DocFileCase(test)
683 yield DocFileCase(test)
901 else:
684 else:
902 yield False # no tests to load
685 yield False # no tests to load
903
686
904 def wantFile(self,filename):
687 def wantFile(self,filename):
905 """Return whether the given filename should be scanned for tests.
688 """Return whether the given filename should be scanned for tests.
906
689
907 Modified version that accepts extension modules as valid containers for
690 Modified version that accepts extension modules as valid containers for
908 doctests.
691 doctests.
909 """
692 """
910 #print 'Filename:',filename # dbg
693 #print 'Filename:',filename # dbg
911
694
912 # temporarily hardcoded list, will move to driver later
695 # temporarily hardcoded list, will move to driver later
913 exclude = ['IPython/external/',
696 exclude = ['IPython/external/',
914 'IPython/Extensions/ipy_',
697 'IPython/Extensions/ipy_',
915 'IPython/platutils_win32',
698 'IPython/platutils_win32',
916 'IPython/frontend/cocoa',
699 'IPython/frontend/cocoa',
917 'IPython_doctest_plugin',
700 'IPython_doctest_plugin',
918 'IPython/Gnuplot',
701 'IPython/Gnuplot',
919 'IPython/Extensions/PhysicalQIn']
702 'IPython/Extensions/PhysicalQIn']
920
703
921 for fex in exclude:
704 for fex in exclude:
922 if fex in filename: # substring
705 if fex in filename: # substring
923 #print '###>>> SKIP:',filename # dbg
706 #print '###>>> SKIP:',filename # dbg
924 return False
707 return False
925
708
926 if is_extension_module(filename):
709 if is_extension_module(filename):
927 return True
710 return True
928 else:
711 else:
929 return doctests.Doctest.wantFile(self,filename)
712 return doctests.Doctest.wantFile(self,filename)
930
713
931
714
932 class IPythonDoctest(ExtensionDoctest):
715 class IPythonDoctest(ExtensionDoctest):
933 """Nose Plugin that supports doctests in extension modules.
716 """Nose Plugin that supports doctests in extension modules.
934 """
717 """
935 name = 'ipdoctest' # call nosetests with --with-ipdoctest
718 name = 'ipdoctest' # call nosetests with --with-ipdoctest
936 enabled = True
719 enabled = True
937
720
938 def configure(self, options, config):
721 def configure(self, options, config):
939
722
940 Plugin.configure(self, options, config)
723 Plugin.configure(self, options, config)
941 self.doctest_tests = options.doctest_tests
724 self.doctest_tests = options.doctest_tests
942 self.extension = tolist(options.doctestExtension)
725 self.extension = tolist(options.doctestExtension)
943 self.parser = IPDocTestParser()
726 self.parser = IPDocTestParser()
944 self.finder = DocTestFinder(parser=self.parser)
727 self.finder = DocTestFinder(parser=self.parser)
945
728
946 # XXX - we need to run in the ipython user's namespace, but doing so is
729 # XXX - we need to run in the ipython user's namespace, but doing so is
947 # breaking normal doctests!
730 # breaking normal doctests!
948
731
949 #self.globs = _ip.user_ns
732 #self.globs = _ip.user_ns
950 self.globs = None
733 self.globs = None
951
734
952 self.extraglobs = None
735 self.extraglobs = None
953
736
954 # Use a specially modified test runner that is IPython-aware
737 # Use a specially modified test runner that is IPython-aware
955 self.iprunner = None
738 self.iprunner = None
General Comments 0
You need to be logged in to leave comments. Login now