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