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