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