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