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