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