##// END OF EJS Templates
Limit special-casing of _ variable to doctests....
Fernando Perez -
Show More
@@ -1,184 +1,195 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-2010 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__
24 24 import os
25 25 import sys
26 26 from types import MethodType
27 27
28 28 # our own
29 29 from . import tools
30 30
31 31 from IPython.frontend.terminal.interactiveshell import TerminalInteractiveShell
32 32
33 33 #-----------------------------------------------------------------------------
34 34 # Functions
35 35 #-----------------------------------------------------------------------------
36 36
37 37 # Hack to modify the %run command so we can sync the user's namespace with the
38 38 # test globals. Once we move over to a clean magic system, this will be done
39 39 # with much less ugliness.
40 40
41 41 class py_file_finder(object):
42 42 def __init__(self,test_filename):
43 43 self.test_filename = test_filename
44 44
45 45 def __call__(self,name):
46 46 from IPython.utils.path import get_py_filename
47 47 try:
48 48 return get_py_filename(name)
49 49 except IOError:
50 50 test_dir = os.path.dirname(self.test_filename)
51 51 new_path = os.path.join(test_dir,name)
52 52 return get_py_filename(new_path)
53 53
54 54
55 55 def _run_ns_sync(self,arg_s,runner=None):
56 56 """Modified version of %run that syncs testing namespaces.
57 57
58 58 This is strictly needed for running doctests that call %run.
59 59 """
60 60 #print('in run_ns_sync', arg_s, file=sys.stderr) # dbg
61 61 finder = py_file_finder(arg_s)
62 62 return get_ipython().magic_run_ori(arg_s, runner, finder)
63 63
64 64
65 65 class ipnsdict(dict):
66 66 """A special subclass of dict for use as an IPython namespace in doctests.
67 67
68 68 This subclass adds a simple checkpointing capability so that when testing
69 69 machinery clears it (we use it as the test execution context), it doesn't
70 70 get completely destroyed.
71
72 In addition, it can handle the presence of the '_' key in a special manner,
73 which is needed because of how Python's doctest machinery operates with
74 '_'. See constructor and :meth:`update` for details.
71 75 """
72 76
73 77 def __init__(self,*a):
74 78 dict.__init__(self,*a)
75 79 self._savedict = {}
80 # If this flag is True, the .update() method will unconditionally
81 # remove a key named '_'. This is so that such a dict can be used as a
82 # namespace in doctests that call '_'.
83 self.protect_underscore = False
76 84
77 85 def clear(self):
78 86 dict.clear(self)
79 87 self.update(self._savedict)
80 88
81 89 def _checkpoint(self):
82 90 self._savedict.clear()
83 91 self._savedict.update(self)
84 92
85 93 def update(self,other):
86 94 self._checkpoint()
87 95 dict.update(self,other)
88 96
89 # If '_' is in the namespace, python won't set it when executing code
90 # *in doctests*, and we have multiple doctests that use '_'. So we
91 # ensure that the namespace is always 'clean' of it before it's used
92 # for test code execution. Note: this means that outside of doctests,
93 # our own testing
94 self.pop('_',None)
97 if self.protect_underscore:
98 # If '_' is in the namespace, python won't set it when executing
99 # code *in doctests*, and we have multiple doctests that use '_'.
100 # So we ensure that the namespace is always 'clean' of it before
101 # it's used for test code execution.
102 # This flag is only turned on by the doctest machinery, so that
103 # normal test code can assume the _ key is updated like any other
104 # key and can test for its presence after cell executions.
105 self.pop('_', None)
95 106
96 107 # The builtins namespace must *always* be the real __builtin__ module,
97 108 # else weird stuff happens. The main ipython code does have provisions
98 109 # to ensure this after %run, but since in this class we do some
99 110 # aggressive low-level cleaning of the execution namespace, we need to
100 111 # correct for that ourselves, to ensure consitency with the 'real'
101 112 # ipython.
102 113 self['__builtins__'] = __builtin__
103 114
104 115
105 116 def get_ipython():
106 117 # This will get replaced by the real thing once we start IPython below
107 118 return start_ipython()
108 119
109 120
110 121 # A couple of methods to override those in the running IPython to interact
111 122 # better with doctest (doctest captures on raw stdout, so we need to direct
112 123 # various types of output there otherwise it will miss them).
113 124
114 125 def xsys(self, cmd):
115 126 """Replace the default system call with a capturing one for doctest.
116 127 """
117 128 # We use getoutput, but we need to strip it because pexpect captures
118 129 # the trailing newline differently from commands.getoutput
119 130 print(self.getoutput(cmd, split=False).rstrip(), end='', file=sys.stdout)
120 131 sys.stdout.flush()
121 132
122 133
123 134 def _showtraceback(self, etype, evalue, stb):
124 135 """Print the traceback purely on stdout for doctest to capture it.
125 136 """
126 137 print(self.InteractiveTB.stb2text(stb), file=sys.stdout)
127 138
128 139
129 140 def start_ipython():
130 141 """Start a global IPython shell, which we need for IPython-specific syntax.
131 142 """
132 143 global get_ipython
133 144
134 145 # This function should only ever run once!
135 146 if hasattr(start_ipython, 'already_called'):
136 147 return
137 148 start_ipython.already_called = True
138 149
139 150 # Store certain global objects that IPython modifies
140 151 _displayhook = sys.displayhook
141 152 _excepthook = sys.excepthook
142 153 _main = sys.modules.get('__main__')
143 154
144 155 # Create custom argv and namespaces for our IPython to be test-friendly
145 156 config = tools.default_config()
146 157
147 158 # Create and initialize our test-friendly IPython instance.
148 159 shell = TerminalInteractiveShell.instance(config=config,
149 160 user_ns=ipnsdict(),
150 161 user_global_ns={}
151 162 )
152 163
153 164 # A few more tweaks needed for playing nicely with doctests...
154 165
155 166 # These traps are normally only active for interactive use, set them
156 167 # permanently since we'll be mocking interactive sessions.
157 168 shell.builtin_trap.activate()
158 169
159 170 # Modify the IPython system call with one that uses getoutput, so that we
160 171 # can capture subcommands and print them to Python's stdout, otherwise the
161 172 # doctest machinery would miss them.
162 173 shell.system = MethodType(xsys, shell, TerminalInteractiveShell)
163 174
164 175
165 176 shell._showtraceback = MethodType(_showtraceback, shell,
166 177 TerminalInteractiveShell)
167 178
168 179 # IPython is ready, now clean up some global state...
169 180
170 181 # Deactivate the various python system hooks added by ipython for
171 182 # interactive convenience so we don't confuse the doctest system
172 183 sys.modules['__main__'] = _main
173 184 sys.displayhook = _displayhook
174 185 sys.excepthook = _excepthook
175 186
176 187 # So that ipython magics and aliases can be doctested (they work by making
177 188 # a call into a global _ip object). Also make the top-level get_ipython
178 189 # now return this without recursively calling here again.
179 190 _ip = shell
180 191 get_ipython = _ip.get_ipython
181 192 __builtin__._ip = _ip
182 193 __builtin__.get_ipython = get_ipython
183 194
184 195 return _ip
@@ -1,786 +1,793 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 starting ipython with the
7 7 flag '--nopprint', by setting pprint to 0 in your ipythonrc file, or by
8 8 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 22 import __builtin__
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.getcwd()
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 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 _ip.user_ns.update(self._dt_test.globs)
275 275 self._dt_test.globs = _ip.user_ns
276 # IPython must protect the _ key in the namespace (it can't exist)
277 # so that Python's doctest code sets it naturally, so we enable
278 # this feature of our testing namespace.
279 _ip.user_ns.protect_underscore = True
276 280
277 281 super(DocTestCase, self).setUp()
278 282
279 283 def tearDown(self):
280 284
281 285 # Undo the test.globs reassignment we made, so that the parent class
282 286 # teardown doesn't destroy the ipython namespace
283 287 if isinstance(self._dt_test.examples[0],IPExample):
284 288 self._dt_test.globs = self._dt_test_globs_ori
289 # Restore the behavior of the '_' key in the user namespace to
290 # normal after each doctest, so that unittests behave normally
291 _ip.user_ns.protect_underscore = False
285 292
286 293 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
287 294 # it does look like one to me: its tearDown method tries to run
288 295 #
289 296 # delattr(__builtin__, self._result_var)
290 297 #
291 298 # without checking that the attribute really is there; it implicitly
292 299 # assumes it should have been set via displayhook. But if the
293 300 # displayhook was never called, this doesn't necessarily happen. I
294 301 # haven't been able to find a little self-contained example outside of
295 302 # ipython that would show the problem so I can report it to the nose
296 303 # team, but it does happen a lot in our code.
297 304 #
298 305 # So here, we just protect as narrowly as possible by trapping an
299 306 # attribute error whose message would be the name of self._result_var,
300 307 # and letting any other error propagate.
301 308 try:
302 309 super(DocTestCase, self).tearDown()
303 310 except AttributeError, exc:
304 311 if exc.args[0] != self._result_var:
305 312 raise
306 313
307 314
308 315 # A simple subclassing of the original with a different class name, so we can
309 316 # distinguish and treat differently IPython examples from pure python ones.
310 317 class IPExample(doctest.Example): pass
311 318
312 319
313 320 class IPExternalExample(doctest.Example):
314 321 """Doctest examples to be run in an external process."""
315 322
316 323 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
317 324 options=None):
318 325 # Parent constructor
319 326 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
320 327
321 328 # An EXTRA newline is needed to prevent pexpect hangs
322 329 self.source += '\n'
323 330
324 331
325 332 class IPDocTestParser(doctest.DocTestParser):
326 333 """
327 334 A class used to parse strings containing doctest examples.
328 335
329 336 Note: This is a version modified to properly recognize IPython input and
330 337 convert any IPython examples into valid Python ones.
331 338 """
332 339 # This regular expression is used to find doctest examples in a
333 340 # string. It defines three groups: `source` is the source code
334 341 # (including leading indentation and prompts); `indent` is the
335 342 # indentation of the first (PS1) line of the source code; and
336 343 # `want` is the expected output (including leading indentation).
337 344
338 345 # Classic Python prompts or default IPython ones
339 346 _PS1_PY = r'>>>'
340 347 _PS2_PY = r'\.\.\.'
341 348
342 349 _PS1_IP = r'In\ \[\d+\]:'
343 350 _PS2_IP = r'\ \ \ \.\.\.+:'
344 351
345 352 _RE_TPL = r'''
346 353 # Source consists of a PS1 line followed by zero or more PS2 lines.
347 354 (?P<source>
348 355 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
349 356 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
350 357 \n? # a newline
351 358 # Want consists of any non-blank lines that do not start with PS1.
352 359 (?P<want> (?:(?![ ]*$) # Not a blank line
353 360 (?![ ]*%s) # Not a line starting with PS1
354 361 (?![ ]*%s) # Not a line starting with PS2
355 362 .*$\n? # But any other line
356 363 )*)
357 364 '''
358 365
359 366 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
360 367 re.MULTILINE | re.VERBOSE)
361 368
362 369 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
363 370 re.MULTILINE | re.VERBOSE)
364 371
365 372 # Mark a test as being fully random. In this case, we simply append the
366 373 # random marker ('#random') to each individual example's output. This way
367 374 # we don't need to modify any other code.
368 375 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
369 376
370 377 # Mark tests to be executed in an external process - currently unsupported.
371 378 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
372 379
373 380 def ip2py(self,source):
374 381 """Convert input IPython source into valid Python."""
375 382 out = []
376 383 newline = out.append
377 384 #print 'IPSRC:\n',source,'\n###' # dbg
378 385 # The input source must be first stripped of all bracketing whitespace
379 386 # and turned into lines, so it looks to the parser like regular user
380 387 # input
381 388 for lnum,line in enumerate(source.strip().splitlines()):
382 389 newline(_ip.prefilter(line,lnum>0))
383 390 newline('') # ensure a closing newline, needed by doctest
384 391 #print "PYSRC:", '\n'.join(out) # dbg
385 392 return '\n'.join(out)
386 393
387 394 def parse(self, string, name='<string>'):
388 395 """
389 396 Divide the given string into examples and intervening text,
390 397 and return them as a list of alternating Examples and strings.
391 398 Line numbers for the Examples are 0-based. The optional
392 399 argument `name` is a name identifying this string, and is only
393 400 used for error messages.
394 401 """
395 402
396 403 #print 'Parse string:\n',string # dbg
397 404
398 405 string = string.expandtabs()
399 406 # If all lines begin with the same indentation, then strip it.
400 407 min_indent = self._min_indent(string)
401 408 if min_indent > 0:
402 409 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
403 410
404 411 output = []
405 412 charno, lineno = 0, 0
406 413
407 414 # We make 'all random' tests by adding the '# random' mark to every
408 415 # block of output in the test.
409 416 if self._RANDOM_TEST.search(string):
410 417 random_marker = '\n# random'
411 418 else:
412 419 random_marker = ''
413 420
414 421 # Whether to convert the input from ipython to python syntax
415 422 ip2py = False
416 423 # Find all doctest examples in the string. First, try them as Python
417 424 # examples, then as IPython ones
418 425 terms = list(self._EXAMPLE_RE_PY.finditer(string))
419 426 if terms:
420 427 # Normal Python example
421 428 #print '-'*70 # dbg
422 429 #print 'PyExample, Source:\n',string # dbg
423 430 #print '-'*70 # dbg
424 431 Example = doctest.Example
425 432 else:
426 433 # It's an ipython example. Note that IPExamples are run
427 434 # in-process, so their syntax must be turned into valid python.
428 435 # IPExternalExamples are run out-of-process (via pexpect) so they
429 436 # don't need any filtering (a real ipython will be executing them).
430 437 terms = list(self._EXAMPLE_RE_IP.finditer(string))
431 438 if self._EXTERNAL_IP.search(string):
432 439 #print '-'*70 # dbg
433 440 #print 'IPExternalExample, Source:\n',string # dbg
434 441 #print '-'*70 # dbg
435 442 Example = IPExternalExample
436 443 else:
437 444 #print '-'*70 # dbg
438 445 #print 'IPExample, Source:\n',string # dbg
439 446 #print '-'*70 # dbg
440 447 Example = IPExample
441 448 ip2py = True
442 449
443 450 for m in terms:
444 451 # Add the pre-example text to `output`.
445 452 output.append(string[charno:m.start()])
446 453 # Update lineno (lines before this example)
447 454 lineno += string.count('\n', charno, m.start())
448 455 # Extract info from the regexp match.
449 456 (source, options, want, exc_msg) = \
450 457 self._parse_example(m, name, lineno,ip2py)
451 458
452 459 # Append the random-output marker (it defaults to empty in most
453 460 # cases, it's only non-empty for 'all-random' tests):
454 461 want += random_marker
455 462
456 463 if Example is IPExternalExample:
457 464 options[doctest.NORMALIZE_WHITESPACE] = True
458 465 want += '\n'
459 466
460 467 # Create an Example, and add it to the list.
461 468 if not self._IS_BLANK_OR_COMMENT(source):
462 469 output.append(Example(source, want, exc_msg,
463 470 lineno=lineno,
464 471 indent=min_indent+len(m.group('indent')),
465 472 options=options))
466 473 # Update lineno (lines inside this example)
467 474 lineno += string.count('\n', m.start(), m.end())
468 475 # Update charno.
469 476 charno = m.end()
470 477 # Add any remaining post-example text to `output`.
471 478 output.append(string[charno:])
472 479 return output
473 480
474 481 def _parse_example(self, m, name, lineno,ip2py=False):
475 482 """
476 483 Given a regular expression match from `_EXAMPLE_RE` (`m`),
477 484 return a pair `(source, want)`, where `source` is the matched
478 485 example's source code (with prompts and indentation stripped);
479 486 and `want` is the example's expected output (with indentation
480 487 stripped).
481 488
482 489 `name` is the string's name, and `lineno` is the line number
483 490 where the example starts; both are used for error messages.
484 491
485 492 Optional:
486 493 `ip2py`: if true, filter the input via IPython to convert the syntax
487 494 into valid python.
488 495 """
489 496
490 497 # Get the example's indentation level.
491 498 indent = len(m.group('indent'))
492 499
493 500 # Divide source into lines; check that they're properly
494 501 # indented; and then strip their indentation & prompts.
495 502 source_lines = m.group('source').split('\n')
496 503
497 504 # We're using variable-length input prompts
498 505 ps1 = m.group('ps1')
499 506 ps2 = m.group('ps2')
500 507 ps1_len = len(ps1)
501 508
502 509 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
503 510 if ps2:
504 511 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
505 512
506 513 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
507 514
508 515 if ip2py:
509 516 # Convert source input from IPython into valid Python syntax
510 517 source = self.ip2py(source)
511 518
512 519 # Divide want into lines; check that it's properly indented; and
513 520 # then strip the indentation. Spaces before the last newline should
514 521 # be preserved, so plain rstrip() isn't good enough.
515 522 want = m.group('want')
516 523 want_lines = want.split('\n')
517 524 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
518 525 del want_lines[-1] # forget final newline & spaces after it
519 526 self._check_prefix(want_lines, ' '*indent, name,
520 527 lineno + len(source_lines))
521 528
522 529 # Remove ipython output prompt that might be present in the first line
523 530 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
524 531
525 532 want = '\n'.join([wl[indent:] for wl in want_lines])
526 533
527 534 # If `want` contains a traceback message, then extract it.
528 535 m = self._EXCEPTION_RE.match(want)
529 536 if m:
530 537 exc_msg = m.group('msg')
531 538 else:
532 539 exc_msg = None
533 540
534 541 # Extract options from the source.
535 542 options = self._find_options(source, name, lineno)
536 543
537 544 return source, options, want, exc_msg
538 545
539 546 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
540 547 """
541 548 Given the lines of a source string (including prompts and
542 549 leading indentation), check to make sure that every prompt is
543 550 followed by a space character. If any line is not followed by
544 551 a space character, then raise ValueError.
545 552
546 553 Note: IPython-modified version which takes the input prompt length as a
547 554 parameter, so that prompts of variable length can be dealt with.
548 555 """
549 556 space_idx = indent+ps1_len
550 557 min_len = space_idx+1
551 558 for i, line in enumerate(lines):
552 559 if len(line) >= min_len and line[space_idx] != ' ':
553 560 raise ValueError('line %r of the docstring for %s '
554 561 'lacks blank after %s: %r' %
555 562 (lineno+i+1, name,
556 563 line[indent:space_idx], line))
557 564
558 565
559 566 SKIP = doctest.register_optionflag('SKIP')
560 567
561 568
562 569 class IPDocTestRunner(doctest.DocTestRunner,object):
563 570 """Test runner that synchronizes the IPython namespace with test globals.
564 571 """
565 572
566 573 def run(self, test, compileflags=None, out=None, clear_globs=True):
567 574
568 575 # Hack: ipython needs access to the execution context of the example,
569 576 # so that it can propagate user variables loaded by %run into
570 577 # test.globs. We put them here into our modified %run as a function
571 578 # attribute. Our new %run will then only make the namespace update
572 579 # when called (rather than unconconditionally updating test.globs here
573 580 # for all examples, most of which won't be calling %run anyway).
574 581 #_ip._ipdoctest_test_globs = test.globs
575 582 #_ip._ipdoctest_test_filename = test.filename
576 583
577 584 test.globs.update(_ip.user_ns)
578 585
579 586 return super(IPDocTestRunner,self).run(test,
580 587 compileflags,out,clear_globs)
581 588
582 589
583 590 class DocFileCase(doctest.DocFileCase):
584 591 """Overrides to provide filename
585 592 """
586 593 def address(self):
587 594 return (self._dt_test.filename, None, None)
588 595
589 596
590 597 class ExtensionDoctest(doctests.Doctest):
591 598 """Nose Plugin that supports doctests in extension modules.
592 599 """
593 600 name = 'extdoctest' # call nosetests with --with-extdoctest
594 601 enabled = True
595 602
596 603 def __init__(self,exclude_patterns=None):
597 604 """Create a new ExtensionDoctest plugin.
598 605
599 606 Parameters
600 607 ----------
601 608
602 609 exclude_patterns : sequence of strings, optional
603 610 These patterns are compiled as regular expressions, subsequently used
604 611 to exclude any filename which matches them from inclusion in the test
605 612 suite (using pattern.search(), NOT pattern.match() ).
606 613 """
607 614
608 615 if exclude_patterns is None:
609 616 exclude_patterns = []
610 617 self.exclude_patterns = map(re.compile,exclude_patterns)
611 618 doctests.Doctest.__init__(self)
612 619
613 620 def options(self, parser, env=os.environ):
614 621 Plugin.options(self, parser, env)
615 622 parser.add_option('--doctest-tests', action='store_true',
616 623 dest='doctest_tests',
617 624 default=env.get('NOSE_DOCTEST_TESTS',True),
618 625 help="Also look for doctests in test modules. "
619 626 "Note that classes, methods and functions should "
620 627 "have either doctests or non-doctest tests, "
621 628 "not both. [NOSE_DOCTEST_TESTS]")
622 629 parser.add_option('--doctest-extension', action="append",
623 630 dest="doctestExtension",
624 631 help="Also look for doctests in files with "
625 632 "this extension [NOSE_DOCTEST_EXTENSION]")
626 633 # Set the default as a list, if given in env; otherwise
627 634 # an additional value set on the command line will cause
628 635 # an error.
629 636 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
630 637 if env_setting is not None:
631 638 parser.set_defaults(doctestExtension=tolist(env_setting))
632 639
633 640
634 641 def configure(self, options, config):
635 642 Plugin.configure(self, options, config)
636 643 self.doctest_tests = options.doctest_tests
637 644 self.extension = tolist(options.doctestExtension)
638 645
639 646 self.parser = doctest.DocTestParser()
640 647 self.finder = DocTestFinder()
641 648 self.checker = IPDoctestOutputChecker()
642 649 self.globs = None
643 650 self.extraglobs = None
644 651
645 652
646 653 def loadTestsFromExtensionModule(self,filename):
647 654 bpath,mod = os.path.split(filename)
648 655 modname = os.path.splitext(mod)[0]
649 656 try:
650 657 sys.path.append(bpath)
651 658 module = __import__(modname)
652 659 tests = list(self.loadTestsFromModule(module))
653 660 finally:
654 661 sys.path.pop()
655 662 return tests
656 663
657 664 # NOTE: the method below is almost a copy of the original one in nose, with
658 665 # a few modifications to control output checking.
659 666
660 667 def loadTestsFromModule(self, module):
661 668 #print '*** ipdoctest - lTM',module # dbg
662 669
663 670 if not self.matches(module.__name__):
664 671 log.debug("Doctest doesn't want module %s", module)
665 672 return
666 673
667 674 tests = self.finder.find(module,globs=self.globs,
668 675 extraglobs=self.extraglobs)
669 676 if not tests:
670 677 return
671 678
672 679 # always use whitespace and ellipsis options
673 680 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
674 681
675 682 tests.sort()
676 683 module_file = module.__file__
677 684 if module_file[-4:] in ('.pyc', '.pyo'):
678 685 module_file = module_file[:-1]
679 686 for test in tests:
680 687 if not test.examples:
681 688 continue
682 689 if not test.filename:
683 690 test.filename = module_file
684 691
685 692 yield DocTestCase(test,
686 693 optionflags=optionflags,
687 694 checker=self.checker)
688 695
689 696
690 697 def loadTestsFromFile(self, filename):
691 698 #print "ipdoctest - from file", filename # dbg
692 699 if is_extension_module(filename):
693 700 for t in self.loadTestsFromExtensionModule(filename):
694 701 yield t
695 702 else:
696 703 if self.extension and anyp(filename.endswith, self.extension):
697 704 name = os.path.basename(filename)
698 705 dh = open(filename)
699 706 try:
700 707 doc = dh.read()
701 708 finally:
702 709 dh.close()
703 710 test = self.parser.get_doctest(
704 711 doc, globs={'__file__': filename}, name=name,
705 712 filename=filename, lineno=0)
706 713 if test.examples:
707 714 #print 'FileCase:',test.examples # dbg
708 715 yield DocFileCase(test)
709 716 else:
710 717 yield False # no tests to load
711 718
712 719 def wantFile(self,filename):
713 720 """Return whether the given filename should be scanned for tests.
714 721
715 722 Modified version that accepts extension modules as valid containers for
716 723 doctests.
717 724 """
718 725 #print '*** ipdoctest- wantFile:',filename # dbg
719 726
720 727 for pat in self.exclude_patterns:
721 728 if pat.search(filename):
722 729 # print '###>>> SKIP:',filename # dbg
723 730 return False
724 731
725 732 if is_extension_module(filename):
726 733 return True
727 734 else:
728 735 return doctests.Doctest.wantFile(self,filename)
729 736
730 737
731 738 class IPythonDoctest(ExtensionDoctest):
732 739 """Nose Plugin that supports doctests in extension modules.
733 740 """
734 741 name = 'ipdoctest' # call nosetests with --with-ipdoctest
735 742 enabled = True
736 743
737 744 def makeTest(self, obj, parent):
738 745 """Look for doctests in the given object, which will be a
739 746 function, method or class.
740 747 """
741 748 #print 'Plugin analyzing:', obj, parent # dbg
742 749 # always use whitespace and ellipsis options
743 750 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
744 751
745 752 doctests = self.finder.find(obj, module=getmodule(parent))
746 753 if doctests:
747 754 for test in doctests:
748 755 if len(test.examples) == 0:
749 756 continue
750 757
751 758 yield DocTestCase(test, obj=obj,
752 759 optionflags=optionflags,
753 760 checker=self.checker)
754 761
755 762 def options(self, parser, env=os.environ):
756 763 #print "Options for nose plugin:", self.name # dbg
757 764 Plugin.options(self, parser, env)
758 765 parser.add_option('--ipdoctest-tests', action='store_true',
759 766 dest='ipdoctest_tests',
760 767 default=env.get('NOSE_IPDOCTEST_TESTS',True),
761 768 help="Also look for doctests in test modules. "
762 769 "Note that classes, methods and functions should "
763 770 "have either doctests or non-doctest tests, "
764 771 "not both. [NOSE_IPDOCTEST_TESTS]")
765 772 parser.add_option('--ipdoctest-extension', action="append",
766 773 dest="ipdoctest_extension",
767 774 help="Also look for doctests in files with "
768 775 "this extension [NOSE_IPDOCTEST_EXTENSION]")
769 776 # Set the default as a list, if given in env; otherwise
770 777 # an additional value set on the command line will cause
771 778 # an error.
772 779 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
773 780 if env_setting is not None:
774 781 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
775 782
776 783 def configure(self, options, config):
777 784 #print "Configuring nose plugin:", self.name # dbg
778 785 Plugin.configure(self, options, config)
779 786 self.doctest_tests = options.ipdoctest_tests
780 787 self.extension = tolist(options.ipdoctest_extension)
781 788
782 789 self.parser = IPDocTestParser()
783 790 self.finder = DocTestFinder(parser=self.parser)
784 791 self.checker = IPDoctestOutputChecker()
785 792 self.globs = None
786 793 self.extraglobs = None
General Comments 0
You need to be logged in to leave comments. Login now