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