##// END OF EJS Templates
actually warn if somebody use it
Matthias Bussonnier -
Show More
@@ -1,135 +1,147 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 Color schemes for exception handling code in IPython.
4 4 """
5 5
6 import warnings
7
6 8 #*****************************************************************************
7 9 # Copyright (C) 2005-2006 Fernando Perez <fperez@colorado.edu>
8 10 #
9 11 # Distributed under the terms of the BSD License. The full license is in
10 12 # the file COPYING, distributed as part of this software.
11 13 #*****************************************************************************
12 14
13 15 from IPython.utils.coloransi import ColorSchemeTable, TermColors, ColorScheme
14 16
15 17 def exception_colors():
16 18 """Return a color table with fields for exception reporting.
17 19
18 20 The table is an instance of ColorSchemeTable with schemes added for
19 21 'Linux', 'LightBG' and 'NoColor' and fields for exception handling filled
20 22 in.
21 23
22 24 Examples:
23 25
24 26 >>> ec = exception_colors()
25 27 >>> ec.active_scheme_name
26 28 ''
27 29 >>> print(ec.active_colors)
28 30 None
29 31
30 32 Now we activate a color scheme:
31 33 >>> ec.set_active_scheme('NoColor')
32 34 >>> ec.active_scheme_name
33 35 'NoColor'
34 36 >>> sorted(ec.active_colors.keys())
35 37 ['Normal', 'caret', 'em', 'excName', 'filename', 'filenameEm', 'line',
36 38 'lineno', 'linenoEm', 'name', 'nameEm', 'normalEm', 'topline', 'vName',
37 39 'val', 'valEm']
38 40 """
39 41
40 42 ex_colors = ColorSchemeTable()
41 43
42 44 # Populate it with color schemes
43 45 C = TermColors # shorthand and local lookup
44 46 ex_colors.add_scheme(ColorScheme(
45 47 'NoColor',
46 48 # The color to be used for the top line
47 49 topline = C.NoColor,
48 50
49 51 # The colors to be used in the traceback
50 52 filename = C.NoColor,
51 53 lineno = C.NoColor,
52 54 name = C.NoColor,
53 55 vName = C.NoColor,
54 56 val = C.NoColor,
55 57 em = C.NoColor,
56 58
57 59 # Emphasized colors for the last frame of the traceback
58 60 normalEm = C.NoColor,
59 61 filenameEm = C.NoColor,
60 62 linenoEm = C.NoColor,
61 63 nameEm = C.NoColor,
62 64 valEm = C.NoColor,
63 65
64 66 # Colors for printing the exception
65 67 excName = C.NoColor,
66 68 line = C.NoColor,
67 69 caret = C.NoColor,
68 70 Normal = C.NoColor
69 71 ))
70 72
71 73 # make some schemes as instances so we can copy them for modification easily
72 74 ex_colors.add_scheme(ColorScheme(
73 75 'Linux',
74 76 # The color to be used for the top line
75 77 topline = C.LightRed,
76 78
77 79 # The colors to be used in the traceback
78 80 filename = C.Green,
79 81 lineno = C.Green,
80 82 name = C.Purple,
81 83 vName = C.Cyan,
82 84 val = C.Green,
83 85 em = C.LightCyan,
84 86
85 87 # Emphasized colors for the last frame of the traceback
86 88 normalEm = C.LightCyan,
87 89 filenameEm = C.LightGreen,
88 90 linenoEm = C.LightGreen,
89 91 nameEm = C.LightPurple,
90 92 valEm = C.LightBlue,
91 93
92 94 # Colors for printing the exception
93 95 excName = C.LightRed,
94 96 line = C.Yellow,
95 97 caret = C.White,
96 98 Normal = C.Normal
97 99 ))
98 100
99 101 # For light backgrounds, swap dark/light colors
100 102 ex_colors.add_scheme(ColorScheme(
101 103 'LightBG',
102 104 # The color to be used for the top line
103 105 topline = C.Red,
104 106
105 107 # The colors to be used in the traceback
106 108 filename = C.LightGreen,
107 109 lineno = C.LightGreen,
108 110 name = C.LightPurple,
109 111 vName = C.Cyan,
110 112 val = C.LightGreen,
111 113 em = C.Cyan,
112 114
113 115 # Emphasized colors for the last frame of the traceback
114 116 normalEm = C.Cyan,
115 117 filenameEm = C.Green,
116 118 linenoEm = C.Green,
117 119 nameEm = C.Purple,
118 120 valEm = C.Blue,
119 121
120 122 # Colors for printing the exception
121 123 excName = C.Red,
122 124 #line = C.Brown, # brown often is displayed as yellow
123 125 line = C.Red,
124 126 caret = C.Normal,
125 127 Normal = C.Normal,
126 128 ))
127 129
128 130 return ex_colors
129 131
132 class Deprec(object):
133
134 def __init__(self, wrapped_obj):
135 self.wrapped=wrapped_obj
136
137 def __getattr__(self, name):
138 val = getattr(self.wrapped, name)
139 warnings.warn("Using ExceptionColors global is deprecated", DeprecationWarning)
140 # using getattr after warnings break ipydoctest in weird way for 3.5
141 return val
130 142
131 143 # For backwards compatibility, keep around a single global object. Note that
132 144 # this should NOT be used, the factory function should be used instead, since
133 145 # these objects are stateful and it's very easy to get strange bugs if any code
134 146 # modifies the module-level object's state.
135 ExceptionColors = exception_colors()
147 ExceptionColors = Deprec(exception_colors())
@@ -1,768 +1,768 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 setting the
7 7 ``PlainTextFormatter.pprint`` option in your configuration file to False, or
8 8 by interactively disabling it with %Pprint. This is required so that IPython
9 9 output matches that of normal Python, which is used by doctest for internal
10 10 execution.
11 11
12 12 - Do not rely on specific prompt numbers for results (such as using
13 13 '_34==True', for example). For IPython tests run via an external process the
14 14 prompt numbers may be different, and IPython tests run as normal python code
15 15 won't even have these special _NN variables set at all.
16 16 """
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Module imports
20 20
21 21 # From the standard library
22 22 import doctest
23 23 import inspect
24 24 import logging
25 25 import os
26 26 import re
27 27 import sys
28 28 import traceback
29 29 import unittest
30 30
31 31 from inspect import getmodule
32 32
33 33 # We are overriding the default doctest runner, so we need to import a few
34 34 # things from doctest directly
35 35 from doctest import (REPORTING_FLAGS, REPORT_ONLY_FIRST_FAILURE,
36 36 _unittest_reportflags, DocTestRunner,
37 37 _extract_future_flags, pdb, _OutputRedirectingPdb,
38 38 _exception_traceback,
39 39 linecache)
40 40
41 41 # Third-party modules
42 42 import nose.core
43 43
44 44 from nose.plugins import doctests, Plugin
45 45 from nose.util import anyp, getpackage, test_address, resolve_name, tolist
46 46
47 47 # Our own imports
48 48 from IPython.utils.py3compat import builtin_mod, PY3, getcwd
49 49
50 50 if PY3:
51 51 from io import StringIO
52 52 else:
53 53 from StringIO import StringIO
54 54
55 55 #-----------------------------------------------------------------------------
56 56 # Module globals and other constants
57 57 #-----------------------------------------------------------------------------
58 58
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 #-----------------------------------------------------------------------------
63 63 # Classes and functions
64 64 #-----------------------------------------------------------------------------
65 65
66 66 def is_extension_module(filename):
67 67 """Return whether the given filename is an extension module.
68 68
69 69 This simply checks that the extension is either .so or .pyd.
70 70 """
71 71 return os.path.splitext(filename)[1].lower() in ('.so','.pyd')
72 72
73 73
74 74 class DocTestSkip(object):
75 75 """Object wrapper for doctests to be skipped."""
76 76
77 77 ds_skip = """Doctest to skip.
78 78 >>> 1 #doctest: +SKIP
79 79 """
80 80
81 81 def __init__(self,obj):
82 82 self.obj = obj
83 83
84 84 def __getattribute__(self,key):
85 85 if key == '__doc__':
86 86 return DocTestSkip.ds_skip
87 87 else:
88 88 return getattr(object.__getattribute__(self,'obj'),key)
89 89
90 90 # Modified version of the one in the stdlib, that fixes a python bug (doctests
91 91 # not found in extension modules, http://bugs.python.org/issue3158)
92 92 class DocTestFinder(doctest.DocTestFinder):
93 93
94 94 def _from_module(self, module, object):
95 95 """
96 96 Return true if the given object is defined in the given
97 97 module.
98 98 """
99 99 if module is None:
100 100 return True
101 101 elif inspect.isfunction(object):
102 102 return module.__dict__ is object.__globals__
103 103 elif inspect.isbuiltin(object):
104 104 return module.__name__ == object.__module__
105 105 elif inspect.isclass(object):
106 106 return module.__name__ == object.__module__
107 107 elif inspect.ismethod(object):
108 108 # This one may be a bug in cython that fails to correctly set the
109 109 # __module__ attribute of methods, but since the same error is easy
110 110 # to make by extension code writers, having this safety in place
111 111 # isn't such a bad idea
112 112 return module.__name__ == object.__self__.__class__.__module__
113 113 elif inspect.getmodule(object) is not None:
114 114 return module is inspect.getmodule(object)
115 115 elif hasattr(object, '__module__'):
116 116 return module.__name__ == object.__module__
117 117 elif isinstance(object, property):
118 118 return True # [XX] no way not be sure.
119 119 elif inspect.ismethoddescriptor(object):
120 120 # Unbound PyQt signals reach this point in Python 3.4b3, and we want
121 121 # to avoid throwing an error. See also http://bugs.python.org/issue3158
122 122 return False
123 123 else:
124 124 raise ValueError("object must be a class or function, got %r" % object)
125 125
126 126 def _find(self, tests, obj, name, module, source_lines, globs, seen):
127 127 """
128 128 Find tests for the given object and any contained objects, and
129 129 add them to `tests`.
130 130 """
131 #print '_find for:', obj, name, module # dbg
131 print('_find for:', obj, name, module) # dbg
132 132 if hasattr(obj,"skip_doctest"):
133 133 #print 'SKIPPING DOCTEST FOR:',obj # dbg
134 134 obj = DocTestSkip(obj)
135
135
136 136 doctest.DocTestFinder._find(self,tests, obj, name, module,
137 137 source_lines, globs, seen)
138 138
139 139 # Below we re-run pieces of the above method with manual modifications,
140 140 # because the original code is buggy and fails to correctly identify
141 141 # doctests in extension modules.
142 142
143 143 # Local shorthands
144 144 from inspect import isroutine, isclass, ismodule
145 145
146 146 # Look for tests in a module's contained objects.
147 147 if inspect.ismodule(obj) and self._recurse:
148 148 for valname, val in obj.__dict__.items():
149 149 valname1 = '%s.%s' % (name, valname)
150 150 if ( (isroutine(val) or isclass(val))
151 151 and self._from_module(module, val) ):
152 152
153 153 self._find(tests, val, valname1, module, source_lines,
154 154 globs, seen)
155 155
156 156 # Look for tests in a class's contained objects.
157 157 if inspect.isclass(obj) and self._recurse:
158 158 #print 'RECURSE into class:',obj # dbg
159 159 for valname, val in obj.__dict__.items():
160 160 # Special handling for staticmethod/classmethod.
161 161 if isinstance(val, staticmethod):
162 162 val = getattr(obj, valname)
163 163 if isinstance(val, classmethod):
164 164 val = getattr(obj, valname).__func__
165 165
166 166 # Recurse to methods, properties, and nested classes.
167 167 if ((inspect.isfunction(val) or inspect.isclass(val) or
168 168 inspect.ismethod(val) or
169 169 isinstance(val, property)) and
170 170 self._from_module(module, val)):
171 171 valname = '%s.%s' % (name, valname)
172 172 self._find(tests, val, valname, module, source_lines,
173 173 globs, seen)
174 174
175 175
176 176 class IPDoctestOutputChecker(doctest.OutputChecker):
177 177 """Second-chance checker with support for random tests.
178 178
179 179 If the default comparison doesn't pass, this checker looks in the expected
180 180 output string for flags that tell us to ignore the output.
181 181 """
182 182
183 183 random_re = re.compile(r'#\s*random\s+')
184 184
185 185 def check_output(self, want, got, optionflags):
186 186 """Check output, accepting special markers embedded in the output.
187 187
188 188 If the output didn't pass the default validation but the special string
189 189 '#random' is included, we accept it."""
190 190
191 191 # Let the original tester verify first, in case people have valid tests
192 192 # that happen to have a comment saying '#random' embedded in.
193 193 ret = doctest.OutputChecker.check_output(self, want, got,
194 194 optionflags)
195 195 if not ret and self.random_re.search(want):
196 196 #print >> sys.stderr, 'RANDOM OK:',want # dbg
197 197 return True
198 198
199 199 return ret
200 200
201 201
202 202 class DocTestCase(doctests.DocTestCase):
203 203 """Proxy for DocTestCase: provides an address() method that
204 204 returns the correct address for the doctest case. Otherwise
205 205 acts as a proxy to the test case. To provide hints for address(),
206 206 an obj may also be passed -- this will be used as the test object
207 207 for purposes of determining the test address, if it is provided.
208 208 """
209 209
210 210 # Note: this method was taken from numpy's nosetester module.
211 211
212 212 # Subclass nose.plugins.doctests.DocTestCase to work around a bug in
213 213 # its constructor that blocks non-default arguments from being passed
214 214 # down into doctest.DocTestCase
215 215
216 216 def __init__(self, test, optionflags=0, setUp=None, tearDown=None,
217 217 checker=None, obj=None, result_var='_'):
218 218 self._result_var = result_var
219 219 doctests.DocTestCase.__init__(self, test,
220 220 optionflags=optionflags,
221 221 setUp=setUp, tearDown=tearDown,
222 222 checker=checker)
223 223 # Now we must actually copy the original constructor from the stdlib
224 224 # doctest class, because we can't call it directly and a bug in nose
225 225 # means it never gets passed the right arguments.
226 226
227 227 self._dt_optionflags = optionflags
228 228 self._dt_checker = checker
229 229 self._dt_test = test
230 230 self._dt_test_globs_ori = test.globs
231 231 self._dt_setUp = setUp
232 232 self._dt_tearDown = tearDown
233 233
234 234 # XXX - store this runner once in the object!
235 235 runner = IPDocTestRunner(optionflags=optionflags,
236 236 checker=checker, verbose=False)
237 237 self._dt_runner = runner
238 238
239 239
240 240 # Each doctest should remember the directory it was loaded from, so
241 241 # things like %run work without too many contortions
242 242 self._ori_dir = os.path.dirname(test.filename)
243 243
244 244 # Modified runTest from the default stdlib
245 245 def runTest(self):
246 246 test = self._dt_test
247 247 runner = self._dt_runner
248 248
249 249 old = sys.stdout
250 250 new = StringIO()
251 251 optionflags = self._dt_optionflags
252 252
253 253 if not (optionflags & REPORTING_FLAGS):
254 254 # The option flags don't include any reporting flags,
255 255 # so add the default reporting flags
256 256 optionflags |= _unittest_reportflags
257 257
258 258 try:
259 259 # Save our current directory and switch out to the one where the
260 260 # test was originally created, in case another doctest did a
261 261 # directory change. We'll restore this in the finally clause.
262 262 curdir = getcwd()
263 263 #print 'runTest in dir:', self._ori_dir # dbg
264 264 os.chdir(self._ori_dir)
265 265
266 266 runner.DIVIDER = "-"*70
267 267 failures, tries = runner.run(test,out=new.write,
268 268 clear_globs=False)
269 269 finally:
270 270 sys.stdout = old
271 271 os.chdir(curdir)
272 272
273 273 if failures:
274 274 raise self.failureException(self.format_failure(new.getvalue()))
275 275
276 276 def setUp(self):
277 277 """Modified test setup that syncs with ipython namespace"""
278 278 #print "setUp test", self._dt_test.examples # dbg
279 279 if isinstance(self._dt_test.examples[0], IPExample):
280 280 # for IPython examples *only*, we swap the globals with the ipython
281 281 # namespace, after updating it with the globals (which doctest
282 282 # fills with the necessary info from the module being tested).
283 283 self.user_ns_orig = {}
284 284 self.user_ns_orig.update(_ip.user_ns)
285 285 _ip.user_ns.update(self._dt_test.globs)
286 286 # We must remove the _ key in the namespace, so that Python's
287 287 # doctest code sets it naturally
288 288 _ip.user_ns.pop('_', None)
289 289 _ip.user_ns['__builtins__'] = builtin_mod
290 290 self._dt_test.globs = _ip.user_ns
291 291
292 292 super(DocTestCase, self).setUp()
293 293
294 294 def tearDown(self):
295 295
296 296 # Undo the test.globs reassignment we made, so that the parent class
297 297 # teardown doesn't destroy the ipython namespace
298 298 if isinstance(self._dt_test.examples[0], IPExample):
299 299 self._dt_test.globs = self._dt_test_globs_ori
300 300 _ip.user_ns.clear()
301 301 _ip.user_ns.update(self.user_ns_orig)
302 302
303 303 # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but
304 304 # it does look like one to me: its tearDown method tries to run
305 305 #
306 306 # delattr(builtin_mod, self._result_var)
307 307 #
308 308 # without checking that the attribute really is there; it implicitly
309 309 # assumes it should have been set via displayhook. But if the
310 310 # displayhook was never called, this doesn't necessarily happen. I
311 311 # haven't been able to find a little self-contained example outside of
312 312 # ipython that would show the problem so I can report it to the nose
313 313 # team, but it does happen a lot in our code.
314 314 #
315 315 # So here, we just protect as narrowly as possible by trapping an
316 316 # attribute error whose message would be the name of self._result_var,
317 317 # and letting any other error propagate.
318 318 try:
319 319 super(DocTestCase, self).tearDown()
320 320 except AttributeError as exc:
321 321 if exc.args[0] != self._result_var:
322 322 raise
323 323
324 324
325 325 # A simple subclassing of the original with a different class name, so we can
326 326 # distinguish and treat differently IPython examples from pure python ones.
327 327 class IPExample(doctest.Example): pass
328 328
329 329
330 330 class IPExternalExample(doctest.Example):
331 331 """Doctest examples to be run in an external process."""
332 332
333 333 def __init__(self, source, want, exc_msg=None, lineno=0, indent=0,
334 334 options=None):
335 335 # Parent constructor
336 336 doctest.Example.__init__(self,source,want,exc_msg,lineno,indent,options)
337 337
338 338 # An EXTRA newline is needed to prevent pexpect hangs
339 339 self.source += '\n'
340 340
341 341
342 342 class IPDocTestParser(doctest.DocTestParser):
343 343 """
344 344 A class used to parse strings containing doctest examples.
345 345
346 346 Note: This is a version modified to properly recognize IPython input and
347 347 convert any IPython examples into valid Python ones.
348 348 """
349 349 # This regular expression is used to find doctest examples in a
350 350 # string. It defines three groups: `source` is the source code
351 351 # (including leading indentation and prompts); `indent` is the
352 352 # indentation of the first (PS1) line of the source code; and
353 353 # `want` is the expected output (including leading indentation).
354 354
355 355 # Classic Python prompts or default IPython ones
356 356 _PS1_PY = r'>>>'
357 357 _PS2_PY = r'\.\.\.'
358 358
359 359 _PS1_IP = r'In\ \[\d+\]:'
360 360 _PS2_IP = r'\ \ \ \.\.\.+:'
361 361
362 362 _RE_TPL = r'''
363 363 # Source consists of a PS1 line followed by zero or more PS2 lines.
364 364 (?P<source>
365 365 (?:^(?P<indent> [ ]*) (?P<ps1> %s) .*) # PS1 line
366 366 (?:\n [ ]* (?P<ps2> %s) .*)*) # PS2 lines
367 367 \n? # a newline
368 368 # Want consists of any non-blank lines that do not start with PS1.
369 369 (?P<want> (?:(?![ ]*$) # Not a blank line
370 370 (?![ ]*%s) # Not a line starting with PS1
371 371 (?![ ]*%s) # Not a line starting with PS2
372 372 .*$\n? # But any other line
373 373 )*)
374 374 '''
375 375
376 376 _EXAMPLE_RE_PY = re.compile( _RE_TPL % (_PS1_PY,_PS2_PY,_PS1_PY,_PS2_PY),
377 377 re.MULTILINE | re.VERBOSE)
378 378
379 379 _EXAMPLE_RE_IP = re.compile( _RE_TPL % (_PS1_IP,_PS2_IP,_PS1_IP,_PS2_IP),
380 380 re.MULTILINE | re.VERBOSE)
381 381
382 382 # Mark a test as being fully random. In this case, we simply append the
383 383 # random marker ('#random') to each individual example's output. This way
384 384 # we don't need to modify any other code.
385 385 _RANDOM_TEST = re.compile(r'#\s*all-random\s+')
386 386
387 387 # Mark tests to be executed in an external process - currently unsupported.
388 388 _EXTERNAL_IP = re.compile(r'#\s*ipdoctest:\s*EXTERNAL')
389 389
390 390 def ip2py(self,source):
391 391 """Convert input IPython source into valid Python."""
392 392 block = _ip.input_transformer_manager.transform_cell(source)
393 393 if len(block.splitlines()) == 1:
394 394 return _ip.prefilter(block)
395 395 else:
396 396 return block
397 397
398 398 def parse(self, string, name='<string>'):
399 399 """
400 400 Divide the given string into examples and intervening text,
401 401 and return them as a list of alternating Examples and strings.
402 402 Line numbers for the Examples are 0-based. The optional
403 403 argument `name` is a name identifying this string, and is only
404 404 used for error messages.
405 405 """
406 406
407 407 #print 'Parse string:\n',string # dbg
408 408
409 409 string = string.expandtabs()
410 410 # If all lines begin with the same indentation, then strip it.
411 411 min_indent = self._min_indent(string)
412 412 if min_indent > 0:
413 413 string = '\n'.join([l[min_indent:] for l in string.split('\n')])
414 414
415 415 output = []
416 416 charno, lineno = 0, 0
417 417
418 418 # We make 'all random' tests by adding the '# random' mark to every
419 419 # block of output in the test.
420 420 if self._RANDOM_TEST.search(string):
421 421 random_marker = '\n# random'
422 422 else:
423 423 random_marker = ''
424 424
425 425 # Whether to convert the input from ipython to python syntax
426 426 ip2py = False
427 427 # Find all doctest examples in the string. First, try them as Python
428 428 # examples, then as IPython ones
429 429 terms = list(self._EXAMPLE_RE_PY.finditer(string))
430 430 if terms:
431 431 # Normal Python example
432 432 #print '-'*70 # dbg
433 433 #print 'PyExample, Source:\n',string # dbg
434 434 #print '-'*70 # dbg
435 435 Example = doctest.Example
436 436 else:
437 437 # It's an ipython example. Note that IPExamples are run
438 438 # in-process, so their syntax must be turned into valid python.
439 439 # IPExternalExamples are run out-of-process (via pexpect) so they
440 440 # don't need any filtering (a real ipython will be executing them).
441 441 terms = list(self._EXAMPLE_RE_IP.finditer(string))
442 442 if self._EXTERNAL_IP.search(string):
443 443 #print '-'*70 # dbg
444 444 #print 'IPExternalExample, Source:\n',string # dbg
445 445 #print '-'*70 # dbg
446 446 Example = IPExternalExample
447 447 else:
448 448 #print '-'*70 # dbg
449 449 #print 'IPExample, Source:\n',string # dbg
450 450 #print '-'*70 # dbg
451 451 Example = IPExample
452 452 ip2py = True
453 453
454 454 for m in terms:
455 455 # Add the pre-example text to `output`.
456 456 output.append(string[charno:m.start()])
457 457 # Update lineno (lines before this example)
458 458 lineno += string.count('\n', charno, m.start())
459 459 # Extract info from the regexp match.
460 460 (source, options, want, exc_msg) = \
461 461 self._parse_example(m, name, lineno,ip2py)
462 462
463 463 # Append the random-output marker (it defaults to empty in most
464 464 # cases, it's only non-empty for 'all-random' tests):
465 465 want += random_marker
466 466
467 467 if Example is IPExternalExample:
468 468 options[doctest.NORMALIZE_WHITESPACE] = True
469 469 want += '\n'
470 470
471 471 # Create an Example, and add it to the list.
472 472 if not self._IS_BLANK_OR_COMMENT(source):
473 473 output.append(Example(source, want, exc_msg,
474 474 lineno=lineno,
475 475 indent=min_indent+len(m.group('indent')),
476 476 options=options))
477 477 # Update lineno (lines inside this example)
478 478 lineno += string.count('\n', m.start(), m.end())
479 479 # Update charno.
480 480 charno = m.end()
481 481 # Add any remaining post-example text to `output`.
482 482 output.append(string[charno:])
483 483 return output
484 484
485 485 def _parse_example(self, m, name, lineno,ip2py=False):
486 486 """
487 487 Given a regular expression match from `_EXAMPLE_RE` (`m`),
488 488 return a pair `(source, want)`, where `source` is the matched
489 489 example's source code (with prompts and indentation stripped);
490 490 and `want` is the example's expected output (with indentation
491 491 stripped).
492 492
493 493 `name` is the string's name, and `lineno` is the line number
494 494 where the example starts; both are used for error messages.
495 495
496 496 Optional:
497 497 `ip2py`: if true, filter the input via IPython to convert the syntax
498 498 into valid python.
499 499 """
500 500
501 501 # Get the example's indentation level.
502 502 indent = len(m.group('indent'))
503 503
504 504 # Divide source into lines; check that they're properly
505 505 # indented; and then strip their indentation & prompts.
506 506 source_lines = m.group('source').split('\n')
507 507
508 508 # We're using variable-length input prompts
509 509 ps1 = m.group('ps1')
510 510 ps2 = m.group('ps2')
511 511 ps1_len = len(ps1)
512 512
513 513 self._check_prompt_blank(source_lines, indent, name, lineno,ps1_len)
514 514 if ps2:
515 515 self._check_prefix(source_lines[1:], ' '*indent + ps2, name, lineno)
516 516
517 517 source = '\n'.join([sl[indent+ps1_len+1:] for sl in source_lines])
518 518
519 519 if ip2py:
520 520 # Convert source input from IPython into valid Python syntax
521 521 source = self.ip2py(source)
522 522
523 523 # Divide want into lines; check that it's properly indented; and
524 524 # then strip the indentation. Spaces before the last newline should
525 525 # be preserved, so plain rstrip() isn't good enough.
526 526 want = m.group('want')
527 527 want_lines = want.split('\n')
528 528 if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]):
529 529 del want_lines[-1] # forget final newline & spaces after it
530 530 self._check_prefix(want_lines, ' '*indent, name,
531 531 lineno + len(source_lines))
532 532
533 533 # Remove ipython output prompt that might be present in the first line
534 534 want_lines[0] = re.sub(r'Out\[\d+\]: \s*?\n?','',want_lines[0])
535 535
536 536 want = '\n'.join([wl[indent:] for wl in want_lines])
537 537
538 538 # If `want` contains a traceback message, then extract it.
539 539 m = self._EXCEPTION_RE.match(want)
540 540 if m:
541 541 exc_msg = m.group('msg')
542 542 else:
543 543 exc_msg = None
544 544
545 545 # Extract options from the source.
546 546 options = self._find_options(source, name, lineno)
547 547
548 548 return source, options, want, exc_msg
549 549
550 550 def _check_prompt_blank(self, lines, indent, name, lineno, ps1_len):
551 551 """
552 552 Given the lines of a source string (including prompts and
553 553 leading indentation), check to make sure that every prompt is
554 554 followed by a space character. If any line is not followed by
555 555 a space character, then raise ValueError.
556 556
557 557 Note: IPython-modified version which takes the input prompt length as a
558 558 parameter, so that prompts of variable length can be dealt with.
559 559 """
560 560 space_idx = indent+ps1_len
561 561 min_len = space_idx+1
562 562 for i, line in enumerate(lines):
563 563 if len(line) >= min_len and line[space_idx] != ' ':
564 564 raise ValueError('line %r of the docstring for %s '
565 565 'lacks blank after %s: %r' %
566 566 (lineno+i+1, name,
567 567 line[indent:space_idx], line))
568 568
569 569
570 570 SKIP = doctest.register_optionflag('SKIP')
571 571
572 572
573 573 class IPDocTestRunner(doctest.DocTestRunner,object):
574 574 """Test runner that synchronizes the IPython namespace with test globals.
575 575 """
576 576
577 577 def run(self, test, compileflags=None, out=None, clear_globs=True):
578 578
579 579 # Hack: ipython needs access to the execution context of the example,
580 580 # so that it can propagate user variables loaded by %run into
581 581 # test.globs. We put them here into our modified %run as a function
582 582 # attribute. Our new %run will then only make the namespace update
583 583 # when called (rather than unconconditionally updating test.globs here
584 584 # for all examples, most of which won't be calling %run anyway).
585 585 #_ip._ipdoctest_test_globs = test.globs
586 586 #_ip._ipdoctest_test_filename = test.filename
587 587
588 588 test.globs.update(_ip.user_ns)
589 589
590 590 return super(IPDocTestRunner,self).run(test,
591 591 compileflags,out,clear_globs)
592 592
593 593
594 594 class DocFileCase(doctest.DocFileCase):
595 595 """Overrides to provide filename
596 596 """
597 597 def address(self):
598 598 return (self._dt_test.filename, None, None)
599 599
600 600
601 601 class ExtensionDoctest(doctests.Doctest):
602 602 """Nose Plugin that supports doctests in extension modules.
603 603 """
604 604 name = 'extdoctest' # call nosetests with --with-extdoctest
605 605 enabled = True
606 606
607 607 def options(self, parser, env=os.environ):
608 608 Plugin.options(self, parser, env)
609 609 parser.add_option('--doctest-tests', action='store_true',
610 610 dest='doctest_tests',
611 611 default=env.get('NOSE_DOCTEST_TESTS',True),
612 612 help="Also look for doctests in test modules. "
613 613 "Note that classes, methods and functions should "
614 614 "have either doctests or non-doctest tests, "
615 615 "not both. [NOSE_DOCTEST_TESTS]")
616 616 parser.add_option('--doctest-extension', action="append",
617 617 dest="doctestExtension",
618 618 help="Also look for doctests in files with "
619 619 "this extension [NOSE_DOCTEST_EXTENSION]")
620 620 # Set the default as a list, if given in env; otherwise
621 621 # an additional value set on the command line will cause
622 622 # an error.
623 623 env_setting = env.get('NOSE_DOCTEST_EXTENSION')
624 624 if env_setting is not None:
625 625 parser.set_defaults(doctestExtension=tolist(env_setting))
626 626
627 627
628 628 def configure(self, options, config):
629 629 Plugin.configure(self, options, config)
630 630 # Pull standard doctest plugin out of config; we will do doctesting
631 631 config.plugins.plugins = [p for p in config.plugins.plugins
632 632 if p.name != 'doctest']
633 633 self.doctest_tests = options.doctest_tests
634 634 self.extension = tolist(options.doctestExtension)
635 635
636 636 self.parser = doctest.DocTestParser()
637 637 self.finder = DocTestFinder()
638 638 self.checker = IPDoctestOutputChecker()
639 639 self.globs = None
640 640 self.extraglobs = None
641 641
642 642
643 643 def loadTestsFromExtensionModule(self,filename):
644 644 bpath,mod = os.path.split(filename)
645 645 modname = os.path.splitext(mod)[0]
646 646 try:
647 647 sys.path.append(bpath)
648 648 module = __import__(modname)
649 649 tests = list(self.loadTestsFromModule(module))
650 650 finally:
651 651 sys.path.pop()
652 652 return tests
653 653
654 654 # NOTE: the method below is almost a copy of the original one in nose, with
655 655 # a few modifications to control output checking.
656 656
657 657 def loadTestsFromModule(self, module):
658 658 #print '*** ipdoctest - lTM',module # dbg
659 659
660 660 if not self.matches(module.__name__):
661 661 log.debug("Doctest doesn't want module %s", module)
662 662 return
663 663
664 664 tests = self.finder.find(module,globs=self.globs,
665 665 extraglobs=self.extraglobs)
666 666 if not tests:
667 667 return
668 668
669 669 # always use whitespace and ellipsis options
670 670 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
671 671
672 672 tests.sort()
673 673 module_file = module.__file__
674 674 if module_file[-4:] in ('.pyc', '.pyo'):
675 675 module_file = module_file[:-1]
676 676 for test in tests:
677 677 if not test.examples:
678 678 continue
679 679 if not test.filename:
680 680 test.filename = module_file
681 681
682 682 yield DocTestCase(test,
683 683 optionflags=optionflags,
684 684 checker=self.checker)
685 685
686 686
687 687 def loadTestsFromFile(self, filename):
688 688 #print "ipdoctest - from file", filename # dbg
689 689 if is_extension_module(filename):
690 690 for t in self.loadTestsFromExtensionModule(filename):
691 691 yield t
692 692 else:
693 693 if self.extension and anyp(filename.endswith, self.extension):
694 694 name = os.path.basename(filename)
695 695 dh = open(filename)
696 696 try:
697 697 doc = dh.read()
698 698 finally:
699 699 dh.close()
700 700 test = self.parser.get_doctest(
701 701 doc, globs={'__file__': filename}, name=name,
702 702 filename=filename, lineno=0)
703 703 if test.examples:
704 704 #print 'FileCase:',test.examples # dbg
705 705 yield DocFileCase(test)
706 706 else:
707 707 yield False # no tests to load
708 708
709 709
710 710 class IPythonDoctest(ExtensionDoctest):
711 711 """Nose Plugin that supports doctests in extension modules.
712 712 """
713 713 name = 'ipdoctest' # call nosetests with --with-ipdoctest
714 714 enabled = True
715 715
716 716 def makeTest(self, obj, parent):
717 717 """Look for doctests in the given object, which will be a
718 718 function, method or class.
719 719 """
720 720 #print 'Plugin analyzing:', obj, parent # dbg
721 721 # always use whitespace and ellipsis options
722 722 optionflags = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
723 723
724 724 doctests = self.finder.find(obj, module=getmodule(parent))
725 725 if doctests:
726 726 for test in doctests:
727 727 if len(test.examples) == 0:
728 728 continue
729 729
730 730 yield DocTestCase(test, obj=obj,
731 731 optionflags=optionflags,
732 732 checker=self.checker)
733 733
734 734 def options(self, parser, env=os.environ):
735 735 #print "Options for nose plugin:", self.name # dbg
736 736 Plugin.options(self, parser, env)
737 737 parser.add_option('--ipdoctest-tests', action='store_true',
738 738 dest='ipdoctest_tests',
739 739 default=env.get('NOSE_IPDOCTEST_TESTS',True),
740 740 help="Also look for doctests in test modules. "
741 741 "Note that classes, methods and functions should "
742 742 "have either doctests or non-doctest tests, "
743 743 "not both. [NOSE_IPDOCTEST_TESTS]")
744 744 parser.add_option('--ipdoctest-extension', action="append",
745 745 dest="ipdoctest_extension",
746 746 help="Also look for doctests in files with "
747 747 "this extension [NOSE_IPDOCTEST_EXTENSION]")
748 748 # Set the default as a list, if given in env; otherwise
749 749 # an additional value set on the command line will cause
750 750 # an error.
751 751 env_setting = env.get('NOSE_IPDOCTEST_EXTENSION')
752 752 if env_setting is not None:
753 753 parser.set_defaults(ipdoctest_extension=tolist(env_setting))
754 754
755 755 def configure(self, options, config):
756 756 #print "Configuring nose plugin:", self.name # dbg
757 757 Plugin.configure(self, options, config)
758 758 # Pull standard doctest plugin out of config; we will do doctesting
759 759 config.plugins.plugins = [p for p in config.plugins.plugins
760 760 if p.name != 'doctest']
761 761 self.doctest_tests = options.ipdoctest_tests
762 762 self.extension = tolist(options.ipdoctest_extension)
763 763
764 764 self.parser = IPDocTestParser()
765 765 self.finder = DocTestFinder(parser=self.parser)
766 766 self.checker = IPDoctestOutputChecker()
767 767 self.globs = None
768 768 self.extraglobs = None
General Comments 0
You need to be logged in to leave comments. Login now