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