##// END OF EJS Templates
Adjust regexps for random tests
Fernando Perez -
Show More
@@ -1,753 +1,753 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
81
82 # XXX - Hack to modify the %run command so we can sync the user's namespace
82 # XXX - Hack to modify the %run command so we can sync the user's namespace
83 # with the test globals. Once we move over to a clean magic system, this will
83 # with the test globals. Once we move over to a clean magic system, this will
84 # be done with much less ugliness.
84 # be done with much less ugliness.
85
85
86 def _run_ns_sync(self,arg_s,runner=None):
86 def _run_ns_sync(self,arg_s,runner=None):
87 """Modified version of %run that syncs testing namespaces.
87 """Modified version of %run that syncs testing namespaces.
88
88
89 This is strictly needed for running doctests that call %run.
89 This is strictly needed for running doctests that call %run.
90 """
90 """
91
91
92 out = _ip.IP.magic_run_ori(arg_s,runner)
92 out = _ip.IP.magic_run_ori(arg_s,runner)
93 _run_ns_sync.test_globs.update(_ip.user_ns)
93 _run_ns_sync.test_globs.update(_ip.user_ns)
94 return out
94 return out
95
95
96
96
97 def start_ipython():
97 def start_ipython():
98 """Start a global IPython shell, which we need for IPython-specific syntax.
98 """Start a global IPython shell, which we need for IPython-specific syntax.
99 """
99 """
100 import new
100 import new
101
101
102 import IPython
102 import IPython
103
103
104 def xsys(cmd):
104 def xsys(cmd):
105 """Execute a command and print its output.
105 """Execute a command and print its output.
106
106
107 This is just a convenience function to replace the IPython system call
107 This is just a convenience function to replace the IPython system call
108 with one that is more doctest-friendly.
108 with one that is more doctest-friendly.
109 """
109 """
110 cmd = _ip.IP.var_expand(cmd,depth=1)
110 cmd = _ip.IP.var_expand(cmd,depth=1)
111 sys.stdout.write(commands.getoutput(cmd))
111 sys.stdout.write(commands.getoutput(cmd))
112 sys.stdout.flush()
112 sys.stdout.flush()
113
113
114 # Store certain global objects that IPython modifies
114 # Store certain global objects that IPython modifies
115 _displayhook = sys.displayhook
115 _displayhook = sys.displayhook
116 _excepthook = sys.excepthook
116 _excepthook = sys.excepthook
117 _main = sys.modules.get('__main__')
117 _main = sys.modules.get('__main__')
118
118
119 # Start IPython instance. We customize it to start with minimal frills.
119 # Start IPython instance. We customize it to start with minimal frills.
120 IPython.Shell.IPShell(['--classic','--noterm_title'])
120 IPython.Shell.IPShell(['--classic','--noterm_title'])
121
121
122 # Deactivate the various python system hooks added by ipython for
122 # Deactivate the various python system hooks added by ipython for
123 # interactive convenience so we don't confuse the doctest system
123 # interactive convenience so we don't confuse the doctest system
124 sys.modules['__main__'] = _main
124 sys.modules['__main__'] = _main
125 sys.displayhook = _displayhook
125 sys.displayhook = _displayhook
126 sys.excepthook = _excepthook
126 sys.excepthook = _excepthook
127
127
128 # So that ipython magics and aliases can be doctested (they work by making
128 # So that ipython magics and aliases can be doctested (they work by making
129 # a call into a global _ip object)
129 # a call into a global _ip object)
130 _ip = IPython.ipapi.get()
130 _ip = IPython.ipapi.get()
131 __builtin__._ip = _ip
131 __builtin__._ip = _ip
132
132
133 # Modify the IPython system call with one that uses getoutput, so that we
133 # Modify the IPython system call with one that uses getoutput, so that we
134 # can capture subcommands and print them to Python's stdout, otherwise the
134 # can capture subcommands and print them to Python's stdout, otherwise the
135 # doctest machinery would miss them.
135 # doctest machinery would miss them.
136 _ip.system = xsys
136 _ip.system = xsys
137
137
138 # Also patch our %run function in.
138 # Also patch our %run function in.
139 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
139 im = new.instancemethod(_run_ns_sync,_ip.IP, _ip.IP.__class__)
140 _ip.IP.magic_run_ori = _ip.IP.magic_run
140 _ip.IP.magic_run_ori = _ip.IP.magic_run
141 _ip.IP.magic_run = im
141 _ip.IP.magic_run = im
142
142
143 # The start call MUST be made here. I'm not sure yet why it doesn't work if
143 # The start call MUST be made here. I'm not sure yet why it doesn't work if
144 # it is made later, at plugin initialization time, but in all my tests, that's
144 # it is made later, at plugin initialization time, but in all my tests, that's
145 # the case.
145 # the case.
146 start_ipython()
146 start_ipython()
147
147
148 # *** END HACK ***
148 # *** END HACK ***
149 ###########################################################################
149 ###########################################################################
150
150
151 # Classes and functions
151 # Classes and functions
152
152
153 def is_extension_module(filename):
153 def is_extension_module(filename):
154 """Return whether the given filename is an extension module.
154 """Return whether the given filename is an extension module.
155
155
156 This simply checks that the extension is either .so or .pyd.
156 This simply checks that the extension is either .so or .pyd.
157 """
157 """
158 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
158 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
159
159
160
160
161 class nodoc(object):
161 class nodoc(object):
162 def __init__(self,obj):
162 def __init__(self,obj):
163 self.obj = obj
163 self.obj = obj
164
164
165 def __getattribute__(self,key):
165 def __getattribute__(self,key):
166 if key == '__doc__':
166 if key == '__doc__':
167 return None
167 return None
168 else:
168 else:
169 return getattr(object.__getattribute__(self,'obj'),key)
169 return getattr(object.__getattribute__(self,'obj'),key)
170
170
171 # Modified version of the one in the stdlib, that fixes a python bug (doctests
171 # Modified version of the one in the stdlib, that fixes a python bug (doctests
172 # not found in extension modules, http://bugs.python.org/issue3158)
172 # not found in extension modules, http://bugs.python.org/issue3158)
173 class DocTestFinder(doctest.DocTestFinder):
173 class DocTestFinder(doctest.DocTestFinder):
174
174
175 def _from_module(self, module, object):
175 def _from_module(self, module, object):
176 """
176 """
177 Return true if the given object is defined in the given
177 Return true if the given object is defined in the given
178 module.
178 module.
179 """
179 """
180 if module is None:
180 if module is None:
181 return True
181 return True
182 elif inspect.isfunction(object):
182 elif inspect.isfunction(object):
183 return module.__dict__ is object.func_globals
183 return module.__dict__ is object.func_globals
184 elif inspect.isbuiltin(object):
184 elif inspect.isbuiltin(object):
185 return module.__name__ == object.__module__
185 return module.__name__ == object.__module__
186 elif inspect.isclass(object):
186 elif inspect.isclass(object):
187 return module.__name__ == object.__module__
187 return module.__name__ == object.__module__
188 elif inspect.ismethod(object):
188 elif inspect.ismethod(object):
189 # This one may be a bug in cython that fails to correctly set the
189 # This one may be a bug in cython that fails to correctly set the
190 # __module__ attribute of methods, but since the same error is easy
190 # __module__ attribute of methods, but since the same error is easy
191 # to make by extension code writers, having this safety in place
191 # to make by extension code writers, having this safety in place
192 # isn't such a bad idea
192 # isn't such a bad idea
193 return module.__name__ == object.im_class.__module__
193 return module.__name__ == object.im_class.__module__
194 elif inspect.getmodule(object) is not None:
194 elif inspect.getmodule(object) is not None:
195 return module is inspect.getmodule(object)
195 return module is inspect.getmodule(object)
196 elif hasattr(object, '__module__'):
196 elif hasattr(object, '__module__'):
197 return module.__name__ == object.__module__
197 return module.__name__ == object.__module__
198 elif isinstance(object, property):
198 elif isinstance(object, property):
199 return True # [XX] no way not be sure.
199 return True # [XX] no way not be sure.
200 else:
200 else:
201 raise ValueError("object must be a class or function")
201 raise ValueError("object must be a class or function")
202
202
203 def _find(self, tests, obj, name, module, source_lines, globs, seen):
203 def _find(self, tests, obj, name, module, source_lines, globs, seen):
204 """
204 """
205 Find tests for the given object and any contained objects, and
205 Find tests for the given object and any contained objects, and
206 add them to `tests`.
206 add them to `tests`.
207 """
207 """
208
208
209 if hasattr(obj,"skip_doctest"):
209 if hasattr(obj,"skip_doctest"):
210 #print 'SKIPPING DOCTEST FOR:',obj # dbg
210 #print 'SKIPPING DOCTEST FOR:',obj # dbg
211 obj = nodoc(obj)
211 obj = nodoc(obj)
212
212
213 doctest.DocTestFinder._find(self,tests, obj, name, module,
213 doctest.DocTestFinder._find(self,tests, obj, name, module,
214 source_lines, globs, seen)
214 source_lines, globs, seen)
215
215
216 # Below we re-run pieces of the above method with manual modifications,
216 # Below we re-run pieces of the above method with manual modifications,
217 # because the original code is buggy and fails to correctly identify
217 # because the original code is buggy and fails to correctly identify
218 # doctests in extension modules.
218 # doctests in extension modules.
219
219
220 # Local shorthands
220 # Local shorthands
221 from inspect import isroutine, isclass, ismodule
221 from inspect import isroutine, isclass, ismodule
222
222
223 # Look for tests in a module's contained objects.
223 # Look for tests in a module's contained objects.
224 if inspect.ismodule(obj) and self._recurse:
224 if inspect.ismodule(obj) and self._recurse:
225 for valname, val in obj.__dict__.items():
225 for valname, val in obj.__dict__.items():
226 valname1 = '%s.%s' % (name, valname)
226 valname1 = '%s.%s' % (name, valname)
227 if ( (isroutine(val) or isclass(val))
227 if ( (isroutine(val) or isclass(val))
228 and self._from_module(module, val) ):
228 and self._from_module(module, val) ):
229
229
230 self._find(tests, val, valname1, module, source_lines,
230 self._find(tests, val, valname1, module, source_lines,
231 globs, seen)
231 globs, seen)
232
232
233 # Look for tests in a class's contained objects.
233 # Look for tests in a class's contained objects.
234 if inspect.isclass(obj) and self._recurse:
234 if inspect.isclass(obj) and self._recurse:
235 #print 'RECURSE into class:',obj # dbg
235 #print 'RECURSE into class:',obj # dbg
236 for valname, val in obj.__dict__.items():
236 for valname, val in obj.__dict__.items():
237 # Special handling for staticmethod/classmethod.
237 # Special handling for staticmethod/classmethod.
238 if isinstance(val, staticmethod):
238 if isinstance(val, staticmethod):
239 val = getattr(obj, valname)
239 val = getattr(obj, valname)
240 if isinstance(val, classmethod):
240 if isinstance(val, classmethod):
241 val = getattr(obj, valname).im_func
241 val = getattr(obj, valname).im_func
242
242
243 # Recurse to methods, properties, and nested classes.
243 # Recurse to methods, properties, and nested classes.
244 if ((inspect.isfunction(val) or inspect.isclass(val) or
244 if ((inspect.isfunction(val) or inspect.isclass(val) or
245 inspect.ismethod(val) or
245 inspect.ismethod(val) or
246 isinstance(val, property)) and
246 isinstance(val, property)) and
247 self._from_module(module, val)):
247 self._from_module(module, val)):
248 valname = '%s.%s' % (name, valname)
248 valname = '%s.%s' % (name, valname)
249 self._find(tests, val, valname, module, source_lines,
249 self._find(tests, val, valname, module, source_lines,
250 globs, seen)
250 globs, seen)
251
251
252
252
253 class IPDoctestOutputChecker(doctest.OutputChecker):
253 class IPDoctestOutputChecker(doctest.OutputChecker):
254 """Second-chance checker with support for random tests.
254 """Second-chance checker with support for random tests.
255
255
256 If the default comparison doesn't pass, this checker looks in the expected
256 If the default comparison doesn't pass, this checker looks in the expected
257 output string for flags that tell us to ignore the output.
257 output string for flags that tell us to ignore the output.
258 """
258 """
259
259
260 random_re = re.compile(r'#\s*random')
260 random_re = re.compile(r'#\s*random\s+')
261
261
262 def check_output(self, want, got, optionflags):
262 def check_output(self, want, got, optionflags):
263 """Check output, accepting special markers embedded in the output.
263 """Check output, accepting special markers embedded in the output.
264
264
265 If the output didn't pass the default validation but the special string
265 If the output didn't pass the default validation but the special string
266 '#random' is included, we accept it."""
266 '#random' is included, we accept it."""
267
267
268 # Let the original tester verify first, in case people have valid tests
268 # Let the original tester verify first, in case people have valid tests
269 # that happen to have a comment saying '#random' embedded in.
269 # that happen to have a comment saying '#random' embedded in.
270 ret = doctest.OutputChecker.check_output(self, want, got,
270 ret = doctest.OutputChecker.check_output(self, want, got,
271 optionflags)
271 optionflags)
272 if not ret and self.random_re.search(want):
272 if not ret and self.random_re.search(want):
273 #print >> sys.stderr, 'RANDOM OK:',want # dbg
273 #print >> sys.stderr, 'RANDOM OK:',want # dbg
274 return True
274 return True
275
275
276 return ret
276 return ret
277
277
278
278
279 class DocTestCase(doctests.DocTestCase):
279 class DocTestCase(doctests.DocTestCase):
280 """Proxy for DocTestCase: provides an address() method that
280 """Proxy for DocTestCase: provides an address() method that
281 returns the correct address for the doctest case. Otherwise
281 returns the correct address for the doctest case. Otherwise
282 acts as a proxy to the test case. To provide hints for address(),
282 acts as a proxy to the test case. To provide hints for address(),
283 an obj may also be passed -- this will be used as the test object
283 an obj may also be passed -- this will be used as the test object
284 for purposes of determining the test address, if it is provided.
284 for purposes of determining the test address, if it is provided.
285 """
285 """
286
286
287 # Note: this method was taken from numpy's nosetester module.
287 # Note: this method was taken from numpy's nosetester module.
288
288
289 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
289 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
290 # its constructor that blocks non-default arguments from being passed
290 # its constructor that blocks non-default arguments from being passed
291 # down into doctest.DocTestCase
291 # down into doctest.DocTestCase
292
292
293 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
293 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
294 checker=None, obj=None, result_var='_'):
294 checker=None, obj=None, result_var='_'):
295 self._result_var = result_var
295 self._result_var = result_var
296 doctests.DocTestCase.__init__(self, test,
296 doctests.DocTestCase.__init__(self, test,
297 optionflags=optionflags,
297 optionflags=optionflags,
298 setUp=setUp, tearDown=tearDown,
298 setUp=setUp, tearDown=tearDown,
299 checker=checker)
299 checker=checker)
300 # Now we must actually copy the original constructor from the stdlib
300 # Now we must actually copy the original constructor from the stdlib
301 # doctest class, because we can't call it directly and a bug in nose
301 # doctest class, because we can't call it directly and a bug in nose
302 # means it never gets passed the right arguments.
302 # means it never gets passed the right arguments.
303
303
304 self._dt_optionflags = optionflags
304 self._dt_optionflags = optionflags
305 self._dt_checker = checker
305 self._dt_checker = checker
306 self._dt_test = test
306 self._dt_test = test
307 self._dt_setUp = setUp
307 self._dt_setUp = setUp
308 self._dt_tearDown = tearDown
308 self._dt_tearDown = tearDown
309
309
310 # Each doctest should remember what directory it was loaded from...
310 # Each doctest should remember what directory it was loaded from...
311 self._ori_dir = os.getcwd()
311 self._ori_dir = os.getcwd()
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 test = self._dt_test
315 test = self._dt_test
316 old = sys.stdout
316 old = sys.stdout
317 new = StringIO()
317 new = StringIO()
318 optionflags = self._dt_optionflags
318 optionflags = self._dt_optionflags
319
319
320 if not (optionflags & REPORTING_FLAGS):
320 if not (optionflags & REPORTING_FLAGS):
321 # The option flags don't include any reporting flags,
321 # The option flags don't include any reporting flags,
322 # so add the default reporting flags
322 # so add the default reporting flags
323 optionflags |= _unittest_reportflags
323 optionflags |= _unittest_reportflags
324
324
325 runner = IPDocTestRunner(optionflags=optionflags,
325 runner = IPDocTestRunner(optionflags=optionflags,
326 checker=self._dt_checker, verbose=False)
326 checker=self._dt_checker, verbose=False)
327
327
328 try:
328 try:
329 # Save our current directory and switch out to the one where the
329 # Save our current directory and switch out to the one where the
330 # test was originally created, in case another doctest did a
330 # test was originally created, in case another doctest did a
331 # directory change. We'll restore this in the finally clause.
331 # directory change. We'll restore this in the finally clause.
332 curdir = os.getcwd()
332 curdir = os.getcwd()
333 os.chdir(self._ori_dir)
333 os.chdir(self._ori_dir)
334
334
335 runner.DIVIDER = "-"*70
335 runner.DIVIDER = "-"*70
336 failures, tries = runner.run(
336 failures, tries = runner.run(
337 test, out=new.write, clear_globs=False)
337 test, out=new.write, clear_globs=False)
338 finally:
338 finally:
339 sys.stdout = old
339 sys.stdout = old
340 os.chdir(curdir)
340 os.chdir(curdir)
341
341
342 if failures:
342 if failures:
343 raise self.failureException(self.format_failure(new.getvalue()))
343 raise self.failureException(self.format_failure(new.getvalue()))
344
344
345
345
346 # A simple subclassing of the original with a different class name, so we can
346 # A simple subclassing of the original with a different class name, so we can
347 # distinguish and treat differently IPython examples from pure python ones.
347 # distinguish and treat differently IPython examples from pure python ones.
348 class IPExample(doctest.Example): pass
348 class IPExample(doctest.Example): pass
349
349
350
350
351 class IPExternalExample(doctest.Example):
351 class IPExternalExample(doctest.Example):
352 """Doctest examples to be run in an external process."""
352 """Doctest examples to be run in an external process."""
353
353
354 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
354 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
355 options=None):
355 options=None):
356 # Parent constructor
356 # Parent constructor
357 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
357 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
358
358
359 # An EXTRA newline is needed to prevent pexpect hangs
359 # An EXTRA newline is needed to prevent pexpect hangs
360 self.source += '\n'
360 self.source += '\n'
361
361
362
362
363 class IPDocTestParser(doctest.DocTestParser):
363 class IPDocTestParser(doctest.DocTestParser):
364 """
364 """
365 A class used to parse strings containing doctest examples.
365 A class used to parse strings containing doctest examples.
366
366
367 Note: This is a version modified to properly recognize IPython input and
367 Note: This is a version modified to properly recognize IPython input and
368 convert any IPython examples into valid Python ones.
368 convert any IPython examples into valid Python ones.
369 """
369 """
370 # This regular expression is used to find doctest examples in a
370 # This regular expression is used to find doctest examples in a
371 # string. It defines three groups: `source` is the source code
371 # string. It defines three groups: `source` is the source code
372 # (including leading indentation and prompts); `indent` is the
372 # (including leading indentation and prompts); `indent` is the
373 # indentation of the first (PS1) line of the source code; and
373 # indentation of the first (PS1) line of the source code; and
374 # `want` is the expected output (including leading indentation).
374 # `want` is the expected output (including leading indentation).
375
375
376 # Classic Python prompts or default IPython ones
376 # Classic Python prompts or default IPython ones
377 _PS1_PY = r'>>>'
377 _PS1_PY = r'>>>'
378 _PS2_PY = r'\.\.\.'
378 _PS2_PY = r'\.\.\.'
379
379
380 _PS1_IP = r'In\ \[\d+\]:'
380 _PS1_IP = r'In\ \[\d+\]:'
381 _PS2_IP = r'\ \ \ \.\.\.+:'
381 _PS2_IP = r'\ \ \ \.\.\.+:'
382
382
383 _RE_TPL = r'''
383 _RE_TPL = r'''
384 # Source consists of a PS1 line followed by zero or more PS2 lines.
384 # Source consists of a PS1 line followed by zero or more PS2 lines.
385 (?P<source>
385 (?P<source>
386 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
386 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
387 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
387 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
388 \n? # a newline
388 \n? # a newline
389 # Want consists of any non-blank lines that do not start with PS1.
389 # Want consists of any non-blank lines that do not start with PS1.
390 (?P<want> (?:(?![ ]*$) # Not a blank line
390 (?P<want> (?:(?![ ]*$) # Not a blank line
391 (?![ ]*%s) # Not a line starting with PS1
391 (?![ ]*%s) # Not a line starting with PS1
392 (?![ ]*%s) # Not a line starting with PS2
392 (?![ ]*%s) # Not a line starting with PS2
393 .*$\n? # But any other line
393 .*$\n? # But any other line
394 )*)
394 )*)
395 '''
395 '''
396
396
397 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
397 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
398 re.MULTILINE | re.VERBOSE)
398 re.MULTILINE | re.VERBOSE)
399
399
400 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
400 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
401 re.MULTILINE | re.VERBOSE)
401 re.MULTILINE | re.VERBOSE)
402
402
403 # Mark a test as being fully random. In this case, we simply append the
403 # Mark a test as being fully random. In this case, we simply append the
404 # random marker ('#random') to each individual example's output. This way
404 # random marker ('#random') to each individual example's output. This way
405 # we don't need to modify any other code.
405 # we don't need to modify any other code.
406 _RANDOM_TEST = re.compile(r'#\s*all-random')
406 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
407
407
408 # Mark tests to be executed in an external process - currently unsupported.
408 # Mark tests to be executed in an external process - currently unsupported.
409 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
409 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
410
410
411 def ip2py(self,source):
411 def ip2py(self,source):
412 """Convert input IPython source into valid Python."""
412 """Convert input IPython source into valid Python."""
413 out = []
413 out = []
414 newline = out.append
414 newline = out.append
415 for lnum,line in enumerate(source.splitlines()):
415 for lnum,line in enumerate(source.splitlines()):
416 newline(_ip.IP.prefilter(line,lnum>0))
416 newline(_ip.IP.prefilter(line,lnum>0))
417 newline('') # ensure a closing newline, needed by doctest
417 newline('') # ensure a closing newline, needed by doctest
418 #print "PYSRC:", '\n'.join(out) # dbg
418 #print "PYSRC:", '\n'.join(out) # dbg
419 return '\n'.join(out)
419 return '\n'.join(out)
420
420
421 def parse(self, string, name='<string>'):
421 def parse(self, string, name='<string>'):
422 """
422 """
423 Divide the given string into examples and intervening text,
423 Divide the given string into examples and intervening text,
424 and return them as a list of alternating Examples and strings.
424 and return them as a list of alternating Examples and strings.
425 Line numbers for the Examples are 0-based. The optional
425 Line numbers for the Examples are 0-based. The optional
426 argument `name` is a name identifying this string, and is only
426 argument `name` is a name identifying this string, and is only
427 used for error messages.
427 used for error messages.
428 """
428 """
429
429
430 #print 'Parse string:\n',string # dbg
430 #print 'Parse string:\n',string # dbg
431
431
432 string = string.expandtabs()
432 string = string.expandtabs()
433 # If all lines begin with the same indentation, then strip it.
433 # If all lines begin with the same indentation, then strip it.
434 min_indent = self._min_indent(string)
434 min_indent = self._min_indent(string)
435 if min_indent > 0:
435 if min_indent > 0:
436 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
436 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
437
437
438 output = []
438 output = []
439 charno, lineno = 0, 0
439 charno, lineno = 0, 0
440
440
441 if self._RANDOM_TEST.search(string):
441 if self._RANDOM_TEST.search(string):
442 random_marker = '\n# random'
442 random_marker = '\n# random'
443 else:
443 else:
444 random_marker = ''
444 random_marker = ''
445
445
446 # Whether to convert the input from ipython to python syntax
446 # Whether to convert the input from ipython to python syntax
447 ip2py = False
447 ip2py = False
448 # Find all doctest examples in the string. First, try them as Python
448 # Find all doctest examples in the string. First, try them as Python
449 # examples, then as IPython ones
449 # examples, then as IPython ones
450 terms = list(self._EXAMPLE_RE_PY.finditer(string))
450 terms = list(self._EXAMPLE_RE_PY.finditer(string))
451 if terms:
451 if terms:
452 # Normal Python example
452 # Normal Python example
453 #print '-'*70 # dbg
453 #print '-'*70 # dbg
454 #print 'PyExample, Source:\n',string # dbg
454 #print 'PyExample, Source:\n',string # dbg
455 #print '-'*70 # dbg
455 #print '-'*70 # dbg
456 Example = doctest.Example
456 Example = doctest.Example
457 else:
457 else:
458 # It's an ipython example. Note that IPExamples are run
458 # It's an ipython example. Note that IPExamples are run
459 # in-process, so their syntax must be turned into valid python.
459 # in-process, so their syntax must be turned into valid python.
460 # IPExternalExamples are run out-of-process (via pexpect) so they
460 # IPExternalExamples are run out-of-process (via pexpect) so they
461 # don't need any filtering (a real ipython will be executing them).
461 # don't need any filtering (a real ipython will be executing them).
462 terms = list(self._EXAMPLE_RE_IP.finditer(string))
462 terms = list(self._EXAMPLE_RE_IP.finditer(string))
463 if self._EXTERNAL_IP.search(string):
463 if self._EXTERNAL_IP.search(string):
464 #print '-'*70 # dbg
464 #print '-'*70 # dbg
465 #print 'IPExternalExample, Source:\n',string # dbg
465 #print 'IPExternalExample, Source:\n',string # dbg
466 #print '-'*70 # dbg
466 #print '-'*70 # dbg
467 Example = IPExternalExample
467 Example = IPExternalExample
468 else:
468 else:
469 #print '-'*70 # dbg
469 #print '-'*70 # dbg
470 #print 'IPExample, Source:\n',string # dbg
470 #print 'IPExample, Source:\n',string # dbg
471 #print '-'*70 # dbg
471 #print '-'*70 # dbg
472 Example = IPExample
472 Example = IPExample
473 ip2py = True
473 ip2py = True
474
474
475 for m in terms:
475 for m in terms:
476 # Add the pre-example text to `output`.
476 # Add the pre-example text to `output`.
477 output.append(string[charno:m.start()])
477 output.append(string[charno:m.start()])
478 # Update lineno (lines before this example)
478 # Update lineno (lines before this example)
479 lineno += string.count('\n', charno, m.start())
479 lineno += string.count('\n', charno, m.start())
480 # Extract info from the regexp match.
480 # Extract info from the regexp match.
481 (source, options, want, exc_msg) = \
481 (source, options, want, exc_msg) = \
482 self._parse_example(m, name, lineno,ip2py)
482 self._parse_example(m, name, lineno,ip2py)
483
483
484 # Append the random-output marker (it defaults to empty in most
484 # Append the random-output marker (it defaults to empty in most
485 # cases, it's only non-empty for 'all-random' tests):
485 # cases, it's only non-empty for 'all-random' tests):
486 want += random_marker
486 want += random_marker
487
487
488 if Example is IPExternalExample:
488 if Example is IPExternalExample:
489 options[doctest.NORMALIZE_WHITESPACE] = True
489 options[doctest.NORMALIZE_WHITESPACE] = True
490 want += '\n'
490 want += '\n'
491
491
492 # Create an Example, and add it to the list.
492 # Create an Example, and add it to the list.
493 if not self._IS_BLANK_OR_COMMENT(source):
493 if not self._IS_BLANK_OR_COMMENT(source):
494 output.append(Example(source, want, exc_msg,
494 output.append(Example(source, want, exc_msg,
495 lineno=lineno,
495 lineno=lineno,
496 indent=min_indent+len(m.group('indent')),
496 indent=min_indent+len(m.group('indent')),
497 options=options))
497 options=options))
498 # Update lineno (lines inside this example)
498 # Update lineno (lines inside this example)
499 lineno += string.count('\n', m.start(), m.end())
499 lineno += string.count('\n', m.start(), m.end())
500 # Update charno.
500 # Update charno.
501 charno = m.end()
501 charno = m.end()
502 # Add any remaining post-example text to `output`.
502 # Add any remaining post-example text to `output`.
503 output.append(string[charno:])
503 output.append(string[charno:])
504 return output
504 return output
505
505
506 def _parse_example(self, m, name, lineno,ip2py=False):
506 def _parse_example(self, m, name, lineno,ip2py=False):
507 """
507 """
508 Given a regular expression match from `_EXAMPLE_RE` (`m`),
508 Given a regular expression match from `_EXAMPLE_RE` (`m`),
509 return a pair `(source, want)`, where `source` is the matched
509 return a pair `(source, want)`, where `source` is the matched
510 example's source code (with prompts and indentation stripped);
510 example's source code (with prompts and indentation stripped);
511 and `want` is the example's expected output (with indentation
511 and `want` is the example's expected output (with indentation
512 stripped).
512 stripped).
513
513
514 `name` is the string's name, and `lineno` is the line number
514 `name` is the string's name, and `lineno` is the line number
515 where the example starts; both are used for error messages.
515 where the example starts; both are used for error messages.
516
516
517 Optional:
517 Optional:
518 `ip2py`: if true, filter the input via IPython to convert the syntax
518 `ip2py`: if true, filter the input via IPython to convert the syntax
519 into valid python.
519 into valid python.
520 """
520 """
521
521
522 # Get the example's indentation level.
522 # Get the example's indentation level.
523 indent = len(m.group('indent'))
523 indent = len(m.group('indent'))
524
524
525 # Divide source into lines; check that they're properly
525 # Divide source into lines; check that they're properly
526 # indented; and then strip their indentation & prompts.
526 # indented; and then strip their indentation & prompts.
527 source_lines = m.group('source').split('\n')
527 source_lines = m.group('source').split('\n')
528
528
529 # We're using variable-length input prompts
529 # We're using variable-length input prompts
530 ps1 = m.group('ps1')
530 ps1 = m.group('ps1')
531 ps2 = m.group('ps2')
531 ps2 = m.group('ps2')
532 ps1_len = len(ps1)
532 ps1_len = len(ps1)
533
533
534 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
534 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
535 if ps2:
535 if ps2:
536 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
536 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
537
537
538 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
538 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
539
539
540 if ip2py:
540 if ip2py:
541 # Convert source input from IPython into valid Python syntax
541 # Convert source input from IPython into valid Python syntax
542 source = self.ip2py(source)
542 source = self.ip2py(source)
543
543
544 # Divide want into lines; check that it's properly indented; and
544 # Divide want into lines; check that it's properly indented; and
545 # then strip the indentation. Spaces before the last newline should
545 # then strip the indentation. Spaces before the last newline should
546 # be preserved, so plain rstrip() isn't good enough.
546 # be preserved, so plain rstrip() isn't good enough.
547 want = m.group('want')
547 want = m.group('want')
548 want_lines = want.split('\n')
548 want_lines = want.split('\n')
549 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
549 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
550 del want_lines[-1] # forget final newline & spaces after it
550 del want_lines[-1] # forget final newline & spaces after it
551 self._check_prefix(want_lines, ' '*indent, name,
551 self._check_prefix(want_lines, ' '*indent, name,
552 lineno + len(source_lines))
552 lineno + len(source_lines))
553
553
554 # Remove ipython output prompt that might be present in the first line
554 # Remove ipython output prompt that might be present in the first line
555 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
555 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
556
556
557 want = '\n'.join([wl[indent:] for wl in want_lines])
557 want = '\n'.join([wl[indent:] for wl in want_lines])
558
558
559 # If `want` contains a traceback message, then extract it.
559 # If `want` contains a traceback message, then extract it.
560 m = self._EXCEPTION_RE.match(want)
560 m = self._EXCEPTION_RE.match(want)
561 if m:
561 if m:
562 exc_msg = m.group('msg')
562 exc_msg = m.group('msg')
563 else:
563 else:
564 exc_msg = None
564 exc_msg = None
565
565
566 # Extract options from the source.
566 # Extract options from the source.
567 options = self._find_options(source, name, lineno)
567 options = self._find_options(source, name, lineno)
568
568
569 return source, options, want, exc_msg
569 return source, options, want, exc_msg
570
570
571 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
571 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
572 """
572 """
573 Given the lines of a source string (including prompts and
573 Given the lines of a source string (including prompts and
574 leading indentation), check to make sure that every prompt is
574 leading indentation), check to make sure that every prompt is
575 followed by a space character. If any line is not followed by
575 followed by a space character. If any line is not followed by
576 a space character, then raise ValueError.
576 a space character, then raise ValueError.
577
577
578 Note: IPython-modified version which takes the input prompt length as a
578 Note: IPython-modified version which takes the input prompt length as a
579 parameter, so that prompts of variable length can be dealt with.
579 parameter, so that prompts of variable length can be dealt with.
580 """
580 """
581 space_idx = indent+ps1_len
581 space_idx = indent+ps1_len
582 min_len = space_idx+1
582 min_len = space_idx+1
583 for i, line in enumerate(lines):
583 for i, line in enumerate(lines):
584 if len(line) >= min_len and line[space_idx] != ' ':
584 if len(line) >= min_len and line[space_idx] != ' ':
585 raise ValueError('line %r of the docstring for %s '
585 raise ValueError('line %r of the docstring for %s '
586 'lacks blank after %s: %r' %
586 'lacks blank after %s: %r' %
587 (lineno+i+1, name,
587 (lineno+i+1, name,
588 line[indent:space_idx], line))
588 line[indent:space_idx], line))
589
589
590
590
591 SKIP = doctest.register_optionflag('SKIP')
591 SKIP = doctest.register_optionflag('SKIP')
592
592
593
593
594 class IPDocTestRunner(doctest.DocTestRunner,object):
594 class IPDocTestRunner(doctest.DocTestRunner,object):
595 """Test runner that synchronizes the IPython namespace with test globals.
595 """Test runner that synchronizes the IPython namespace with test globals.
596 """
596 """
597
597
598 def run(self, test, compileflags=None, out=None, clear_globs=True):
598 def run(self, test, compileflags=None, out=None, clear_globs=True):
599
599
600 # Hack: ipython needs access to the execution context of the example,
600 # Hack: ipython needs access to the execution context of the example,
601 # so that it can propagate user variables loaded by %run into
601 # so that it can propagate user variables loaded by %run into
602 # test.globs. We put them here into our modified %run as a function
602 # test.globs. We put them here into our modified %run as a function
603 # attribute. Our new %run will then only make the namespace update
603 # attribute. Our new %run will then only make the namespace update
604 # when called (rather than unconconditionally updating test.globs here
604 # when called (rather than unconconditionally updating test.globs here
605 # for all examples, most of which won't be calling %run anyway).
605 # for all examples, most of which won't be calling %run anyway).
606 _run_ns_sync.test_globs = test.globs
606 _run_ns_sync.test_globs = test.globs
607
607
608 # dbg
608 # dbg
609 ## print >> sys.stderr, "Test:",test
609 ## print >> sys.stderr, "Test:",test
610 ## for ex in test.examples:
610 ## for ex in test.examples:
611 ## print >> sys.stderr, ex.source
611 ## print >> sys.stderr, ex.source
612 ## print >> sys.stderr, 'Want:\n',ex.want,'\n--'
612 ## print >> sys.stderr, 'Want:\n',ex.want,'\n--'
613
613
614 return super(IPDocTestRunner,self).run(test,
614 return super(IPDocTestRunner,self).run(test,
615 compileflags,out,clear_globs)
615 compileflags,out,clear_globs)
616
616
617
617
618 class DocFileCase(doctest.DocFileCase):
618 class DocFileCase(doctest.DocFileCase):
619 """Overrides to provide filename
619 """Overrides to provide filename
620 """
620 """
621 def address(self):
621 def address(self):
622 return (self._dt_test.filename, None, None)
622 return (self._dt_test.filename, None, None)
623
623
624
624
625 class ExtensionDoctest(doctests.Doctest):
625 class ExtensionDoctest(doctests.Doctest):
626 """Nose Plugin that supports doctests in extension modules.
626 """Nose Plugin that supports doctests in extension modules.
627 """
627 """
628 name = 'extdoctest' # call nosetests with --with-extdoctest
628 name = 'extdoctest' # call nosetests with --with-extdoctest
629 enabled = True
629 enabled = True
630
630
631 def options(self, parser, env=os.environ):
631 def options(self, parser, env=os.environ):
632 Plugin.options(self, parser, env)
632 Plugin.options(self, parser, env)
633
633
634 def configure(self, options, config):
634 def configure(self, options, config):
635 Plugin.configure(self, options, config)
635 Plugin.configure(self, options, config)
636 self.doctest_tests = options.doctest_tests
636 self.doctest_tests = options.doctest_tests
637 self.extension = tolist(options.doctestExtension)
637 self.extension = tolist(options.doctestExtension)
638 self.finder = DocTestFinder()
638 self.finder = DocTestFinder()
639 self.parser = doctest.DocTestParser()
639 self.parser = doctest.DocTestParser()
640 self.globs = None
640 self.globs = None
641 self.extraglobs = None
641 self.extraglobs = None
642
642
643 def loadTestsFromExtensionModule(self,filename):
643 def loadTestsFromExtensionModule(self,filename):
644 bpath,mod = os.path.split(filename)
644 bpath,mod = os.path.split(filename)
645 modname = os.path.splitext(mod)[0]
645 modname = os.path.splitext(mod)[0]
646 try:
646 try:
647 sys.path.append(bpath)
647 sys.path.append(bpath)
648 module = __import__(modname)
648 module = __import__(modname)
649 tests = list(self.loadTestsFromModule(module))
649 tests = list(self.loadTestsFromModule(module))
650 finally:
650 finally:
651 sys.path.pop()
651 sys.path.pop()
652 return tests
652 return tests
653
653
654 # NOTE: the method below is almost a copy of the original one in nose, with
654 # NOTE: the method below is almost a copy of the original one in nose, with
655 # a few modifications to control output checking.
655 # a few modifications to control output checking.
656
656
657 def loadTestsFromModule(self, module):
657 def loadTestsFromModule(self, module):
658 #print 'lTM',module # dbg
658 #print 'lTM',module # dbg
659
659
660 if not self.matches(module.__name__):
660 if not self.matches(module.__name__):
661 log.debug("Doctest doesn't want module %s", module)
661 log.debug("Doctest doesn't want module %s", module)
662 return
662 return
663
663
664 tests = self.finder.find(module,globs=self.globs,
664 tests = self.finder.find(module,globs=self.globs,
665 extraglobs=self.extraglobs)
665 extraglobs=self.extraglobs)
666 if not tests:
666 if not tests:
667 return
667 return
668
668
669 tests.sort()
669 tests.sort()
670 module_file = module.__file__
670 module_file = module.__file__
671 if module_file[-4:] in ('.pyc', '.pyo'):
671 if module_file[-4:] in ('.pyc', '.pyo'):
672 module_file = module_file[:-1]
672 module_file = module_file[:-1]
673 for test in tests:
673 for test in tests:
674 if not test.examples:
674 if not test.examples:
675 continue
675 continue
676 if not test.filename:
676 if not test.filename:
677 test.filename = module_file
677 test.filename = module_file
678
678
679 # xxx - checker and options may be ok instantiated once outside loop
679 # xxx - checker and options may be ok instantiated once outside loop
680 # always use whitespace and ellipsis options
680 # always use whitespace and ellipsis options
681 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
681 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
682 checker = IPDoctestOutputChecker()
682 checker = IPDoctestOutputChecker()
683
683
684 yield DocTestCase(test,
684 yield DocTestCase(test,
685 optionflags=optionflags,
685 optionflags=optionflags,
686 checker=checker)
686 checker=checker)
687
687
688 def loadTestsFromFile(self, filename):
688 def loadTestsFromFile(self, filename):
689 #print 'lTF',filename # dbg
689 #print 'lTF',filename # dbg
690
690
691 if is_extension_module(filename):
691 if is_extension_module(filename):
692 for t in self.loadTestsFromExtensionModule(filename):
692 for t in self.loadTestsFromExtensionModule(filename):
693 yield t
693 yield t
694 else:
694 else:
695 if self.extension and anyp(filename.endswith, self.extension):
695 if self.extension and anyp(filename.endswith, self.extension):
696 name = os.path.basename(filename)
696 name = os.path.basename(filename)
697 dh = open(filename)
697 dh = open(filename)
698 try:
698 try:
699 doc = dh.read()
699 doc = dh.read()
700 finally:
700 finally:
701 dh.close()
701 dh.close()
702 test = self.parser.get_doctest(
702 test = self.parser.get_doctest(
703 doc, globs={'__file__': filename}, name=name,
703 doc, globs={'__file__': filename}, name=name,
704 filename=filename, lineno=0)
704 filename=filename, lineno=0)
705 if test.examples:
705 if test.examples:
706 #print 'FileCase:',test.examples # dbg
706 #print 'FileCase:',test.examples # dbg
707 yield DocFileCase(test)
707 yield DocFileCase(test)
708 else:
708 else:
709 yield False # no tests to load
709 yield False # no tests to load
710
710
711 def wantFile(self,filename):
711 def wantFile(self,filename):
712 """Return whether the given filename should be scanned for tests.
712 """Return whether the given filename should be scanned for tests.
713
713
714 Modified version that accepts extension modules as valid containers for
714 Modified version that accepts extension modules as valid containers for
715 doctests.
715 doctests.
716 """
716 """
717 #print 'Filename:',filename # dbg
717 #print 'Filename:',filename # dbg
718
718
719 # temporarily hardcoded list, will move to driver later
719 # temporarily hardcoded list, will move to driver later
720 exclude = ['IPython/external/',
720 exclude = ['IPython/external/',
721 'IPython/Extensions/ipy_',
721 'IPython/Extensions/ipy_',
722 'IPython/platutils_win32',
722 'IPython/platutils_win32',
723 'IPython/frontend/cocoa',
723 'IPython/frontend/cocoa',
724 'IPython_doctest_plugin',
724 'IPython_doctest_plugin',
725 'IPython/Gnuplot',
725 'IPython/Gnuplot',
726 'IPython/Extensions/PhysicalQIn']
726 'IPython/Extensions/PhysicalQIn']
727
727
728 for fex in exclude:
728 for fex in exclude:
729 if fex in filename: # substring
729 if fex in filename: # substring
730 #print '###>>> SKIP:',filename # dbg
730 #print '###>>> SKIP:',filename # dbg
731 return False
731 return False
732
732
733 if is_extension_module(filename):
733 if is_extension_module(filename):
734 return True
734 return True
735 else:
735 else:
736 return doctests.Doctest.wantFile(self,filename)
736 return doctests.Doctest.wantFile(self,filename)
737
737
738
738
739 class IPythonDoctest(ExtensionDoctest):
739 class IPythonDoctest(ExtensionDoctest):
740 """Nose Plugin that supports doctests in extension modules.
740 """Nose Plugin that supports doctests in extension modules.
741 """
741 """
742 name = 'ipdoctest' # call nosetests with --with-ipdoctest
742 name = 'ipdoctest' # call nosetests with --with-ipdoctest
743 enabled = True
743 enabled = True
744
744
745 def configure(self, options, config):
745 def configure(self, options, config):
746
746
747 Plugin.configure(self, options, config)
747 Plugin.configure(self, options, config)
748 self.doctest_tests = options.doctest_tests
748 self.doctest_tests = options.doctest_tests
749 self.extension = tolist(options.doctestExtension)
749 self.extension = tolist(options.doctestExtension)
750 self.parser = IPDocTestParser()
750 self.parser = IPDocTestParser()
751 self.finder = DocTestFinder(parser=self.parser)
751 self.finder = DocTestFinder(parser=self.parser)
752 self.globs = None
752 self.globs = None
753 self.extraglobs = None
753 self.extraglobs = None
General Comments 0
You need to be logged in to leave comments. Login now