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