##// END OF EJS Templates
Fix doctests in IPython.testing
Thomas Kluyver -
Show More
@@ -1,164 +1,161 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 from IPython.utils.py3compat import doctest_refactor_print
7 7
8 8 def pyfunc():
9 9 """Some pure python tests...
10 10
11 11 >>> pyfunc()
12 12 'pyfunc'
13 13
14 14 >>> import os
15 15
16 16 >>> 2+3
17 17 5
18 18
19 19 >>> for i in range(3):
20 ... print i,
21 ... print i+1,
20 ... print(i, end=' ')
21 ... print(i+1, end=' ')
22 22 ...
23 0 1 1 2 2 3
23 0 1 1 2 2 3
24 24 """
25 25 return 'pyfunc'
26 26
27 27 @doctest_refactor_print
28 28 def ipfunc():
29 29 """Some ipython tests...
30 30
31 31 In [1]: import os
32 32
33 33 In [3]: 2+3
34 34 Out[3]: 5
35 35
36 36 In [26]: for i in range(3):
37 ....: print i,
38 ....: print i+1,
37 ....: print(i, end=' ')
38 ....: print(i+1, end=' ')
39 39 ....:
40 0 1 1 2 2 3
40 0 1 1 2 2 3
41 41
42 42
43 43 Examples that access the operating system work:
44 44
45 45 In [1]: !echo hello
46 46 hello
47 47
48 48 In [2]: !echo hello > /tmp/foo_iptest
49 49
50 50 In [3]: !cat /tmp/foo_iptest
51 51 hello
52 52
53 53 In [4]: rm -f /tmp/foo_iptest
54 54
55 55 It's OK to use '_' for the last result, but do NOT try to use IPython's
56 56 numbered history of _NN outputs, since those won't exist under the
57 57 doctest environment:
58 58
59 59 In [7]: 'hi'
60 60 Out[7]: 'hi'
61 61
62 In [8]: print repr(_)
62 In [8]: print(repr(_))
63 63 'hi'
64 64
65 65 In [7]: 3+4
66 66 Out[7]: 7
67 67
68 68 In [8]: _+3
69 69 Out[8]: 10
70 70
71 71 In [9]: ipfunc()
72 72 Out[9]: 'ipfunc'
73 73 """
74 74 return 'ipfunc'
75 75
76 76
77 77 def ranfunc():
78 78 """A function with some random output.
79 79
80 80 Normal examples are verified as usual:
81 81 >>> 1+3
82 82 4
83 83
84 84 But if you put '# random' in the output, it is ignored:
85 85 >>> 1+3
86 86 junk goes here... # random
87 87
88 88 >>> 1+2
89 89 again, anything goes #random
90 90 if multiline, the random mark is only needed once.
91 91
92 92 >>> 1+2
93 93 You can also put the random marker at the end:
94 94 # random
95 95
96 96 >>> 1+2
97 97 # random
98 98 .. or at the beginning.
99 99
100 100 More correct input is properly verified:
101 101 >>> ranfunc()
102 102 'ranfunc'
103 103 """
104 104 return 'ranfunc'
105 105
106 106
107 107 def random_all():
108 108 """A function where we ignore the output of ALL examples.
109 109
110 110 Examples:
111 111
112 112 # all-random
113 113
114 114 This mark tells the testing machinery that all subsequent examples should
115 115 be treated as random (ignoring their output). They are still executed,
116 116 so if a they raise an error, it will be detected as such, but their
117 117 output is completely ignored.
118 118
119 119 >>> 1+3
120 120 junk goes here...
121 121
122 122 >>> 1+3
123 123 klasdfj;
124 124
125 125 >>> 1+2
126 126 again, anything goes
127 127 blah...
128 128 """
129 129 pass
130 130
131 131 @doctest_refactor_print
132 132 def iprand():
133 133 """Some ipython tests with random output.
134 134
135 135 In [7]: 3+4
136 136 Out[7]: 7
137 137
138 In [8]: print 'hello'
138 In [8]: print('hello')
139 139 world # random
140 140
141 141 In [9]: iprand()
142 142 Out[9]: 'iprand'
143 143 """
144 144 return 'iprand'
145 145
146 146 @doctest_refactor_print
147 147 def iprand_all():
148 148 """Some ipython tests with fully random output.
149 149
150 150 # all-random
151 151
152 152 In [7]: 1
153 153 Out[7]: 99
154 154
155 In [8]: print 'hello'
155 In [8]: print('hello')
156 156 world
157 157
158 158 In [9]: iprand_all()
159 159 Out[9]: 'junk'
160 160 """
161 161 return 'iprand_all'
162
163
164
@@ -1,33 +1,33 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 ... print i,
20 ... print i+1,
19 ... print(i, end=' ')
20 ... print(i+1, end=' ')
21 21 ...
22 0 1 1 2 2 3
22 0 1 1 2 2 3
23 23 """
24 24 return 'pyfunc'
25 25
26 26
27 27 def ipyfunc2():
28 28 """Some pure python tests...
29 29
30 30 >>> 1+1
31 31 2
32 32 """
33 33 return 'pyfunc2'
@@ -1,444 +1,444 b''
1 1 """Generic testing tools.
2 2
3 3 Authors
4 4 -------
5 5 - Fernando Perez <Fernando.Perez@berkeley.edu>
6 6 """
7 7
8 8 from __future__ import absolute_import
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Copyright (C) 2009 The IPython Development Team
12 12 #
13 13 # Distributed under the terms of the BSD License. The full license is in
14 14 # the file COPYING, distributed as part of this software.
15 15 #-----------------------------------------------------------------------------
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Imports
19 19 #-----------------------------------------------------------------------------
20 20
21 21 import os
22 22 import re
23 23 import sys
24 24 import tempfile
25 25
26 26 from contextlib import contextmanager
27 27 from io import StringIO
28 28 from subprocess import Popen, PIPE
29 29
30 30 try:
31 31 # These tools are used by parts of the runtime, so we make the nose
32 32 # dependency optional at this point. Nose is a hard dependency to run the
33 33 # test suite, but NOT to use ipython itself.
34 34 import nose.tools as nt
35 35 has_nose = True
36 36 except ImportError:
37 37 has_nose = False
38 38
39 39 from IPython.config.loader import Config
40 40 from IPython.utils.process import get_output_error_code
41 41 from IPython.utils.text import list_strings
42 42 from IPython.utils.io import temp_pyfile, Tee
43 43 from IPython.utils import py3compat
44 44 from IPython.utils.encoding import DEFAULT_ENCODING
45 45
46 46 from . import decorators as dec
47 47 from . import skipdoctest
48 48
49 49 #-----------------------------------------------------------------------------
50 50 # Functions and classes
51 51 #-----------------------------------------------------------------------------
52 52
53 53 # The docstring for full_path doctests differently on win32 (different path
54 54 # separator) so just skip the doctest there. The example remains informative.
55 55 doctest_deco = skipdoctest.skip_doctest if sys.platform == 'win32' else dec.null_deco
56 56
57 57 @doctest_deco
58 58 def full_path(startPath,files):
59 59 """Make full paths for all the listed files, based on startPath.
60 60
61 61 Only the base part of startPath is kept, since this routine is typically
62 62 used with a script's __file__ variable as startPath. The base of startPath
63 63 is then prepended to all the listed files, forming the output list.
64 64
65 65 Parameters
66 66 ----------
67 67 startPath : string
68 68 Initial path to use as the base for the results. This path is split
69 69 using os.path.split() and only its first component is kept.
70 70
71 71 files : string or list
72 72 One or more files.
73 73
74 74 Examples
75 75 --------
76 76
77 77 >>> full_path('/foo/bar.py',['a.txt','b.txt'])
78 78 ['/foo/a.txt', '/foo/b.txt']
79 79
80 80 >>> full_path('/foo',['a.txt','b.txt'])
81 81 ['/a.txt', '/b.txt']
82 82
83 83 If a single file is given, the output is still a list:
84 84 >>> full_path('/foo','a.txt')
85 85 ['/a.txt']
86 86 """
87 87
88 88 files = list_strings(files)
89 89 base = os.path.split(startPath)[0]
90 90 return [ os.path.join(base,f) for f in files ]
91 91
92 92
93 93 def parse_test_output(txt):
94 94 """Parse the output of a test run and return errors, failures.
95 95
96 96 Parameters
97 97 ----------
98 98 txt : str
99 99 Text output of a test run, assumed to contain a line of one of the
100 100 following forms::
101 101
102 102 'FAILED (errors=1)'
103 103 'FAILED (failures=1)'
104 104 'FAILED (errors=1, failures=1)'
105 105
106 106 Returns
107 107 -------
108 108 nerr, nfail: number of errors and failures.
109 109 """
110 110
111 111 err_m = re.search(r'^FAILED \(errors=(\d+)\)', txt, re.MULTILINE)
112 112 if err_m:
113 113 nerr = int(err_m.group(1))
114 114 nfail = 0
115 115 return nerr, nfail
116 116
117 117 fail_m = re.search(r'^FAILED \(failures=(\d+)\)', txt, re.MULTILINE)
118 118 if fail_m:
119 119 nerr = 0
120 120 nfail = int(fail_m.group(1))
121 121 return nerr, nfail
122 122
123 123 both_m = re.search(r'^FAILED \(errors=(\d+), failures=(\d+)\)', txt,
124 124 re.MULTILINE)
125 125 if both_m:
126 126 nerr = int(both_m.group(1))
127 127 nfail = int(both_m.group(2))
128 128 return nerr, nfail
129 129
130 130 # If the input didn't match any of these forms, assume no error/failures
131 131 return 0, 0
132 132
133 133
134 134 # So nose doesn't think this is a test
135 135 parse_test_output.__test__ = False
136 136
137 137
138 138 def default_argv():
139 139 """Return a valid default argv for creating testing instances of ipython"""
140 140
141 141 return ['--quick', # so no config file is loaded
142 142 # Other defaults to minimize side effects on stdout
143 143 '--colors=NoColor', '--no-term-title','--no-banner',
144 144 '--autocall=0']
145 145
146 146
147 147 def default_config():
148 148 """Return a config object with good defaults for testing."""
149 149 config = Config()
150 150 config.TerminalInteractiveShell.colors = 'NoColor'
151 151 config.TerminalTerminalInteractiveShell.term_title = False,
152 152 config.TerminalInteractiveShell.autocall = 0
153 153 config.HistoryManager.hist_file = tempfile.mktemp(u'test_hist.sqlite')
154 154 config.HistoryManager.db_cache_size = 10000
155 155 return config
156 156
157 157
158 158 def get_ipython_cmd(as_string=False):
159 159 """
160 160 Return appropriate IPython command line name. By default, this will return
161 161 a list that can be used with subprocess.Popen, for example, but passing
162 162 `as_string=True` allows for returning the IPython command as a string.
163 163
164 164 Parameters
165 165 ----------
166 166 as_string: bool
167 167 Flag to allow to return the command as a string.
168 168 """
169 169 ipython_cmd = [sys.executable, "-m", "IPython"]
170 170
171 171 if as_string:
172 172 ipython_cmd = " ".join(ipython_cmd)
173 173
174 174 return ipython_cmd
175 175
176 176 def ipexec(fname, options=None):
177 177 """Utility to call 'ipython filename'.
178 178
179 179 Starts IPython with a minimal and safe configuration to make startup as fast
180 180 as possible.
181 181
182 182 Note that this starts IPython in a subprocess!
183 183
184 184 Parameters
185 185 ----------
186 186 fname : str
187 187 Name of file to be executed (should have .py or .ipy extension).
188 188
189 189 options : optional, list
190 190 Extra command-line flags to be passed to IPython.
191 191
192 192 Returns
193 193 -------
194 194 (stdout, stderr) of ipython subprocess.
195 195 """
196 196 if options is None: options = []
197 197
198 198 # For these subprocess calls, eliminate all prompt printing so we only see
199 199 # output from script execution
200 200 prompt_opts = [ '--PromptManager.in_template=""',
201 201 '--PromptManager.in2_template=""',
202 202 '--PromptManager.out_template=""'
203 203 ]
204 204 cmdargs = default_argv() + prompt_opts + options
205 205
206 206 test_dir = os.path.dirname(__file__)
207 207
208 208 ipython_cmd = get_ipython_cmd()
209 209 # Absolute path for filename
210 210 full_fname = os.path.join(test_dir, fname)
211 211 full_cmd = ipython_cmd + cmdargs + [full_fname]
212 212 p = Popen(full_cmd, stdout=PIPE, stderr=PIPE)
213 213 out, err = p.communicate()
214 214 out, err = py3compat.bytes_to_str(out), py3compat.bytes_to_str(err)
215 215 # `import readline` causes 'ESC[?1034h' to be output sometimes,
216 216 # so strip that out before doing comparisons
217 217 if out:
218 218 out = re.sub(r'\x1b\[[^h]+h', '', out)
219 219 return out, err
220 220
221 221
222 222 def ipexec_validate(fname, expected_out, expected_err='',
223 223 options=None):
224 224 """Utility to call 'ipython filename' and validate output/error.
225 225
226 226 This function raises an AssertionError if the validation fails.
227 227
228 228 Note that this starts IPython in a subprocess!
229 229
230 230 Parameters
231 231 ----------
232 232 fname : str
233 233 Name of the file to be executed (should have .py or .ipy extension).
234 234
235 235 expected_out : str
236 236 Expected stdout of the process.
237 237
238 238 expected_err : optional, str
239 239 Expected stderr of the process.
240 240
241 241 options : optional, list
242 242 Extra command-line flags to be passed to IPython.
243 243
244 244 Returns
245 245 -------
246 246 None
247 247 """
248 248
249 249 import nose.tools as nt
250 250
251 251 out, err = ipexec(fname, options)
252 252 #print 'OUT', out # dbg
253 253 #print 'ERR', err # dbg
254 254 # If there are any errors, we must check those befor stdout, as they may be
255 255 # more informative than simply having an empty stdout.
256 256 if err:
257 257 if expected_err:
258 258 nt.assert_equal("\n".join(err.strip().splitlines()), "\n".join(expected_err.strip().splitlines()))
259 259 else:
260 260 raise ValueError('Running file %r produced error: %r' %
261 261 (fname, err))
262 262 # If no errors or output on stderr was expected, match stdout
263 263 nt.assert_equal("\n".join(out.strip().splitlines()), "\n".join(expected_out.strip().splitlines()))
264 264
265 265
266 266 class TempFileMixin(object):
267 267 """Utility class to create temporary Python/IPython files.
268 268
269 269 Meant as a mixin class for test cases."""
270 270
271 271 def mktmp(self, src, ext='.py'):
272 272 """Make a valid python temp file."""
273 273 fname, f = temp_pyfile(src, ext)
274 274 self.tmpfile = f
275 275 self.fname = fname
276 276
277 277 def tearDown(self):
278 278 if hasattr(self, 'tmpfile'):
279 279 # If the tmpfile wasn't made because of skipped tests, like in
280 280 # win32, there's nothing to cleanup.
281 281 self.tmpfile.close()
282 282 try:
283 283 os.unlink(self.fname)
284 284 except:
285 285 # On Windows, even though we close the file, we still can't
286 286 # delete it. I have no clue why
287 287 pass
288 288
289 289 pair_fail_msg = ("Testing {0}\n\n"
290 290 "In:\n"
291 291 " {1!r}\n"
292 292 "Expected:\n"
293 293 " {2!r}\n"
294 294 "Got:\n"
295 295 " {3!r}\n")
296 296 def check_pairs(func, pairs):
297 297 """Utility function for the common case of checking a function with a
298 298 sequence of input/output pairs.
299 299
300 300 Parameters
301 301 ----------
302 302 func : callable
303 303 The function to be tested. Should accept a single argument.
304 304 pairs : iterable
305 305 A list of (input, expected_output) tuples.
306 306
307 307 Returns
308 308 -------
309 309 None. Raises an AssertionError if any output does not match the expected
310 310 value.
311 311 """
312 312 name = getattr(func, "func_name", getattr(func, "__name__", "<unknown>"))
313 313 for inp, expected in pairs:
314 314 out = func(inp)
315 315 assert out == expected, pair_fail_msg.format(name, inp, expected, out)
316 316
317 317
318 318 if py3compat.PY3:
319 319 MyStringIO = StringIO
320 320 else:
321 321 # In Python 2, stdout/stderr can have either bytes or unicode written to them,
322 322 # so we need a class that can handle both.
323 323 class MyStringIO(StringIO):
324 324 def write(self, s):
325 325 s = py3compat.cast_unicode(s, encoding=DEFAULT_ENCODING)
326 326 super(MyStringIO, self).write(s)
327 327
328 328 notprinted_msg = """Did not find {0!r} in printed output (on {1}):
329 329 -------
330 330 {2!s}
331 331 -------
332 332 """
333 333
334 334 class AssertPrints(object):
335 335 """Context manager for testing that code prints certain text.
336 336
337 337 Examples
338 338 --------
339 339 >>> with AssertPrints("abc", suppress=False):
340 ... print "abcd"
341 ... print "def"
340 ... print("abcd")
341 ... print("def")
342 342 ...
343 343 abcd
344 344 def
345 345 """
346 346 def __init__(self, s, channel='stdout', suppress=True):
347 347 self.s = s
348 348 if isinstance(self.s, py3compat.string_types):
349 349 self.s = [self.s]
350 350 self.channel = channel
351 351 self.suppress = suppress
352 352
353 353 def __enter__(self):
354 354 self.orig_stream = getattr(sys, self.channel)
355 355 self.buffer = MyStringIO()
356 356 self.tee = Tee(self.buffer, channel=self.channel)
357 357 setattr(sys, self.channel, self.buffer if self.suppress else self.tee)
358 358
359 359 def __exit__(self, etype, value, traceback):
360 360 if value is not None:
361 361 # If an error was raised, don't check anything else
362 362 return False
363 363 self.tee.flush()
364 364 setattr(sys, self.channel, self.orig_stream)
365 365 printed = self.buffer.getvalue()
366 366 for s in self.s:
367 367 assert s in printed, notprinted_msg.format(s, self.channel, printed)
368 368 return False
369 369
370 370 printed_msg = """Found {0!r} in printed output (on {1}):
371 371 -------
372 372 {2!s}
373 373 -------
374 374 """
375 375
376 376 class AssertNotPrints(AssertPrints):
377 377 """Context manager for checking that certain output *isn't* produced.
378 378
379 379 Counterpart of AssertPrints"""
380 380 def __exit__(self, etype, value, traceback):
381 381 if value is not None:
382 382 # If an error was raised, don't check anything else
383 383 return False
384 384 self.tee.flush()
385 385 setattr(sys, self.channel, self.orig_stream)
386 386 printed = self.buffer.getvalue()
387 387 for s in self.s:
388 388 assert s not in printed, printed_msg.format(s, self.channel, printed)
389 389 return False
390 390
391 391 @contextmanager
392 392 def mute_warn():
393 393 from IPython.utils import warn
394 394 save_warn = warn.warn
395 395 warn.warn = lambda *a, **kw: None
396 396 try:
397 397 yield
398 398 finally:
399 399 warn.warn = save_warn
400 400
401 401 @contextmanager
402 402 def make_tempfile(name):
403 403 """ Create an empty, named, temporary file for the duration of the context.
404 404 """
405 405 f = open(name, 'w')
406 406 f.close()
407 407 try:
408 408 yield
409 409 finally:
410 410 os.unlink(name)
411 411
412 412
413 413 @contextmanager
414 414 def monkeypatch(obj, name, attr):
415 415 """
416 416 Context manager to replace attribute named `name` in `obj` with `attr`.
417 417 """
418 418 orig = getattr(obj, name)
419 419 setattr(obj, name, attr)
420 420 yield
421 421 setattr(obj, name, orig)
422 422
423 423
424 424 def help_output_test(subcommand=''):
425 425 """test that `ipython [subcommand] -h` works"""
426 426 cmd = ' '.join(get_ipython_cmd() + [subcommand, '-h'])
427 427 out, err, rc = get_output_error_code(cmd)
428 428 nt.assert_equal(rc, 0, err)
429 429 nt.assert_not_in("Traceback", err)
430 430 nt.assert_in("Options", out)
431 431 nt.assert_in("--help-all", out)
432 432 return out, err
433 433
434 434
435 435 def help_all_output_test(subcommand=''):
436 436 """test that `ipython [subcommand] --help-all` works"""
437 437 cmd = ' '.join(get_ipython_cmd() + [subcommand, '--help-all'])
438 438 out, err, rc = get_output_error_code(cmd)
439 439 nt.assert_equal(rc, 0, err)
440 440 nt.assert_not_in("Traceback", err)
441 441 nt.assert_in("Options", out)
442 442 nt.assert_in("Class parameters", out)
443 443 return out, err
444 444
General Comments 0
You need to be logged in to leave comments. Login now