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