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