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