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