##// END OF EJS Templates
Checkpoint where tests are recognized. Random tests not working yet.
Fernando Perez -
Show More
@@ -1,24 +1,28 b''
1 1 # Set this prefix to where you want to install the plugin
2 2 PREFIX=~/usr/local
3 3 PREFIX=~/tmp/local
4 4
5 5 plugin: IPython_doctest_plugin.egg-info
6 6
7 dtest: plugin dtexample.py
8 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
9 dtexample.py
10
7 11 test: plugin dtexample.py
8 nosetests -s --with-ipdoctest --doctest-tests --doctest-extension=txt \
12 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
9 13 dtexample.py test*.txt
10 14
11 15 deb: plugin dtexample.py
12 16 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
13 17 test_combo.txt
14 18
15 19 iptest: plugin
16 20 nosetests -vs --with-ipdoctest --doctest-tests --doctest-extension=txt \
17 21 IPython
18 22
19 23 IPython_doctest_plugin.egg-info: ipdoctest.py setup.py
20 24 python setup.py install --prefix=$(PREFIX)
21 25 touch $@
22 26
23 27 clean:
24 28 rm -rf IPython_doctest_plugin.egg-info *~ *pyc build/ dist/
@@ -1,72 +1,107 b''
1 1 """Simple example using doctests.
2 2
3 3 This file just contains doctests both using plain python and IPython prompts.
4 4 All tests should be loaded by nose.
5 5 """
6 6
7 7 def pyfunc():
8 8 """Some pure python tests...
9 9
10 10 >>> pyfunc()
11 11 'pyfunc'
12 12
13 13 >>> import os
14 14
15 15 >>> 2+3
16 16 5
17 17
18 18 >>> for i in range(3):
19 19 ... print i,
20 20 ... print i+1,
21 21 ...
22 22 0 1 1 2 2 3
23 23 """
24 24
25 25 return 'pyfunc'
26 26
27 27 def ipfunc():
28 28 """Some ipython tests...
29 29
30 30 In [1]: import os
31 31
32 32 In [2]: cd /
33 33 /
34 34
35 35 In [3]: 2+3
36 36 Out[3]: 5
37 37
38 38 In [26]: for i in range(3):
39 39 ....: print i,
40 40 ....: print i+1,
41 41 ....:
42 42 0 1 1 2 2 3
43 43
44 44
45 45 Examples that access the operating system work:
46 46
47 47 In [1]: !echo hello
48 48 hello
49 49
50 50 In [2]: !echo hello > /tmp/foo
51 51
52 52 In [3]: !cat /tmp/foo
53 53 hello
54 54
55 55 In [4]: rm -f /tmp/foo
56 56
57 57 It's OK to use '_' for the last result, but do NOT try to use IPython's
58 58 numbered history of _NN outputs, since those won't exist under the
59 59 doctest environment:
60 60
61 61 In [7]: 3+4
62 62 Out[7]: 7
63 63
64 64 In [8]: _+3
65 65 Out[8]: 10
66 66
67 67 In [9]: ipfunc()
68 68 Out[9]: 'ipfunc'
69 69 """
70 70
71 71 return 'ipfunc'
72 72
73
74 def ranfunc():
75 """A function with some random output.
76
77 >>> 1+3 #random
78 junk goes here...
79
80 >>> 1+3
81 4
82
83 >>> 1+2 #random
84 again, anything goes
85 """
86 return 'ranfunc'
87
88
89 def ranf2():
90 """A function whose examples are all all random
91
92 Examples:
93
94 #all-random
95
96 >>> 1+3 #random
97 junk goes here...
98
99 >>> 1+3
100 klasdfj;
101
102 >>> 1+2 #random
103 again, anything goes
104
105 """
106 return 'ranf2'
107
@@ -1,598 +1,619 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 - IPython functions that produce output as a side-effect of calling a system
18 18 process (e.g. 'ls') can be doc-tested, but they must be handled in an
19 19 external IPython process. Such doctests must be tagged with:
20 20
21 21 # ipdoctest: EXTERNAL
22 22
23 23 so that the testing machinery handles them differently. Since these are run
24 24 via pexpect in an external process, they can't deal with exceptions or other
25 25 fancy featurs of regular doctests. You must limit such tests to simple
26 26 matching of the output. For this reason, I recommend you limit these kinds
27 27 of doctests to features that truly require a separate process, and use the
28 28 normal IPython ones (which have all the features of normal doctests) for
29 29 everything else. See the examples at the bottom of this file for a
30 30 comparison of what can be done with both types.
31 31 """
32 32
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Module imports
36 36
37 37 # From the standard library
38 38 import __builtin__
39 39 import commands
40 40 import doctest
41 41 import inspect
42 42 import logging
43 43 import os
44 44 import re
45 45 import sys
46 46 import unittest
47 47
48 48 from inspect import getmodule
49 49
50 50 # Third-party modules
51 51 import nose.core
52 52
53 53 from nose.plugins import doctests, Plugin
54 54 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
55 55
56 56 # Our own imports
57 57 #from extdoctest import ExtensionDoctest, DocTestFinder
58 58 #from dttools import DocTestFinder, DocTestCase
59 59 #-----------------------------------------------------------------------------
60 60 # Module globals and other constants
61 61
62 62 log = logging.getLogger(__name__)
63 63
64 64 ###########################################################################
65 65 # *** HACK ***
66 66 # We must start our own ipython object and heavily muck with it so that all the
67 67 # modifications IPython makes to system behavior don't send the doctest
68 68 # machinery into a fit. This code should be considered a gross hack, but it
69 69 # gets the job done.
70 70
71 71 def start_ipython():
72 72 """Start a global IPython shell, which we need for IPython-specific syntax.
73 73 """
74 74 import IPython
75 75
76 76 def xsys(cmd):
77 77 """Execute a command and print its output.
78 78
79 79 This is just a convenience function to replace the IPython system call
80 80 with one that is more doctest-friendly.
81 81 """
82 82 cmd = _ip.IP.var_expand(cmd,depth=1)
83 83 sys.stdout.write(commands.getoutput(cmd))
84 84 sys.stdout.flush()
85 85
86 86 # Store certain global objects that IPython modifies
87 87 _displayhook = sys.displayhook
88 88 _excepthook = sys.excepthook
89 89 _main = sys.modules.get('__main__')
90 90
91 91 # Start IPython instance
92 92 IPython.Shell.IPShell(['--classic','--noterm_title'])
93 93
94 94 # Deactivate the various python system hooks added by ipython for
95 95 # interactive convenience so we don't confuse the doctest system
96 96 sys.modules['__main__'] = _main
97 97 sys.displayhook = _displayhook
98 98 sys.excepthook = _excepthook
99 99
100 100 # So that ipython magics and aliases can be doctested (they work by making
101 101 # a call into a global _ip object)
102 102 _ip = IPython.ipapi.get()
103 103 __builtin__._ip = _ip
104 104
105 105 # Modify the IPython system call with one that uses getoutput, so that we
106 106 # can capture subcommands and print them to Python's stdout, otherwise the
107 107 # doctest machinery would miss them.
108 108 _ip.system = xsys
109 109
110 110 # The start call MUST be made here. I'm not sure yet why it doesn't work if
111 111 # it is made later, at plugin initialization time, but in all my tests, that's
112 112 # the case.
113 113 start_ipython()
114 114
115 115 # *** END HACK ***
116 116 ###########################################################################
117 117
118 118 # Classes and functions
119 119
120 120 def is_extension_module(filename):
121 121 """Return whether the given filename is an extension module.
122 122
123 123 This simply checks that the extension is either .so or .pyd.
124 124 """
125 125 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
126 126
127 127
128 128 # Modified version of the one in the stdlib, that fixes a python bug (doctests
129 129 # not found in extension modules, http://bugs.python.org/issue3158)
130 130 class DocTestFinder(doctest.DocTestFinder):
131 131
132 132 def _from_module(self, module, object):
133 133 """
134 134 Return true if the given object is defined in the given
135 135 module.
136 136 """
137 137 if module is None:
138 138 #print '_fm C1' # dbg
139 139 return True
140 140 elif inspect.isfunction(object):
141 141 #print '_fm C2' # dbg
142 142 return module.__dict__ is object.func_globals
143 143 elif inspect.isbuiltin(object):
144 144 #print '_fm C2-1' # dbg
145 145 return module.__name__ == object.__module__
146 146 elif inspect.isclass(object):
147 147 #print '_fm C3' # dbg
148 148 return module.__name__ == object.__module__
149 149 elif inspect.ismethod(object):
150 150 # This one may be a bug in cython that fails to correctly set the
151 151 # __module__ attribute of methods, but since the same error is easy
152 152 # to make by extension code writers, having this safety in place
153 153 # isn't such a bad idea
154 154 #print '_fm C3-1' # dbg
155 155 return module.__name__ == object.im_class.__module__
156 156 elif inspect.getmodule(object) is not None:
157 157 #print '_fm C4' # dbg
158 158 #print 'C4 mod',module,'obj',object # dbg
159 159 return module is inspect.getmodule(object)
160 160 elif hasattr(object, '__module__'):
161 161 #print '_fm C5' # dbg
162 162 return module.__name__ == object.__module__
163 163 elif isinstance(object, property):
164 164 #print '_fm C6' # dbg
165 165 return True # [XX] no way not be sure.
166 166 else:
167 167 raise ValueError("object must be a class or function")
168 168
169 169 def _find(self, tests, obj, name, module, source_lines, globs, seen):
170 170 """
171 171 Find tests for the given object and any contained objects, and
172 172 add them to `tests`.
173 173 """
174 174
175 175 doctest.DocTestFinder._find(self,tests, obj, name, module,
176 176 source_lines, globs, seen)
177 177
178 178 # Below we re-run pieces of the above method with manual modifications,
179 179 # because the original code is buggy and fails to correctly identify
180 180 # doctests in extension modules.
181 181
182 182 # Local shorthands
183 183 from inspect import isroutine, isclass, ismodule
184 184
185 185 # Look for tests in a module's contained objects.
186 186 if inspect.ismodule(obj) and self._recurse:
187 187 for valname, val in obj.__dict__.items():
188 188 valname1 = '%s.%s' % (name, valname)
189 189 if ( (isroutine(val) or isclass(val))
190 190 and self._from_module(module, val) ):
191 191
192 192 self._find(tests, val, valname1, module, source_lines,
193 193 globs, seen)
194 194
195 195 # Look for tests in a class's contained objects.
196 196 if inspect.isclass(obj) and self._recurse:
197 197 #print 'RECURSE into class:',obj # dbg
198 198 for valname, val in obj.__dict__.items():
199 199 #valname1 = '%s.%s' % (name, valname) # dbg
200 200 #print 'N',name,'VN:',valname,'val:',str(val)[:77] # dbg
201 201 # Special handling for staticmethod/classmethod.
202 202 if isinstance(val, staticmethod):
203 203 val = getattr(obj, valname)
204 204 if isinstance(val, classmethod):
205 205 val = getattr(obj, valname).im_func
206 206
207 207 # Recurse to methods, properties, and nested classes.
208 208 if ((inspect.isfunction(val) or inspect.isclass(val) or
209 209 inspect.ismethod(val) or
210 210 isinstance(val, property)) and
211 211 self._from_module(module, val)):
212 212 valname = '%s.%s' % (name, valname)
213 213 self._find(tests, val, valname, module, source_lines,
214 214 globs, seen)
215 215
216 216
217 217 class DocTestCase(doctests.DocTestCase):
218 218 """Proxy for DocTestCase: provides an address() method that
219 219 returns the correct address for the doctest case. Otherwise
220 220 acts as a proxy to the test case. To provide hints for address(),
221 221 an obj may also be passed -- this will be used as the test object
222 222 for purposes of determining the test address, if it is provided.
223 223 """
224 224
225 # Note: this method was taken from numpy's nosetester module.
226
227 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
228 # its constructor that blocks non-default arguments from being passed
229 # down into doctest.DocTestCase
230 ## def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
231 ## checker=None, obj=None, result_var='_'):
232 ## self._result_var = result_var
233 ## self._nose_obj = obj
234 ## doctest.DocTestCase.__init__(self, test,
235 ## optionflags=optionflags,
236 ## setUp=setUp, tearDown=tearDown,
237 ## checker=checker)
238
225 239 # doctests loaded via find(obj) omit the module name
226 240 # so we need to override id, __repr__ and shortDescription
227 241 # bonus: this will squash a 2.3 vs 2.4 incompatiblity
228 242 def id(self):
229 243 name = self._dt_test.name
230 244 filename = self._dt_test.filename
231 245 if filename is not None:
232 246 pk = getpackage(filename)
233 247 if pk is not None and not name.startswith(pk):
234 248 name = "%s.%s" % (pk, name)
235 249 return name
236 250
237 251
252
238 253 # A simple subclassing of the original with a different class name, so we can
239 254 # distinguish and treat differently IPython examples from pure python ones.
240 255 class IPExample(doctest.Example): pass
241 256
242 257
243 258 class IPExternalExample(doctest.Example):
244 259 """Doctest examples to be run in an external process."""
245 260
246 261 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
247 262 options=None):
248 263 # Parent constructor
249 264 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
250 265
251 266 # An EXTRA newline is needed to prevent pexpect hangs
252 267 self.source += '\n'
253 268
254 269
255 270 class IPDocTestParser(doctest.DocTestParser):
256 271 """
257 272 A class used to parse strings containing doctest examples.
258 273
259 274 Note: This is a version modified to properly recognize IPython input and
260 275 convert any IPython examples into valid Python ones.
261 276 """
262 277 # This regular expression is used to find doctest examples in a
263 278 # string. It defines three groups: `source` is the source code
264 279 # (including leading indentation and prompts); `indent` is the
265 280 # indentation of the first (PS1) line of the source code; and
266 281 # `want` is the expected output (including leading indentation).
267 282
268 283 # Classic Python prompts or default IPython ones
269 284 _PS1_PY = r'>>>'
270 285 _PS2_PY = r'\.\.\.'
271 286
272 287 _PS1_IP = r'In\ \[\d+\]:'
273 288 _PS2_IP = r'\ \ \ \.\.\.+:'
274 289
275 290 _RE_TPL = r'''
276 291 # Source consists of a PS1 line followed by zero or more PS2 lines.
277 292 (?P<source>
278 293 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
279 294 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
280 295 \n? # a newline
281 296 # Want consists of any non-blank lines that do not start with PS1.
282 297 (?P<want> (?:(?![ ]*$) # Not a blank line
283 298 (?![ ]*%s) # Not a line starting with PS1
284 299 (?![ ]*%s) # Not a line starting with PS2
285 300 .*$\n? # But any other line
286 301 )*)
287 302 '''
288 303
289 304 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
290 305 re.MULTILINE | re.VERBOSE)
291 306
292 307 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
293 308 re.MULTILINE | re.VERBOSE)
294 309
295 310 def ip2py(self,source):
296 311 """Convert input IPython source into valid Python."""
297 312 out = []
298 313 newline = out.append
299 314 for lnum,line in enumerate(source.splitlines()):
300 315 #newline(_ip.IPipython.prefilter(line,True))
301 316 newline(_ip.IP.prefilter(line,lnum>0))
302 317 newline('') # ensure a closing newline, needed by doctest
303 318 return '\n'.join(out)
304 319
305 320 def parse(self, string, name='<string>'):
306 321 """
307 322 Divide the given string into examples and intervening text,
308 323 and return them as a list of alternating Examples and strings.
309 324 Line numbers for the Examples are 0-based. The optional
310 325 argument `name` is a name identifying this string, and is only
311 326 used for error messages.
312 327 """
313 328
314 #print 'Parse string:\n',string # dbg
329 print 'Parse string:\n',string # dbg
315 330
316 331 string = string.expandtabs()
317 332 # If all lines begin with the same indentation, then strip it.
318 333 min_indent = self._min_indent(string)
319 334 if min_indent > 0:
320 335 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
321 336
322 337 output = []
323 338 charno, lineno = 0, 0
324 339
325 340 # Whether to convert the input from ipython to python syntax
326 341 ip2py = False
327 342 # Find all doctest examples in the string. First, try them as Python
328 343 # examples, then as IPython ones
329 344 terms = list(self._EXAMPLE_RE_PY.finditer(string))
330 345 if terms:
331 346 # Normal Python example
332 347 #print '-'*70 # dbg
333 348 #print 'PyExample, Source:\n',string # dbg
334 349 #print '-'*70 # dbg
335 350 Example = doctest.Example
336 351 else:
337 352 # It's an ipython example. Note that IPExamples are run
338 353 # in-process, so their syntax must be turned into valid python.
339 354 # IPExternalExamples are run out-of-process (via pexpect) so they
340 355 # don't need any filtering (a real ipython will be executing them).
341 356 terms = list(self._EXAMPLE_RE_IP.finditer(string))
342 357 if re.search(r'#\s*ipdoctest:\s*EXTERNAL',string):
343 358 #print '-'*70 # dbg
344 359 #print 'IPExternalExample, Source:\n',string # dbg
345 360 #print '-'*70 # dbg
346 361 Example = IPExternalExample
347 362 else:
348 363 #print '-'*70 # dbg
349 364 #print 'IPExample, Source:\n',string # dbg
350 365 #print '-'*70 # dbg
351 366 Example = IPExample
352 367 ip2py = True
353 368
354 369 for m in terms:
355 370 # Add the pre-example text to `output`.
356 371 output.append(string[charno:m.start()])
357 372 # Update lineno (lines before this example)
358 373 lineno += string.count('\n', charno, m.start())
359 374 # Extract info from the regexp match.
360 375 (source, options, want, exc_msg) = \
361 376 self._parse_example(m, name, lineno,ip2py)
362 377 if Example is IPExternalExample:
363 378 options[doctest.NORMALIZE_WHITESPACE] = True
364 379 want += '\n'
365 380 # Create an Example, and add it to the list.
366 381 if not self._IS_BLANK_OR_COMMENT(source):
367 382 #print 'Example source:', source # dbg
368 383 output.append(Example(source, want, exc_msg,
369 384 lineno=lineno,
370 385 indent=min_indent+len(m.group('indent')),
371 386 options=options))
372 387 # Update lineno (lines inside this example)
373 388 lineno += string.count('\n', m.start(), m.end())
374 389 # Update charno.
375 390 charno = m.end()
376 391 # Add any remaining post-example text to `output`.
377 392 output.append(string[charno:])
378 393
394 #print 'OUT:',output # dbg
395
379 396 return output
380 397
381 398 def _parse_example(self, m, name, lineno,ip2py=False):
382 399 """
383 400 Given a regular expression match from `_EXAMPLE_RE` (`m`),
384 401 return a pair `(source, want)`, where `source` is the matched
385 402 example's source code (with prompts and indentation stripped);
386 403 and `want` is the example's expected output (with indentation
387 404 stripped).
388 405
389 406 `name` is the string's name, and `lineno` is the line number
390 407 where the example starts; both are used for error messages.
391 408
392 409 Optional:
393 410 `ip2py`: if true, filter the input via IPython to convert the syntax
394 411 into valid python.
395 412 """
396 413
397 414 # Get the example's indentation level.
398 415 indent = len(m.group('indent'))
399 416
400 417 # Divide source into lines; check that they're properly
401 418 # indented; and then strip their indentation & prompts.
402 419 source_lines = m.group('source').split('\n')
403 420
404 421 # We're using variable-length input prompts
405 422 ps1 = m.group('ps1')
406 423 ps2 = m.group('ps2')
407 424 ps1_len = len(ps1)
408 425
409 426 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
410 427 if ps2:
411 428 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
412 429
413 430 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
414 431
415 432 if ip2py:
416 433 # Convert source input from IPython into valid Python syntax
417 434 source = self.ip2py(source)
418 435
419 436 # Divide want into lines; check that it's properly indented; and
420 437 # then strip the indentation. Spaces before the last newline should
421 438 # be preserved, so plain rstrip() isn't good enough.
422 439 want = m.group('want')
423 440 want_lines = want.split('\n')
424 441 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
425 442 del want_lines[-1] # forget final newline & spaces after it
426 443 self._check_prefix(want_lines, ' '*indent, name,
427 444 lineno + len(source_lines))
428 445
429 446 # Remove ipython output prompt that might be present in the first line
430 447 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
431 448
432 449 want = '\n'.join([wl[indent:] for wl in want_lines])
433 450
434 451 # If `want` contains a traceback message, then extract it.
435 452 m = self._EXCEPTION_RE.match(want)
436 453 if m:
437 454 exc_msg = m.group('msg')
438 455 else:
439 456 exc_msg = None
440 457
441 458 # Extract options from the source.
442 459 options = self._find_options(source, name, lineno)
443 460
444 461 return source, options, want, exc_msg
445 462
446 463 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
447 464 """
448 465 Given the lines of a source string (including prompts and
449 466 leading indentation), check to make sure that every prompt is
450 467 followed by a space character. If any line is not followed by
451 468 a space character, then raise ValueError.
452 469
453 470 Note: IPython-modified version which takes the input prompt length as a
454 471 parameter, so that prompts of variable length can be dealt with.
455 472 """
456 473 space_idx = indent+ps1_len
457 474 min_len = space_idx+1
458 475 for i, line in enumerate(lines):
459 476 if len(line) >= min_len and line[space_idx] != ' ':
460 477 raise ValueError('line %r of the docstring for %s '
461 478 'lacks blank after %s: %r' %
462 479 (lineno+i+1, name,
463 480 line[indent:space_idx], line))
464 481
465 482 SKIP = doctest.register_optionflag('SKIP')
466 483
467 484
468 485 class DocFileCase(doctest.DocFileCase):
469 486 """Overrides to provide filename
470 487 """
471 488 def address(self):
472 489 return (self._dt_test.filename, None, None)
473 490
474 491
475 492 class ExtensionDoctest(doctests.Doctest):
476 493 """Nose Plugin that supports doctests in extension modules.
477 494 """
478 495 name = 'extdoctest' # call nosetests with --with-extdoctest
479 496 enabled = True
480 497
481 498 def options(self, parser, env=os.environ):
482 499 Plugin.options(self, parser, env)
483 500
484 501 def configure(self, options, config):
485 502 Plugin.configure(self, options, config)
486 503 self.doctest_tests = options.doctest_tests
487 504 self.extension = tolist(options.doctestExtension)
488 505 self.finder = DocTestFinder()
489 506 self.parser = doctest.DocTestParser()
490 507
491 508
492 509 def loadTestsFromExtensionModule(self,filename):
493 510 bpath,mod = os.path.split(filename)
494 511 modname = os.path.splitext(mod)[0]
495 512 try:
496 513 sys.path.append(bpath)
497 514 module = __import__(modname)
498 515 tests = list(self.loadTestsFromModule(module))
499 516 finally:
500 517 sys.path.pop()
501 518 return tests
502 519
503 520 def loadTestsFromFile(self, filename):
521 print 'lTF',filename # dbg
522
504 523 if is_extension_module(filename):
505 524 for t in self.loadTestsFromExtensionModule(filename):
506 525 yield t
507 526 else:
508 527 ## for t in list(doctests.Doctest.loadTestsFromFile(self,filename)):
509 528 ## yield t
510 529 pass
511 530
512 531 if self.extension and anyp(filename.endswith, self.extension):
513 #print 'lTF',filename # dbg
514 532 name = os.path.basename(filename)
515 533 dh = open(filename)
516 534 try:
517 535 doc = dh.read()
518 536 finally:
519 537 dh.close()
520 538 test = self.parser.get_doctest(
521 539 doc, globs={'__file__': filename}, name=name,
522 540 filename=filename, lineno=0)
523 541 if test.examples:
524 542 #print 'FileCase:',test.examples # dbg
525 543 yield DocFileCase(test)
526 544 else:
527 545 yield False # no tests to load
528 546
529 547 def wantFile(self,filename):
530 548 """Return whether the given filename should be scanned for tests.
531 549
532 550 Modified version that accepts extension modules as valid containers for
533 551 doctests.
534 552 """
535 #print 'Filename:',filename # dbg
553 print 'Filename:',filename # dbg
536 554
537 555 # temporarily hardcoded list, will move to driver later
538 556 exclude = ['IPython/external/',
539 557 'IPython/Extensions/ipy_',
540 558 'IPython/platutils_win32',
541 559 'IPython/frontend/cocoa',
542 560 'IPython_doctest_plugin',
543 561 'IPython/Gnuplot',
544 562 'IPython/Extensions/PhysicalQIn']
545 563
546 564 for fex in exclude:
547 565 if fex in filename: # substring
548 566 #print '###>>> SKIP:',filename # dbg
549 567 return False
550 568
551 569 if is_extension_module(filename):
552 570 return True
553 571 else:
554 572 return doctests.Doctest.wantFile(self,filename)
555 573
556 # NOTE: the method below is a *copy* of the one in the nose doctests
557 # plugin, but we have to replicate it here in order to have it resolve the
558 # DocTestCase (last line) to our local copy, since the nose plugin doesn't
559 # provide a public hook for what TestCase class to use. The alternative
560 # would be to monkeypatch doctest in the stdlib, but that's ugly and
561 # brittle, since a change in plugin load order can break it. So for now,
562 # we just paste this in here, inelegant as this may be.
563
574 # NOTE: the method below is almost a copy of the original one in nose, with
575 # a few modifications to control output checking.
576
564 577 def loadTestsFromModule(self, module):
565 578 #print 'lTM',module # dbg
566 579
567 580 if not self.matches(module.__name__):
568 581 log.debug("Doctest doesn't want module %s", module)
569 582 return
570 583 tests = self.finder.find(module)
571 584 if not tests:
572 585 return
573 586 tests.sort()
574 587 module_file = module.__file__
575 588 if module_file[-4:] in ('.pyc', '.pyo'):
576 589 module_file = module_file[:-1]
577 590 for test in tests:
578 591 if not test.examples:
579 592 continue
580 593 if not test.filename:
581 594 test.filename = module_file
582 595 yield DocTestCase(test)
583 596
597 # always use whitespace and ellipsis options
598 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
599 #checker = DoctestOutputChecker()
600 checker = None
601 yield DocTestCase(test,
602 optionflags=optionflags,
603 checker=checker)
604
605
584 606
585 607 class IPythonDoctest(ExtensionDoctest):
586 608 """Nose Plugin that supports doctests in extension modules.
587 609 """
588 610 name = 'ipdoctest' # call nosetests with --with-ipdoctest
589 611 enabled = True
590 612
591 613 def configure(self, options, config):
592 614
593 615 Plugin.configure(self, options, config)
594 616 self.doctest_tests = options.doctest_tests
595 617 self.extension = tolist(options.doctestExtension)
596 618 self.parser = IPDocTestParser()
597 #self.finder = DocTestFinder(parser=IPDocTestParser())
598 619 self.finder = DocTestFinder(parser=self.parser)
General Comments 0
You need to be logged in to leave comments. Login now