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