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