##// END OF EJS Templates
Checkpoint where tests are recognized. Random tests not working yet.
Fernando Perez -
Show More
@@ -1,24 +1,28 b''
1 # Set this prefix to where you want to install the plugin
1 # Set this prefix to where you want to install the plugin
2 PREFIX=~/usr/local
2 PREFIX=~/usr/local
3 PREFIX=~/tmp/local
3 PREFIX=~/tmp/local
4
4
5 plugin: IPython_doctest_plugin.egg-info
5 plugin: IPython_doctest_plugin.egg-info
6
6
7 dtest: plugin dtexample.py
8 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
9 dtexample.py
10
7 test: plugin dtexample.py
11 test: plugin dtexample.py
8 nosetests -s --with-ipdoctest --doctest-tests --doctest-extension=txt \
12 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
9 dtexample.py test*.txt
13 dtexample.py test*.txt
10
14
11 deb: plugin dtexample.py
15 deb: plugin dtexample.py
12 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
16 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
13 test_combo.txt
17 test_combo.txt
14
18
15 iptest: plugin
19 iptest: plugin
16 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
20 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
17 IPython
21 IPython
18
22
19 IPython_doctest_plugin.egg-info: ipdoctest.py setup.py
23 IPython_doctest_plugin.egg-info: ipdoctest.py setup.py
20 python setup.py install --prefix=$(PREFIX)
24 python setup.py install --prefix=$(PREFIX)
21 touch $@
25 touch $@
22
26
23 clean:
27 clean:
24 rm -rf IPython_doctest_plugin.egg-info *~ *pyc build/ dist/
28 rm -rf IPython_doctest_plugin.egg-info *~ *pyc build/ dist/
@@ -1,72 +1,107 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
74 def ranfunc():
75 """A function with some random output.
76
77 >>> 1+3 #random
78 junk goes here...
79
80 >>> 1+3
81 4
82
83 >>> 1+2 #random
84 again, anything goes
85 """
86 return 'ranfunc'
87
88
89 def ranf2():
90 """A function whose examples are all all random
91
92 Examples:
93
94 #all-random
95
96 >>> 1+3 #random
97 junk goes here...
98
99 >>> 1+3
100 klasdfj;
101
102 >>> 1+2 #random
103 again, anything goes
104
105 """
106 return 'ranf2'
107
@@ -1,598 +1,619 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 unittest
46 import unittest
47
47
48 from inspect import getmodule
48 from inspect import getmodule
49
49
50 # Third-party modules
50 # Third-party modules
51 import nose.core
51 import nose.core
52
52
53 from nose.plugins import doctests, Plugin
53 from nose.plugins import doctests, Plugin
54 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
54 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
55
55
56 # Our own imports
56 # Our own imports
57 #from extdoctest import ExtensionDoctest, DocTestFinder
57 #from extdoctest import ExtensionDoctest, DocTestFinder
58 #from dttools import DocTestFinder, DocTestCase
58 #from dttools import DocTestFinder, DocTestCase
59 #-----------------------------------------------------------------------------
59 #-----------------------------------------------------------------------------
60 # Module globals and other constants
60 # Module globals and other constants
61
61
62 log = logging.getLogger(__name__)
62 log = logging.getLogger(__name__)
63
63
64 ###########################################################################
64 ###########################################################################
65 # *** HACK ***
65 # *** HACK ***
66 # We must start our own ipython object and heavily muck with it so that all the
66 # We must start our own ipython object and heavily muck with it so that all the
67 # modifications IPython makes to system behavior don't send the doctest
67 # modifications IPython makes to system behavior don't send the doctest
68 # machinery into a fit. This code should be considered a gross hack, but it
68 # machinery into a fit. This code should be considered a gross hack, but it
69 # gets the job done.
69 # gets the job done.
70
70
71 def start_ipython():
71 def start_ipython():
72 """Start a global IPython shell, which we need for IPython-specific syntax.
72 """Start a global IPython shell, which we need for IPython-specific syntax.
73 """
73 """
74 import IPython
74 import IPython
75
75
76 def xsys(cmd):
76 def xsys(cmd):
77 """Execute a command and print its output.
77 """Execute a command and print its output.
78
78
79 This is just a convenience function to replace the IPython system call
79 This is just a convenience function to replace the IPython system call
80 with one that is more doctest-friendly.
80 with one that is more doctest-friendly.
81 """
81 """
82 cmd = _ip.IP.var_expand(cmd,depth=1)
82 cmd = _ip.IP.var_expand(cmd,depth=1)
83 sys.stdout.write(commands.getoutput(cmd))
83 sys.stdout.write(commands.getoutput(cmd))
84 sys.stdout.flush()
84 sys.stdout.flush()
85
85
86 # Store certain global objects that IPython modifies
86 # Store certain global objects that IPython modifies
87 _displayhook = sys.displayhook
87 _displayhook = sys.displayhook
88 _excepthook = sys.excepthook
88 _excepthook = sys.excepthook
89 _main = sys.modules.get('__main__')
89 _main = sys.modules.get('__main__')
90
90
91 # Start IPython instance
91 # Start IPython instance
92 IPython.Shell.IPShell(['--classic','--noterm_title'])
92 IPython.Shell.IPShell(['--classic','--noterm_title'])
93
93
94 # Deactivate the various python system hooks added by ipython for
94 # Deactivate the various python system hooks added by ipython for
95 # interactive convenience so we don't confuse the doctest system
95 # interactive convenience so we don't confuse the doctest system
96 sys.modules['__main__'] = _main
96 sys.modules['__main__'] = _main
97 sys.displayhook = _displayhook
97 sys.displayhook = _displayhook
98 sys.excepthook = _excepthook
98 sys.excepthook = _excepthook
99
99
100 # So that ipython magics and aliases can be doctested (they work by making
100 # So that ipython magics and aliases can be doctested (they work by making
101 # a call into a global _ip object)
101 # a call into a global _ip object)
102 _ip = IPython.ipapi.get()
102 _ip = IPython.ipapi.get()
103 __builtin__._ip = _ip
103 __builtin__._ip = _ip
104
104
105 # Modify the IPython system call with one that uses getoutput, so that we
105 # Modify the IPython system call with one that uses getoutput, so that we
106 # can capture subcommands and print them to Python's stdout, otherwise the
106 # can capture subcommands and print them to Python's stdout, otherwise the
107 # doctest machinery would miss them.
107 # doctest machinery would miss them.
108 _ip.system = xsys
108 _ip.system = xsys
109
109
110 # The start call MUST be made here. I'm not sure yet why it doesn't work if
110 # The start call MUST be made here. I'm not sure yet why it doesn't work if
111 # it is made later, at plugin initialization time, but in all my tests, that's
111 # it is made later, at plugin initialization time, but in all my tests, that's
112 # the case.
112 # the case.
113 start_ipython()
113 start_ipython()
114
114
115 # *** END HACK ***
115 # *** END HACK ***
116 ###########################################################################
116 ###########################################################################
117
117
118 # Classes and functions
118 # Classes and functions
119
119
120 def is_extension_module(filename):
120 def is_extension_module(filename):
121 """Return whether the given filename is an extension module.
121 """Return whether the given filename is an extension module.
122
122
123 This simply checks that the extension is either .so or .pyd.
123 This simply checks that the extension is either .so or .pyd.
124 """
124 """
125 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
125 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
126
126
127
127
128 # Modified version of the one in the stdlib, that fixes a python bug (doctests
128 # Modified version of the one in the stdlib, that fixes a python bug (doctests
129 # not found in extension modules, http://bugs.python.org/issue3158)
129 # not found in extension modules, http://bugs.python.org/issue3158)
130 class DocTestFinder(doctest.DocTestFinder):
130 class DocTestFinder(doctest.DocTestFinder):
131
131
132 def _from_module(self, module, object):
132 def _from_module(self, module, object):
133 """
133 """
134 Return true if the given object is defined in the given
134 Return true if the given object is defined in the given
135 module.
135 module.
136 """
136 """
137 if module is None:
137 if module is None:
138 #print '_fm C1' # dbg
138 #print '_fm C1' # dbg
139 return True
139 return True
140 elif inspect.isfunction(object):
140 elif inspect.isfunction(object):
141 #print '_fm C2' # dbg
141 #print '_fm C2' # dbg
142 return module.__dict__ is object.func_globals
142 return module.__dict__ is object.func_globals
143 elif inspect.isbuiltin(object):
143 elif inspect.isbuiltin(object):
144 #print '_fm C2-1' # dbg
144 #print '_fm C2-1' # dbg
145 return module.__name__ == object.__module__
145 return module.__name__ == object.__module__
146 elif inspect.isclass(object):
146 elif inspect.isclass(object):
147 #print '_fm C3' # dbg
147 #print '_fm C3' # dbg
148 return module.__name__ == object.__module__
148 return module.__name__ == object.__module__
149 elif inspect.ismethod(object):
149 elif inspect.ismethod(object):
150 # This one may be a bug in cython that fails to correctly set the
150 # This one may be a bug in cython that fails to correctly set the
151 # __module__ attribute of methods, but since the same error is easy
151 # __module__ attribute of methods, but since the same error is easy
152 # to make by extension code writers, having this safety in place
152 # to make by extension code writers, having this safety in place
153 # isn't such a bad idea
153 # isn't such a bad idea
154 #print '_fm C3-1' # dbg
154 #print '_fm C3-1' # dbg
155 return module.__name__ == object.im_class.__module__
155 return module.__name__ == object.im_class.__module__
156 elif inspect.getmodule(object) is not None:
156 elif inspect.getmodule(object) is not None:
157 #print '_fm C4' # dbg
157 #print '_fm C4' # dbg
158 #print 'C4 mod',module,'obj',object # dbg
158 #print 'C4 mod',module,'obj',object # dbg
159 return module is inspect.getmodule(object)
159 return module is inspect.getmodule(object)
160 elif hasattr(object, '__module__'):
160 elif hasattr(object, '__module__'):
161 #print '_fm C5' # dbg
161 #print '_fm C5' # dbg
162 return module.__name__ == object.__module__
162 return module.__name__ == object.__module__
163 elif isinstance(object, property):
163 elif isinstance(object, property):
164 #print '_fm C6' # dbg
164 #print '_fm C6' # dbg
165 return True # [XX] no way not be sure.
165 return True # [XX] no way not be sure.
166 else:
166 else:
167 raise ValueError("object must be a class or function")
167 raise ValueError("object must be a class or function")
168
168
169 def _find(self, tests, obj, name, module, source_lines, globs, seen):
169 def _find(self, tests, obj, name, module, source_lines, globs, seen):
170 """
170 """
171 Find tests for the given object and any contained objects, and
171 Find tests for the given object and any contained objects, and
172 add them to `tests`.
172 add them to `tests`.
173 """
173 """
174
174
175 doctest.DocTestFinder._find(self,tests, obj, name, module,
175 doctest.DocTestFinder._find(self,tests, obj, name, module,
176 source_lines, globs, seen)
176 source_lines, globs, seen)
177
177
178 # Below we re-run pieces of the above method with manual modifications,
178 # Below we re-run pieces of the above method with manual modifications,
179 # because the original code is buggy and fails to correctly identify
179 # because the original code is buggy and fails to correctly identify
180 # doctests in extension modules.
180 # doctests in extension modules.
181
181
182 # Local shorthands
182 # Local shorthands
183 from inspect import isroutine, isclass, ismodule
183 from inspect import isroutine, isclass, ismodule
184
184
185 # Look for tests in a module's contained objects.
185 # Look for tests in a module's contained objects.
186 if inspect.ismodule(obj) and self._recurse:
186 if inspect.ismodule(obj) and self._recurse:
187 for valname, val in obj.__dict__.items():
187 for valname, val in obj.__dict__.items():
188 valname1 = '%s.%s' % (name, valname)
188 valname1 = '%s.%s' % (name, valname)
189 if ( (isroutine(val) or isclass(val))
189 if ( (isroutine(val) or isclass(val))
190 and self._from_module(module, val) ):
190 and self._from_module(module, val) ):
191
191
192 self._find(tests, val, valname1, module, source_lines,
192 self._find(tests, val, valname1, module, source_lines,
193 globs, seen)
193 globs, seen)
194
194
195 # Look for tests in a class's contained objects.
195 # Look for tests in a class's contained objects.
196 if inspect.isclass(obj) and self._recurse:
196 if inspect.isclass(obj) and self._recurse:
197 #print 'RECURSE into class:',obj # dbg
197 #print 'RECURSE into class:',obj # dbg
198 for valname, val in obj.__dict__.items():
198 for valname, val in obj.__dict__.items():
199 #valname1 = '%s.%s' % (name, valname) # dbg
199 #valname1 = '%s.%s' % (name, valname) # dbg
200 #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg
200 #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg
201 # Special handling for staticmethod/classmethod.
201 # Special handling for staticmethod/classmethod.
202 if isinstance(val, staticmethod):
202 if isinstance(val, staticmethod):
203 val = getattr(obj, valname)
203 val = getattr(obj, valname)
204 if isinstance(val, classmethod):
204 if isinstance(val, classmethod):
205 val = getattr(obj, valname).im_func
205 val = getattr(obj, valname).im_func
206
206
207 # Recurse to methods, properties, and nested classes.
207 # Recurse to methods, properties, and nested classes.
208 if ((inspect.isfunction(val) or inspect.isclass(val) or
208 if ((inspect.isfunction(val) or inspect.isclass(val) or
209 inspect.ismethod(val) or
209 inspect.ismethod(val) or
210 isinstance(val, property)) and
210 isinstance(val, property)) and
211 self._from_module(module, val)):
211 self._from_module(module, val)):
212 valname = '%s.%s' % (name, valname)
212 valname = '%s.%s' % (name, valname)
213 self._find(tests, val, valname, module, source_lines,
213 self._find(tests, val, valname, module, source_lines,
214 globs, seen)
214 globs, seen)
215
215
216
216
217 class DocTestCase(doctests.DocTestCase):
217 class DocTestCase(doctests.DocTestCase):
218 """Proxy for DocTestCase: provides an address() method that
218 """Proxy for DocTestCase: provides an address() method that
219 returns the correct address for the doctest case. Otherwise
219 returns the correct address for the doctest case. Otherwise
220 acts as a proxy to the test case. To provide hints for address(),
220 acts as a proxy to the test case. To provide hints for address(),
221 an obj may also be passed -- this will be used as the test object
221 an obj may also be passed -- this will be used as the test object
222 for purposes of determining the test address, if it is provided.
222 for purposes of determining the test address, if it is provided.
223 """
223 """
224
224
225 # Note: this method was taken from numpy's nosetester module.
226
227 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
228 # its constructor that blocks non-default arguments from being passed
229 # down into doctest.DocTestCase
230 ## def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
231 ## checker=None, obj=None, result_var='_'):
232 ## self._result_var = result_var
233 ## self._nose_obj = obj
234 ## doctest.DocTestCase.__init__(self, test,
235 ## optionflags=optionflags,
236 ## setUp=setUp, tearDown=tearDown,
237 ## checker=checker)
238
225 # doctests loaded via find(obj) omit the module name
239 # doctests loaded via find(obj) omit the module name
226 # so we need to override id, __repr__ and shortDescription
240 # so we need to override id, __repr__ and shortDescription
227 # bonus: this will squash a 2.3 vs 2.4 incompatiblity
241 # bonus: this will squash a 2.3 vs 2.4 incompatiblity
228 def id(self):
242 def id(self):
229 name = self._dt_test.name
243 name = self._dt_test.name
230 filename = self._dt_test.filename
244 filename = self._dt_test.filename
231 if filename is not None:
245 if filename is not None:
232 pk = getpackage(filename)
246 pk = getpackage(filename)
233 if pk is not None and not name.startswith(pk):
247 if pk is not None and not name.startswith(pk):
234 name = "%s.%s" % (pk, name)
248 name = "%s.%s" % (pk, name)
235 return name
249 return name
236
250
237
251
252
238 # A simple subclassing of the original with a different class name, so we can
253 # A simple subclassing of the original with a different class name, so we can
239 # distinguish and treat differently IPython examples from pure python ones.
254 # distinguish and treat differently IPython examples from pure python ones.
240 class IPExample(doctest.Example): pass
255 class IPExample(doctest.Example): pass
241
256
242
257
243 class IPExternalExample(doctest.Example):
258 class IPExternalExample(doctest.Example):
244 """Doctest examples to be run in an external process."""
259 """Doctest examples to be run in an external process."""
245
260
246 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
261 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
247 options=None):
262 options=None):
248 # Parent constructor
263 # Parent constructor
249 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
264 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
250
265
251 # An EXTRA newline is needed to prevent pexpect hangs
266 # An EXTRA newline is needed to prevent pexpect hangs
252 self.source += '\n'
267 self.source += '\n'
253
268
254
269
255 class IPDocTestParser(doctest.DocTestParser):
270 class IPDocTestParser(doctest.DocTestParser):
256 """
271 """
257 A class used to parse strings containing doctest examples.
272 A class used to parse strings containing doctest examples.
258
273
259 Note: This is a version modified to properly recognize IPython input and
274 Note: This is a version modified to properly recognize IPython input and
260 convert any IPython examples into valid Python ones.
275 convert any IPython examples into valid Python ones.
261 """
276 """
262 # This regular expression is used to find doctest examples in a
277 # This regular expression is used to find doctest examples in a
263 # string. It defines three groups: `source` is the source code
278 # string. It defines three groups: `source` is the source code
264 # (including leading indentation and prompts); `indent` is the
279 # (including leading indentation and prompts); `indent` is the
265 # indentation of the first (PS1) line of the source code; and
280 # indentation of the first (PS1) line of the source code; and
266 # `want` is the expected output (including leading indentation).
281 # `want` is the expected output (including leading indentation).
267
282
268 # Classic Python prompts or default IPython ones
283 # Classic Python prompts or default IPython ones
269 _PS1_PY = r'>>>'
284 _PS1_PY = r'>>>'
270 _PS2_PY = r'\.\.\.'
285 _PS2_PY = r'\.\.\.'
271
286
272 _PS1_IP = r'In\ \[\d+\]:'
287 _PS1_IP = r'In\ \[\d+\]:'
273 _PS2_IP = r'\ \ \ \.\.\.+:'
288 _PS2_IP = r'\ \ \ \.\.\.+:'
274
289
275 _RE_TPL = r'''
290 _RE_TPL = r'''
276 # Source consists of a PS1 line followed by zero or more PS2 lines.
291 # Source consists of a PS1 line followed by zero or more PS2 lines.
277 (?P<source>
292 (?P<source>
278 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
293 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
279 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
294 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
280 \n? # a newline
295 \n? # a newline
281 # Want consists of any non-blank lines that do not start with PS1.
296 # Want consists of any non-blank lines that do not start with PS1.
282 (?P<want> (?:(?![ ]*$) # Not a blank line
297 (?P<want> (?:(?![ ]*$) # Not a blank line
283 (?![ ]*%s) # Not a line starting with PS1
298 (?![ ]*%s) # Not a line starting with PS1
284 (?![ ]*%s) # Not a line starting with PS2
299 (?![ ]*%s) # Not a line starting with PS2
285 .*$\n? # But any other line
300 .*$\n? # But any other line
286 )*)
301 )*)
287 '''
302 '''
288
303
289 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
304 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
290 re.MULTILINE | re.VERBOSE)
305 re.MULTILINE | re.VERBOSE)
291
306
292 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
307 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
293 re.MULTILINE | re.VERBOSE)
308 re.MULTILINE | re.VERBOSE)
294
309
295 def ip2py(self,source):
310 def ip2py(self,source):
296 """Convert input IPython source into valid Python."""
311 """Convert input IPython source into valid Python."""
297 out = []
312 out = []
298 newline = out.append
313 newline = out.append
299 for lnum,line in enumerate(source.splitlines()):
314 for lnum,line in enumerate(source.splitlines()):
300 #newline(_ip.IPipython.prefilter(line,True))
315 #newline(_ip.IPipython.prefilter(line,True))
301 newline(_ip.IP.prefilter(line,lnum>0))
316 newline(_ip.IP.prefilter(line,lnum>0))
302 newline('') # ensure a closing newline, needed by doctest
317 newline('') # ensure a closing newline, needed by doctest
303 return '\n'.join(out)
318 return '\n'.join(out)
304
319
305 def parse(self, string, name='<string>'):
320 def parse(self, string, name='<string>'):
306 """
321 """
307 Divide the given string into examples and intervening text,
322 Divide the given string into examples and intervening text,
308 and return them as a list of alternating Examples and strings.
323 and return them as a list of alternating Examples and strings.
309 Line numbers for the Examples are 0-based. The optional
324 Line numbers for the Examples are 0-based. The optional
310 argument `name` is a name identifying this string, and is only
325 argument `name` is a name identifying this string, and is only
311 used for error messages.
326 used for error messages.
312 """
327 """
313
328
314 #print 'Parse string:\n',string # dbg
329 print 'Parse string:\n',string # dbg
315
330
316 string = string.expandtabs()
331 string = string.expandtabs()
317 # If all lines begin with the same indentation, then strip it.
332 # If all lines begin with the same indentation, then strip it.
318 min_indent = self._min_indent(string)
333 min_indent = self._min_indent(string)
319 if min_indent > 0:
334 if min_indent > 0:
320 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
335 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
321
336
322 output = []
337 output = []
323 charno, lineno = 0, 0
338 charno, lineno = 0, 0
324
339
325 # Whether to convert the input from ipython to python syntax
340 # Whether to convert the input from ipython to python syntax
326 ip2py = False
341 ip2py = False
327 # Find all doctest examples in the string. First, try them as Python
342 # Find all doctest examples in the string. First, try them as Python
328 # examples, then as IPython ones
343 # examples, then as IPython ones
329 terms = list(self._EXAMPLE_RE_PY.finditer(string))
344 terms = list(self._EXAMPLE_RE_PY.finditer(string))
330 if terms:
345 if terms:
331 # Normal Python example
346 # Normal Python example
332 #print '-'*70 # dbg
347 #print '-'*70 # dbg
333 #print 'PyExample, Source:\n',string # dbg
348 #print 'PyExample, Source:\n',string # dbg
334 #print '-'*70 # dbg
349 #print '-'*70 # dbg
335 Example = doctest.Example
350 Example = doctest.Example
336 else:
351 else:
337 # It's an ipython example. Note that IPExamples are run
352 # It's an ipython example. Note that IPExamples are run
338 # in-process, so their syntax must be turned into valid python.
353 # in-process, so their syntax must be turned into valid python.
339 # IPExternalExamples are run out-of-process (via pexpect) so they
354 # IPExternalExamples are run out-of-process (via pexpect) so they
340 # don't need any filtering (a real ipython will be executing them).
355 # don't need any filtering (a real ipython will be executing them).
341 terms = list(self._EXAMPLE_RE_IP.finditer(string))
356 terms = list(self._EXAMPLE_RE_IP.finditer(string))
342 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
357 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
343 #print '-'*70 # dbg
358 #print '-'*70 # dbg
344 #print 'IPExternalExample, Source:\n',string # dbg
359 #print 'IPExternalExample, Source:\n',string # dbg
345 #print '-'*70 # dbg
360 #print '-'*70 # dbg
346 Example = IPExternalExample
361 Example = IPExternalExample
347 else:
362 else:
348 #print '-'*70 # dbg
363 #print '-'*70 # dbg
349 #print 'IPExample, Source:\n',string # dbg
364 #print 'IPExample, Source:\n',string # dbg
350 #print '-'*70 # dbg
365 #print '-'*70 # dbg
351 Example = IPExample
366 Example = IPExample
352 ip2py = True
367 ip2py = True
353
368
354 for m in terms:
369 for m in terms:
355 # Add the pre-example text to `output`.
370 # Add the pre-example text to `output`.
356 output.append(string[charno:m.start()])
371 output.append(string[charno:m.start()])
357 # Update lineno (lines before this example)
372 # Update lineno (lines before this example)
358 lineno += string.count('\n', charno, m.start())
373 lineno += string.count('\n', charno, m.start())
359 # Extract info from the regexp match.
374 # Extract info from the regexp match.
360 (source, options, want, exc_msg) = \
375 (source, options, want, exc_msg) = \
361 self._parse_example(m, name, lineno,ip2py)
376 self._parse_example(m, name, lineno,ip2py)
362 if Example is IPExternalExample:
377 if Example is IPExternalExample:
363 options[doctest.NORMALIZE_WHITESPACE] = True
378 options[doctest.NORMALIZE_WHITESPACE] = True
364 want += '\n'
379 want += '\n'
365 # Create an Example, and add it to the list.
380 # Create an Example, and add it to the list.
366 if not self._IS_BLANK_OR_COMMENT(source):
381 if not self._IS_BLANK_OR_COMMENT(source):
367 #print 'Example source:', source # dbg
382 #print 'Example source:', source # dbg
368 output.append(Example(source, want, exc_msg,
383 output.append(Example(source, want, exc_msg,
369 lineno=lineno,
384 lineno=lineno,
370 indent=min_indent+len(m.group('indent')),
385 indent=min_indent+len(m.group('indent')),
371 options=options))
386 options=options))
372 # Update lineno (lines inside this example)
387 # Update lineno (lines inside this example)
373 lineno += string.count('\n', m.start(), m.end())
388 lineno += string.count('\n', m.start(), m.end())
374 # Update charno.
389 # Update charno.
375 charno = m.end()
390 charno = m.end()
376 # Add any remaining post-example text to `output`.
391 # Add any remaining post-example text to `output`.
377 output.append(string[charno:])
392 output.append(string[charno:])
378
393
394 #print 'OUT:',output # dbg
395
379 return output
396 return output
380
397
381 def _parse_example(self, m, name, lineno,ip2py=False):
398 def _parse_example(self, m, name, lineno,ip2py=False):
382 """
399 """
383 Given a regular expression match from `_EXAMPLE_RE` (`m`),
400 Given a regular expression match from `_EXAMPLE_RE` (`m`),
384 return a pair `(source, want)`, where `source` is the matched
401 return a pair `(source, want)`, where `source` is the matched
385 example's source code (with prompts and indentation stripped);
402 example's source code (with prompts and indentation stripped);
386 and `want` is the example's expected output (with indentation
403 and `want` is the example's expected output (with indentation
387 stripped).
404 stripped).
388
405
389 `name` is the string's name, and `lineno` is the line number
406 `name` is the string's name, and `lineno` is the line number
390 where the example starts; both are used for error messages.
407 where the example starts; both are used for error messages.
391
408
392 Optional:
409 Optional:
393 `ip2py`: if true, filter the input via IPython to convert the syntax
410 `ip2py`: if true, filter the input via IPython to convert the syntax
394 into valid python.
411 into valid python.
395 """
412 """
396
413
397 # Get the example's indentation level.
414 # Get the example's indentation level.
398 indent = len(m.group('indent'))
415 indent = len(m.group('indent'))
399
416
400 # Divide source into lines; check that they're properly
417 # Divide source into lines; check that they're properly
401 # indented; and then strip their indentation & prompts.
418 # indented; and then strip their indentation & prompts.
402 source_lines = m.group('source').split('\n')
419 source_lines = m.group('source').split('\n')
403
420
404 # We're using variable-length input prompts
421 # We're using variable-length input prompts
405 ps1 = m.group('ps1')
422 ps1 = m.group('ps1')
406 ps2 = m.group('ps2')
423 ps2 = m.group('ps2')
407 ps1_len = len(ps1)
424 ps1_len = len(ps1)
408
425
409 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
426 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
410 if ps2:
427 if ps2:
411 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
428 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
412
429
413 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
430 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
414
431
415 if ip2py:
432 if ip2py:
416 # Convert source input from IPython into valid Python syntax
433 # Convert source input from IPython into valid Python syntax
417 source = self.ip2py(source)
434 source = self.ip2py(source)
418
435
419 # Divide want into lines; check that it's properly indented; and
436 # Divide want into lines; check that it's properly indented; and
420 # then strip the indentation. Spaces before the last newline should
437 # then strip the indentation. Spaces before the last newline should
421 # be preserved, so plain rstrip() isn't good enough.
438 # be preserved, so plain rstrip() isn't good enough.
422 want = m.group('want')
439 want = m.group('want')
423 want_lines = want.split('\n')
440 want_lines = want.split('\n')
424 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
441 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
425 del want_lines[-1] # forget final newline & spaces after it
442 del want_lines[-1] # forget final newline & spaces after it
426 self._check_prefix(want_lines, ' '*indent, name,
443 self._check_prefix(want_lines, ' '*indent, name,
427 lineno + len(source_lines))
444 lineno + len(source_lines))
428
445
429 # Remove ipython output prompt that might be present in the first line
446 # Remove ipython output prompt that might be present in the first line
430 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
447 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
431
448
432 want = '\n'.join([wl[indent:] for wl in want_lines])
449 want = '\n'.join([wl[indent:] for wl in want_lines])
433
450
434 # If `want` contains a traceback message, then extract it.
451 # If `want` contains a traceback message, then extract it.
435 m = self._EXCEPTION_RE.match(want)
452 m = self._EXCEPTION_RE.match(want)
436 if m:
453 if m:
437 exc_msg = m.group('msg')
454 exc_msg = m.group('msg')
438 else:
455 else:
439 exc_msg = None
456 exc_msg = None
440
457
441 # Extract options from the source.
458 # Extract options from the source.
442 options = self._find_options(source, name, lineno)
459 options = self._find_options(source, name, lineno)
443
460
444 return source, options, want, exc_msg
461 return source, options, want, exc_msg
445
462
446 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
463 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
447 """
464 """
448 Given the lines of a source string (including prompts and
465 Given the lines of a source string (including prompts and
449 leading indentation), check to make sure that every prompt is
466 leading indentation), check to make sure that every prompt is
450 followed by a space character. If any line is not followed by
467 followed by a space character. If any line is not followed by
451 a space character, then raise ValueError.
468 a space character, then raise ValueError.
452
469
453 Note: IPython-modified version which takes the input prompt length as a
470 Note: IPython-modified version which takes the input prompt length as a
454 parameter, so that prompts of variable length can be dealt with.
471 parameter, so that prompts of variable length can be dealt with.
455 """
472 """
456 space_idx = indent+ps1_len
473 space_idx = indent+ps1_len
457 min_len = space_idx+1
474 min_len = space_idx+1
458 for i, line in enumerate(lines):
475 for i, line in enumerate(lines):
459 if len(line) >= min_len and line[space_idx] != ' ':
476 if len(line) >= min_len and line[space_idx] != ' ':
460 raise ValueError('line %r of the docstring for %s '
477 raise ValueError('line %r of the docstring for %s '
461 'lacks blank after %s: %r' %
478 'lacks blank after %s: %r' %
462 (lineno+i+1, name,
479 (lineno+i+1, name,
463 line[indent:space_idx], line))
480 line[indent:space_idx], line))
464
481
465 SKIP = doctest.register_optionflag('SKIP')
482 SKIP = doctest.register_optionflag('SKIP')
466
483
467
484
468 class DocFileCase(doctest.DocFileCase):
485 class DocFileCase(doctest.DocFileCase):
469 """Overrides to provide filename
486 """Overrides to provide filename
470 """
487 """
471 def address(self):
488 def address(self):
472 return (self._dt_test.filename, None, None)
489 return (self._dt_test.filename, None, None)
473
490
474
491
475 class ExtensionDoctest(doctests.Doctest):
492 class ExtensionDoctest(doctests.Doctest):
476 """Nose Plugin that supports doctests in extension modules.
493 """Nose Plugin that supports doctests in extension modules.
477 """
494 """
478 name = 'extdoctest' # call nosetests with --with-extdoctest
495 name = 'extdoctest' # call nosetests with --with-extdoctest
479 enabled = True
496 enabled = True
480
497
481 def options(self, parser, env=os.environ):
498 def options(self, parser, env=os.environ):
482 Plugin.options(self, parser, env)
499 Plugin.options(self, parser, env)
483
500
484 def configure(self, options, config):
501 def configure(self, options, config):
485 Plugin.configure(self, options, config)
502 Plugin.configure(self, options, config)
486 self.doctest_tests = options.doctest_tests
503 self.doctest_tests = options.doctest_tests
487 self.extension = tolist(options.doctestExtension)
504 self.extension = tolist(options.doctestExtension)
488 self.finder = DocTestFinder()
505 self.finder = DocTestFinder()
489 self.parser = doctest.DocTestParser()
506 self.parser = doctest.DocTestParser()
490
507
491
508
492 def loadTestsFromExtensionModule(self,filename):
509 def loadTestsFromExtensionModule(self,filename):
493 bpath,mod = os.path.split(filename)
510 bpath,mod = os.path.split(filename)
494 modname = os.path.splitext(mod)[0]
511 modname = os.path.splitext(mod)[0]
495 try:
512 try:
496 sys.path.append(bpath)
513 sys.path.append(bpath)
497 module = __import__(modname)
514 module = __import__(modname)
498 tests = list(self.loadTestsFromModule(module))
515 tests = list(self.loadTestsFromModule(module))
499 finally:
516 finally:
500 sys.path.pop()
517 sys.path.pop()
501 return tests
518 return tests
502
519
503 def loadTestsFromFile(self, filename):
520 def loadTestsFromFile(self, filename):
521 print 'lTF',filename # dbg
522
504 if is_extension_module(filename):
523 if is_extension_module(filename):
505 for t in self.loadTestsFromExtensionModule(filename):
524 for t in self.loadTestsFromExtensionModule(filename):
506 yield t
525 yield t
507 else:
526 else:
508 ## for t in list(doctests.Doctest.loadTestsFromFile(self,filename)):
527 ## for t in list(doctests.Doctest.loadTestsFromFile(self,filename)):
509 ## yield t
528 ## yield t
510 pass
529 pass
511
530
512 if self.extension and anyp(filename.endswith, self.extension):
531 if self.extension and anyp(filename.endswith, self.extension):
513 #print 'lTF',filename # dbg
514 name = os.path.basename(filename)
532 name = os.path.basename(filename)
515 dh = open(filename)
533 dh = open(filename)
516 try:
534 try:
517 doc = dh.read()
535 doc = dh.read()
518 finally:
536 finally:
519 dh.close()
537 dh.close()
520 test = self.parser.get_doctest(
538 test = self.parser.get_doctest(
521 doc, globs={'__file__': filename}, name=name,
539 doc, globs={'__file__': filename}, name=name,
522 filename=filename, lineno=0)
540 filename=filename, lineno=0)
523 if test.examples:
541 if test.examples:
524 #print 'FileCase:',test.examples # dbg
542 #print 'FileCase:',test.examples # dbg
525 yield DocFileCase(test)
543 yield DocFileCase(test)
526 else:
544 else:
527 yield False # no tests to load
545 yield False # no tests to load
528
546
529 def wantFile(self,filename):
547 def wantFile(self,filename):
530 """Return whether the given filename should be scanned for tests.
548 """Return whether the given filename should be scanned for tests.
531
549
532 Modified version that accepts extension modules as valid containers for
550 Modified version that accepts extension modules as valid containers for
533 doctests.
551 doctests.
534 """
552 """
535 #print 'Filename:',filename # dbg
553 print 'Filename:',filename # dbg
536
554
537 # temporarily hardcoded list, will move to driver later
555 # temporarily hardcoded list, will move to driver later
538 exclude = ['IPython/external/',
556 exclude = ['IPython/external/',
539 'IPython/Extensions/ipy_',
557 'IPython/Extensions/ipy_',
540 'IPython/platutils_win32',
558 'IPython/platutils_win32',
541 'IPython/frontend/cocoa',
559 'IPython/frontend/cocoa',
542 'IPython_doctest_plugin',
560 'IPython_doctest_plugin',
543 'IPython/Gnuplot',
561 'IPython/Gnuplot',
544 'IPython/Extensions/PhysicalQIn']
562 'IPython/Extensions/PhysicalQIn']
545
563
546 for fex in exclude:
564 for fex in exclude:
547 if fex in filename: # substring
565 if fex in filename: # substring
548 #print '###>>> SKIP:',filename # dbg
566 #print '###>>> SKIP:',filename # dbg
549 return False
567 return False
550
568
551 if is_extension_module(filename):
569 if is_extension_module(filename):
552 return True
570 return True
553 else:
571 else:
554 return doctests.Doctest.wantFile(self,filename)
572 return doctests.Doctest.wantFile(self,filename)
555
573
556 # NOTE: the method below is a *copy* of the one in the nose doctests
574 # NOTE: the method below is almost a copy of the original one in nose, with
557 # plugin, but we have to replicate it here in order to have it resolve the
575 # a few modifications to control output checking.
558 # DocTestCase (last line) to our local copy, since the nose plugin doesn't
576
559 # provide a public hook for what TestCase class to use. The alternative
560 # would be to monkeypatch doctest in the stdlib, but that's ugly and
561 # brittle, since a change in plugin load order can break it. So for now,
562 # we just paste this in here, inelegant as this may be.
563
564 def loadTestsFromModule(self, module):
577 def loadTestsFromModule(self, module):
565 #print 'lTM',module # dbg
578 #print 'lTM',module # dbg
566
579
567 if not self.matches(module.__name__):
580 if not self.matches(module.__name__):
568 log.debug("Doctest doesn't want module %s", module)
581 log.debug("Doctest doesn't want module %s", module)
569 return
582 return
570 tests = self.finder.find(module)
583 tests = self.finder.find(module)
571 if not tests:
584 if not tests:
572 return
585 return
573 tests.sort()
586 tests.sort()
574 module_file = module.__file__
587 module_file = module.__file__
575 if module_file[-4:] in ('.pyc', '.pyo'):
588 if module_file[-4:] in ('.pyc', '.pyo'):
576 module_file = module_file[:-1]
589 module_file = module_file[:-1]
577 for test in tests:
590 for test in tests:
578 if not test.examples:
591 if not test.examples:
579 continue
592 continue
580 if not test.filename:
593 if not test.filename:
581 test.filename = module_file
594 test.filename = module_file
582 yield DocTestCase(test)
595 yield DocTestCase(test)
583
596
597 # always use whitespace and ellipsis options
598 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
599 #checker = DoctestOutputChecker()
600 checker = None
601 yield DocTestCase(test,
602 optionflags=optionflags,
603 checker=checker)
604
605
584
606
585 class IPythonDoctest(ExtensionDoctest):
607 class IPythonDoctest(ExtensionDoctest):
586 """Nose Plugin that supports doctests in extension modules.
608 """Nose Plugin that supports doctests in extension modules.
587 """
609 """
588 name = 'ipdoctest' # call nosetests with --with-ipdoctest
610 name = 'ipdoctest' # call nosetests with --with-ipdoctest
589 enabled = True
611 enabled = True
590
612
591 def configure(self, options, config):
613 def configure(self, options, config):
592
614
593 Plugin.configure(self, options, config)
615 Plugin.configure(self, options, config)
594 self.doctest_tests = options.doctest_tests
616 self.doctest_tests = options.doctest_tests
595 self.extension = tolist(options.doctestExtension)
617 self.extension = tolist(options.doctestExtension)
596 self.parser = IPDocTestParser()
618 self.parser = IPDocTestParser()
597 #self.finder = DocTestFinder(parser=IPDocTestParser())
598 self.finder = DocTestFinder(parser=self.parser)
619 self.finder = DocTestFinder(parser=self.parser)
General Comments 0
You need to be logged in to leave comments. Login now