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