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