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