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