##// END OF EJS Templates
Add a workaround for a nose problem so the test suite runs with nose 0.11.x
Fernando Perez -
Show More
@@ -1,917 +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 doctests.DocTestCase.setUp(self)
442 super(DocTestCase, self).setUp()
443
444 def tearDown(self):
445 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
446 # it does look like one to me: its tearDown method tries to run
447 #
448 # delattr(__builtin__, self._result_var)
449 #
450 # without checking that the attribute really is there; it implicitly
451 # assumes it should have been set via displayhook. But if the
452 # displayhook was never called, this doesn't necessarily happen. I
453 # haven't been able to find a little self-contained example outside of
454 # ipython that would show the problem so I can report it to the nose
455 # team, but it does happen a lot in our code.
456 #
457 # So here, we just protect as narrowly as possible by trapping an
458 # attribute error whose message would be the name of self._result_var,
459 # and letting any other error propagate.
460 try:
461 super(DocTestCase, self).tearDown()
462 except AttributeError, exc:
463 if exc.message != self._result_var:
464 raise
443 465
444 466
445 467 # A simple subclassing of the original with a different class name, so we can
446 468 # distinguish and treat differently IPython examples from pure python ones.
447 469 class IPExample(doctest.Example): pass
448 470
449 471
450 472 class IPExternalExample(doctest.Example):
451 473 """Doctest examples to be run in an external process."""
452 474
453 475 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
454 476 options=None):
455 477 # Parent constructor
456 478 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
457 479
458 480 # An EXTRA newline is needed to prevent pexpect hangs
459 481 self.source += '\n'
460 482
461 483
462 484 class IPDocTestParser(doctest.DocTestParser):
463 485 """
464 486 A class used to parse strings containing doctest examples.
465 487
466 488 Note: This is a version modified to properly recognize IPython input and
467 489 convert any IPython examples into valid Python ones.
468 490 """
469 491 # This regular expression is used to find doctest examples in a
470 492 # string. It defines three groups: `source` is the source code
471 493 # (including leading indentation and prompts); `indent` is the
472 494 # indentation of the first (PS1) line of the source code; and
473 495 # `want` is the expected output (including leading indentation).
474 496
475 497 # Classic Python prompts or default IPython ones
476 498 _PS1_PY = r'>>>'
477 499 _PS2_PY = r'\.\.\.'
478 500
479 501 _PS1_IP = r'In\ \[\d+\]:'
480 502 _PS2_IP = r'\ \ \ \.\.\.+:'
481 503
482 504 _RE_TPL = r'''
483 505 # Source consists of a PS1 line followed by zero or more PS2 lines.
484 506 (?P<source>
485 507 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
486 508 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
487 509 \n? # a newline
488 510 # Want consists of any non-blank lines that do not start with PS1.
489 511 (?P<want> (?:(?![ ]*$) # Not a blank line
490 512 (?![ ]*%s) # Not a line starting with PS1
491 513 (?![ ]*%s) # Not a line starting with PS2
492 514 .*$\n? # But any other line
493 515 )*)
494 516 '''
495 517
496 518 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
497 519 re.MULTILINE | re.VERBOSE)
498 520
499 521 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
500 522 re.MULTILINE | re.VERBOSE)
501 523
502 524 # Mark a test as being fully random. In this case, we simply append the
503 525 # random marker ('#random') to each individual example's output. This way
504 526 # we don't need to modify any other code.
505 527 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
506 528
507 529 # Mark tests to be executed in an external process - currently unsupported.
508 530 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
509 531
510 532 def ip2py(self,source):
511 533 """Convert input IPython source into valid Python."""
512 534 out = []
513 535 newline = out.append
514 536 #print 'IPSRC:\n',source,'\n###' # dbg
515 537 # The input source must be first stripped of all bracketing whitespace
516 538 # and turned into lines, so it looks to the parser like regular user
517 539 # input
518 540 for lnum,line in enumerate(source.strip().splitlines()):
519 541 newline(_ip.IP.prefilter(line,lnum>0))
520 542 newline('') # ensure a closing newline, needed by doctest
521 543 #print "PYSRC:", '\n'.join(out) # dbg
522 544 return '\n'.join(out)
523 545
524 546 def parse(self, string, name='<string>'):
525 547 """
526 548 Divide the given string into examples and intervening text,
527 549 and return them as a list of alternating Examples and strings.
528 550 Line numbers for the Examples are 0-based. The optional
529 551 argument `name` is a name identifying this string, and is only
530 552 used for error messages.
531 553 """
532 554
533 555 #print 'Parse string:\n',string # dbg
534 556
535 557 string = string.expandtabs()
536 558 # If all lines begin with the same indentation, then strip it.
537 559 min_indent = self._min_indent(string)
538 560 if min_indent > 0:
539 561 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
540 562
541 563 output = []
542 564 charno, lineno = 0, 0
543 565
544 566 # We make 'all random' tests by adding the '# random' mark to every
545 567 # block of output in the test.
546 568 if self._RANDOM_TEST.search(string):
547 569 random_marker = '\n# random'
548 570 else:
549 571 random_marker = ''
550 572
551 573 # Whether to convert the input from ipython to python syntax
552 574 ip2py = False
553 575 # Find all doctest examples in the string. First, try them as Python
554 576 # examples, then as IPython ones
555 577 terms = list(self._EXAMPLE_RE_PY.finditer(string))
556 578 if terms:
557 579 # Normal Python example
558 580 #print '-'*70 # dbg
559 581 #print 'PyExample, Source:\n',string # dbg
560 582 #print '-'*70 # dbg
561 583 Example = doctest.Example
562 584 else:
563 585 # It's an ipython example. Note that IPExamples are run
564 586 # in-process, so their syntax must be turned into valid python.
565 587 # IPExternalExamples are run out-of-process (via pexpect) so they
566 588 # don't need any filtering (a real ipython will be executing them).
567 589 terms = list(self._EXAMPLE_RE_IP.finditer(string))
568 590 if self._EXTERNAL_IP.search(string):
569 591 #print '-'*70 # dbg
570 592 #print 'IPExternalExample, Source:\n',string # dbg
571 593 #print '-'*70 # dbg
572 594 Example = IPExternalExample
573 595 else:
574 596 #print '-'*70 # dbg
575 597 #print 'IPExample, Source:\n',string # dbg
576 598 #print '-'*70 # dbg
577 599 Example = IPExample
578 600 ip2py = True
579 601
580 602 for m in terms:
581 603 # Add the pre-example text to `output`.
582 604 output.append(string[charno:m.start()])
583 605 # Update lineno (lines before this example)
584 606 lineno += string.count('\n', charno, m.start())
585 607 # Extract info from the regexp match.
586 608 (source, options, want, exc_msg) = \
587 609 self._parse_example(m, name, lineno,ip2py)
588 610
589 611 # Append the random-output marker (it defaults to empty in most
590 612 # cases, it's only non-empty for 'all-random' tests):
591 613 want += random_marker
592 614
593 615 if Example is IPExternalExample:
594 616 options[doctest.NORMALIZE_WHITESPACE] = True
595 617 want += '\n'
596 618
597 619 # Create an Example, and add it to the list.
598 620 if not self._IS_BLANK_OR_COMMENT(source):
599 621 output.append(Example(source, want, exc_msg,
600 622 lineno=lineno,
601 623 indent=min_indent+len(m.group('indent')),
602 624 options=options))
603 625 # Update lineno (lines inside this example)
604 626 lineno += string.count('\n', m.start(), m.end())
605 627 # Update charno.
606 628 charno = m.end()
607 629 # Add any remaining post-example text to `output`.
608 630 output.append(string[charno:])
609 631 return output
610 632
611 633 def _parse_example(self, m, name, lineno,ip2py=False):
612 634 """
613 635 Given a regular expression match from `_EXAMPLE_RE` (`m`),
614 636 return a pair `(source, want)`, where `source` is the matched
615 637 example's source code (with prompts and indentation stripped);
616 638 and `want` is the example's expected output (with indentation
617 639 stripped).
618 640
619 641 `name` is the string's name, and `lineno` is the line number
620 642 where the example starts; both are used for error messages.
621 643
622 644 Optional:
623 645 `ip2py`: if true, filter the input via IPython to convert the syntax
624 646 into valid python.
625 647 """
626 648
627 649 # Get the example's indentation level.
628 650 indent = len(m.group('indent'))
629 651
630 652 # Divide source into lines; check that they're properly
631 653 # indented; and then strip their indentation & prompts.
632 654 source_lines = m.group('source').split('\n')
633 655
634 656 # We're using variable-length input prompts
635 657 ps1 = m.group('ps1')
636 658 ps2 = m.group('ps2')
637 659 ps1_len = len(ps1)
638 660
639 661 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
640 662 if ps2:
641 663 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
642 664
643 665 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
644 666
645 667 if ip2py:
646 668 # Convert source input from IPython into valid Python syntax
647 669 source = self.ip2py(source)
648 670
649 671 # Divide want into lines; check that it's properly indented; and
650 672 # then strip the indentation. Spaces before the last newline should
651 673 # be preserved, so plain rstrip() isn't good enough.
652 674 want = m.group('want')
653 675 want_lines = want.split('\n')
654 676 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
655 677 del want_lines[-1] # forget final newline & spaces after it
656 678 self._check_prefix(want_lines, ' '*indent, name,
657 679 lineno + len(source_lines))
658 680
659 681 # Remove ipython output prompt that might be present in the first line
660 682 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
661 683
662 684 want = '\n'.join([wl[indent:] for wl in want_lines])
663 685
664 686 # If `want` contains a traceback message, then extract it.
665 687 m = self._EXCEPTION_RE.match(want)
666 688 if m:
667 689 exc_msg = m.group('msg')
668 690 else:
669 691 exc_msg = None
670 692
671 693 # Extract options from the source.
672 694 options = self._find_options(source, name, lineno)
673 695
674 696 return source, options, want, exc_msg
675 697
676 698 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
677 699 """
678 700 Given the lines of a source string (including prompts and
679 701 leading indentation), check to make sure that every prompt is
680 702 followed by a space character. If any line is not followed by
681 703 a space character, then raise ValueError.
682 704
683 705 Note: IPython-modified version which takes the input prompt length as a
684 706 parameter, so that prompts of variable length can be dealt with.
685 707 """
686 708 space_idx = indent+ps1_len
687 709 min_len = space_idx+1
688 710 for i, line in enumerate(lines):
689 711 if len(line) >= min_len and line[space_idx] != ' ':
690 712 raise ValueError('line %r of the docstring for %s '
691 713 'lacks blank after %s: %r' %
692 714 (lineno+i+1, name,
693 715 line[indent:space_idx], line))
694 716
695 717
696 718 SKIP = doctest.register_optionflag('SKIP')
697 719
698 720
699 721 class IPDocTestRunner(doctest.DocTestRunner,object):
700 722 """Test runner that synchronizes the IPython namespace with test globals.
701 723 """
702 724
703 725 def run(self, test, compileflags=None, out=None, clear_globs=True):
704 726
705 727 # Hack: ipython needs access to the execution context of the example,
706 728 # so that it can propagate user variables loaded by %run into
707 729 # test.globs. We put them here into our modified %run as a function
708 730 # attribute. Our new %run will then only make the namespace update
709 731 # when called (rather than unconconditionally updating test.globs here
710 732 # for all examples, most of which won't be calling %run anyway).
711 733 _run_ns_sync.test_globs = test.globs
712 734 _run_ns_sync.test_filename = test.filename
713 735
714 736 return super(IPDocTestRunner,self).run(test,
715 737 compileflags,out,clear_globs)
716 738
717 739
718 740 class DocFileCase(doctest.DocFileCase):
719 741 """Overrides to provide filename
720 742 """
721 743 def address(self):
722 744 return (self._dt_test.filename, None, None)
723 745
724 746
725 747 class ExtensionDoctest(doctests.Doctest):
726 748 """Nose Plugin that supports doctests in extension modules.
727 749 """
728 750 name = 'extdoctest' # call nosetests with --with-extdoctest
729 751 enabled = True
730 752
731 753 def __init__(self,exclude_patterns=None):
732 754 """Create a new ExtensionDoctest plugin.
733 755
734 756 Parameters
735 757 ----------
736 758
737 759 exclude_patterns : sequence of strings, optional
738 760 These patterns are compiled as regular expressions, subsequently used
739 761 to exclude any filename which matches them from inclusion in the test
740 762 suite (using pattern.search(), NOT pattern.match() ).
741 763 """
742 764
743 765 if exclude_patterns is None:
744 766 exclude_patterns = []
745 767 self.exclude_patterns = map(re.compile,exclude_patterns)
746 768 doctests.Doctest.__init__(self)
747 769
748 770 def options(self, parser, env=os.environ):
749 771 Plugin.options(self, parser, env)
750 772 parser.add_option('--doctest-tests', action='store_true',
751 773 dest='doctest_tests',
752 774 default=env.get('NOSE_DOCTEST_TESTS',True),
753 775 help="Also look for doctests in test modules. "
754 776 "Note that classes, methods and functions should "
755 777 "have either doctests or non-doctest tests, "
756 778 "not both. [NOSE_DOCTEST_TESTS]")
757 779 parser.add_option('--doctest-extension', action="append",
758 780 dest="doctestExtension",
759 781 help="Also look for doctests in files with "
760 782 "this extension [NOSE_DOCTEST_EXTENSION]")
761 783 # Set the default as a list, if given in env; otherwise
762 784 # an additional value set on the command line will cause
763 785 # an error.
764 786 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
765 787 if env_setting is not None:
766 788 parser.set_defaults(doctestExtension=tolist(env_setting))
767 789
768 790
769 791 def configure(self, options, config):
770 792 Plugin.configure(self, options, config)
771 793 self.doctest_tests = options.doctest_tests
772 794 self.extension = tolist(options.doctestExtension)
773 795
774 796 self.parser = doctest.DocTestParser()
775 797 self.finder = DocTestFinder()
776 798 self.checker = IPDoctestOutputChecker()
777 799 self.globs = None
778 800 self.extraglobs = None
779 801
780 802
781 803 def loadTestsFromExtensionModule(self,filename):
782 804 bpath,mod = os.path.split(filename)
783 805 modname = os.path.splitext(mod)[0]
784 806 try:
785 807 sys.path.append(bpath)
786 808 module = __import__(modname)
787 809 tests = list(self.loadTestsFromModule(module))
788 810 finally:
789 811 sys.path.pop()
790 812 return tests
791 813
792 814 # NOTE: the method below is almost a copy of the original one in nose, with
793 815 # a few modifications to control output checking.
794 816
795 817 def loadTestsFromModule(self, module):
796 818 #print '*** ipdoctest - lTM',module # dbg
797 819
798 820 if not self.matches(module.__name__):
799 821 log.debug("Doctest doesn't want module %s", module)
800 822 return
801 823
802 824 tests = self.finder.find(module,globs=self.globs,
803 825 extraglobs=self.extraglobs)
804 826 if not tests:
805 827 return
806 828
807 829 # always use whitespace and ellipsis options
808 830 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
809 831
810 832 tests.sort()
811 833 module_file = module.__file__
812 834 if module_file[-4:] in ('.pyc', '.pyo'):
813 835 module_file = module_file[:-1]
814 836 for test in tests:
815 837 if not test.examples:
816 838 continue
817 839 if not test.filename:
818 840 test.filename = module_file
819 841
820 842 yield DocTestCase(test,
821 843 optionflags=optionflags,
822 844 checker=self.checker)
823 845
824 846
825 847 def loadTestsFromFile(self, filename):
826 848 if is_extension_module(filename):
827 849 for t in self.loadTestsFromExtensionModule(filename):
828 850 yield t
829 851 else:
830 852 if self.extension and anyp(filename.endswith, self.extension):
831 853 name = os.path.basename(filename)
832 854 dh = open(filename)
833 855 try:
834 856 doc = dh.read()
835 857 finally:
836 858 dh.close()
837 859 test = self.parser.get_doctest(
838 860 doc, globs={'__file__': filename}, name=name,
839 861 filename=filename, lineno=0)
840 862 if test.examples:
841 863 #print 'FileCase:',test.examples # dbg
842 864 yield DocFileCase(test)
843 865 else:
844 866 yield False # no tests to load
845 867
846 868 def wantFile(self,filename):
847 869 """Return whether the given filename should be scanned for tests.
848 870
849 871 Modified version that accepts extension modules as valid containers for
850 872 doctests.
851 873 """
852 874 # print '*** ipdoctest- wantFile:',filename # dbg
853 875
854 876 for pat in self.exclude_patterns:
855 877 if pat.search(filename):
856 878 # print '###>>> SKIP:',filename # dbg
857 879 return False
858 880
859 881 if is_extension_module(filename):
860 882 return True
861 883 else:
862 884 return doctests.Doctest.wantFile(self,filename)
863 885
864 886
865 887 class IPythonDoctest(ExtensionDoctest):
866 888 """Nose Plugin that supports doctests in extension modules.
867 889 """
868 890 name = 'ipdoctest' # call nosetests with --with-ipdoctest
869 891 enabled = True
870 892
871 893 def makeTest(self, obj, parent):
872 894 """Look for doctests in the given object, which will be a
873 895 function, method or class.
874 896 """
875 897 # always use whitespace and ellipsis options
876 898 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
877 899
878 900 doctests = self.finder.find(obj, module=getmodule(parent))
879 901 if doctests:
880 902 for test in doctests:
881 903 if len(test.examples) == 0:
882 904 continue
883 905
884 906 yield DocTestCase(test, obj=obj,
885 907 optionflags=optionflags,
886 908 checker=self.checker)
887 909
888 910 def options(self, parser, env=os.environ):
889 911 Plugin.options(self, parser, env)
890 912 parser.add_option('--ipdoctest-tests', action='store_true',
891 913 dest='ipdoctest_tests',
892 914 default=env.get('NOSE_IPDOCTEST_TESTS',True),
893 915 help="Also look for doctests in test modules. "
894 916 "Note that classes, methods and functions should "
895 917 "have either doctests or non-doctest tests, "
896 918 "not both. [NOSE_IPDOCTEST_TESTS]")
897 919 parser.add_option('--ipdoctest-extension', action="append",
898 920 dest="ipdoctest_extension",
899 921 help="Also look for doctests in files with "
900 922 "this extension [NOSE_IPDOCTEST_EXTENSION]")
901 923 # Set the default as a list, if given in env; otherwise
902 924 # an additional value set on the command line will cause
903 925 # an error.
904 926 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
905 927 if env_setting is not None:
906 928 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
907 929
908 930 def configure(self, options, config):
909 931 Plugin.configure(self, options, config)
910 932 self.doctest_tests = options.ipdoctest_tests
911 933 self.extension = tolist(options.ipdoctest_extension)
912 934
913 935 self.parser = IPDocTestParser()
914 936 self.finder = DocTestFinder(parser=self.parser)
915 937 self.checker = IPDoctestOutputChecker()
916 938 self.globs = None
917 939 self.extraglobs = None
General Comments 0
You need to be logged in to leave comments. Login now