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