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