##// END OF EJS Templates
Test process exit codes with terminating signal
Thomas Kluyver -
Show More
@@ -1,659 +1,682 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7
7
8 Authors
8 Authors
9 -------
9 -------
10 * Fernando Perez
10 * Fernando Perez
11 """
11 """
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2011 The IPython Development Team
13 # Copyright (C) 2011 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # stdlib
22 # stdlib
23 import ast
23 import ast
24 import os
24 import os
25 import signal
25 import shutil
26 import shutil
26 import sys
27 import sys
27 import tempfile
28 import tempfile
28 import unittest
29 import unittest
29 from os.path import join
30 from os.path import join
30 from StringIO import StringIO
31 from StringIO import StringIO
31
32
32 # third-party
33 # third-party
33 import nose.tools as nt
34 import nose.tools as nt
34
35
35 # Our own
36 # Our own
36 from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths
37 from IPython.testing.decorators import skipif, skip_win32, onlyif_unicode_paths
37 from IPython.testing import tools as tt
38 from IPython.testing import tools as tt
38 from IPython.utils import io
39 from IPython.utils import io
39
40
40 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
41 # Globals
42 # Globals
42 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
43 # This is used by every single test, no point repeating it ad nauseam
44 # This is used by every single test, no point repeating it ad nauseam
44 ip = get_ipython()
45 ip = get_ipython()
45
46
46 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
47 # Tests
48 # Tests
48 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
49
50
50 class InteractiveShellTestCase(unittest.TestCase):
51 class InteractiveShellTestCase(unittest.TestCase):
51 def test_naked_string_cells(self):
52 def test_naked_string_cells(self):
52 """Test that cells with only naked strings are fully executed"""
53 """Test that cells with only naked strings are fully executed"""
53 # First, single-line inputs
54 # First, single-line inputs
54 ip.run_cell('"a"\n')
55 ip.run_cell('"a"\n')
55 self.assertEqual(ip.user_ns['_'], 'a')
56 self.assertEqual(ip.user_ns['_'], 'a')
56 # And also multi-line cells
57 # And also multi-line cells
57 ip.run_cell('"""a\nb"""\n')
58 ip.run_cell('"""a\nb"""\n')
58 self.assertEqual(ip.user_ns['_'], 'a\nb')
59 self.assertEqual(ip.user_ns['_'], 'a\nb')
59
60
60 def test_run_empty_cell(self):
61 def test_run_empty_cell(self):
61 """Just make sure we don't get a horrible error with a blank
62 """Just make sure we don't get a horrible error with a blank
62 cell of input. Yes, I did overlook that."""
63 cell of input. Yes, I did overlook that."""
63 old_xc = ip.execution_count
64 old_xc = ip.execution_count
64 ip.run_cell('')
65 ip.run_cell('')
65 self.assertEqual(ip.execution_count, old_xc)
66 self.assertEqual(ip.execution_count, old_xc)
66
67
67 def test_run_cell_multiline(self):
68 def test_run_cell_multiline(self):
68 """Multi-block, multi-line cells must execute correctly.
69 """Multi-block, multi-line cells must execute correctly.
69 """
70 """
70 src = '\n'.join(["x=1",
71 src = '\n'.join(["x=1",
71 "y=2",
72 "y=2",
72 "if 1:",
73 "if 1:",
73 " x += 1",
74 " x += 1",
74 " y += 1",])
75 " y += 1",])
75 ip.run_cell(src)
76 ip.run_cell(src)
76 self.assertEqual(ip.user_ns['x'], 2)
77 self.assertEqual(ip.user_ns['x'], 2)
77 self.assertEqual(ip.user_ns['y'], 3)
78 self.assertEqual(ip.user_ns['y'], 3)
78
79
79 def test_multiline_string_cells(self):
80 def test_multiline_string_cells(self):
80 "Code sprinkled with multiline strings should execute (GH-306)"
81 "Code sprinkled with multiline strings should execute (GH-306)"
81 ip.run_cell('tmp=0')
82 ip.run_cell('tmp=0')
82 self.assertEqual(ip.user_ns['tmp'], 0)
83 self.assertEqual(ip.user_ns['tmp'], 0)
83 ip.run_cell('tmp=1;"""a\nb"""\n')
84 ip.run_cell('tmp=1;"""a\nb"""\n')
84 self.assertEqual(ip.user_ns['tmp'], 1)
85 self.assertEqual(ip.user_ns['tmp'], 1)
85
86
86 def test_dont_cache_with_semicolon(self):
87 def test_dont_cache_with_semicolon(self):
87 "Ending a line with semicolon should not cache the returned object (GH-307)"
88 "Ending a line with semicolon should not cache the returned object (GH-307)"
88 oldlen = len(ip.user_ns['Out'])
89 oldlen = len(ip.user_ns['Out'])
89 a = ip.run_cell('1;', store_history=True)
90 a = ip.run_cell('1;', store_history=True)
90 newlen = len(ip.user_ns['Out'])
91 newlen = len(ip.user_ns['Out'])
91 self.assertEqual(oldlen, newlen)
92 self.assertEqual(oldlen, newlen)
92 #also test the default caching behavior
93 #also test the default caching behavior
93 ip.run_cell('1', store_history=True)
94 ip.run_cell('1', store_history=True)
94 newlen = len(ip.user_ns['Out'])
95 newlen = len(ip.user_ns['Out'])
95 self.assertEqual(oldlen+1, newlen)
96 self.assertEqual(oldlen+1, newlen)
96
97
97 def test_In_variable(self):
98 def test_In_variable(self):
98 "Verify that In variable grows with user input (GH-284)"
99 "Verify that In variable grows with user input (GH-284)"
99 oldlen = len(ip.user_ns['In'])
100 oldlen = len(ip.user_ns['In'])
100 ip.run_cell('1;', store_history=True)
101 ip.run_cell('1;', store_history=True)
101 newlen = len(ip.user_ns['In'])
102 newlen = len(ip.user_ns['In'])
102 self.assertEqual(oldlen+1, newlen)
103 self.assertEqual(oldlen+1, newlen)
103 self.assertEqual(ip.user_ns['In'][-1],'1;')
104 self.assertEqual(ip.user_ns['In'][-1],'1;')
104
105
105 def test_magic_names_in_string(self):
106 def test_magic_names_in_string(self):
106 ip.run_cell('a = """\n%exit\n"""')
107 ip.run_cell('a = """\n%exit\n"""')
107 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
108 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
108
109
109 def test_alias_crash(self):
110 def test_alias_crash(self):
110 """Errors in prefilter can't crash IPython"""
111 """Errors in prefilter can't crash IPython"""
111 ip.run_cell('%alias parts echo first %s second %s')
112 ip.run_cell('%alias parts echo first %s second %s')
112 # capture stderr:
113 # capture stderr:
113 save_err = io.stderr
114 save_err = io.stderr
114 io.stderr = StringIO()
115 io.stderr = StringIO()
115 ip.run_cell('parts 1')
116 ip.run_cell('parts 1')
116 err = io.stderr.getvalue()
117 err = io.stderr.getvalue()
117 io.stderr = save_err
118 io.stderr = save_err
118 self.assertEqual(err.split(':')[0], 'ERROR')
119 self.assertEqual(err.split(':')[0], 'ERROR')
119
120
120 def test_trailing_newline(self):
121 def test_trailing_newline(self):
121 """test that running !(command) does not raise a SyntaxError"""
122 """test that running !(command) does not raise a SyntaxError"""
122 ip.run_cell('!(true)\n', False)
123 ip.run_cell('!(true)\n', False)
123 ip.run_cell('!(true)\n\n\n', False)
124 ip.run_cell('!(true)\n\n\n', False)
124
125
125 def test_gh_597(self):
126 def test_gh_597(self):
126 """Pretty-printing lists of objects with non-ascii reprs may cause
127 """Pretty-printing lists of objects with non-ascii reprs may cause
127 problems."""
128 problems."""
128 class Spam(object):
129 class Spam(object):
129 def __repr__(self):
130 def __repr__(self):
130 return "\xe9"*50
131 return "\xe9"*50
131 import IPython.core.formatters
132 import IPython.core.formatters
132 f = IPython.core.formatters.PlainTextFormatter()
133 f = IPython.core.formatters.PlainTextFormatter()
133 f([Spam(),Spam()])
134 f([Spam(),Spam()])
134
135
135
136
136 def test_future_flags(self):
137 def test_future_flags(self):
137 """Check that future flags are used for parsing code (gh-777)"""
138 """Check that future flags are used for parsing code (gh-777)"""
138 ip.run_cell('from __future__ import print_function')
139 ip.run_cell('from __future__ import print_function')
139 try:
140 try:
140 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
141 ip.run_cell('prfunc_return_val = print(1,2, sep=" ")')
141 assert 'prfunc_return_val' in ip.user_ns
142 assert 'prfunc_return_val' in ip.user_ns
142 finally:
143 finally:
143 # Reset compiler flags so we don't mess up other tests.
144 # Reset compiler flags so we don't mess up other tests.
144 ip.compile.reset_compiler_flags()
145 ip.compile.reset_compiler_flags()
145
146
146 def test_future_unicode(self):
147 def test_future_unicode(self):
147 """Check that unicode_literals is imported from __future__ (gh #786)"""
148 """Check that unicode_literals is imported from __future__ (gh #786)"""
148 try:
149 try:
149 ip.run_cell(u'byte_str = "a"')
150 ip.run_cell(u'byte_str = "a"')
150 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
151 assert isinstance(ip.user_ns['byte_str'], str) # string literals are byte strings by default
151 ip.run_cell('from __future__ import unicode_literals')
152 ip.run_cell('from __future__ import unicode_literals')
152 ip.run_cell(u'unicode_str = "a"')
153 ip.run_cell(u'unicode_str = "a"')
153 assert isinstance(ip.user_ns['unicode_str'], unicode) # strings literals are now unicode
154 assert isinstance(ip.user_ns['unicode_str'], unicode) # strings literals are now unicode
154 finally:
155 finally:
155 # Reset compiler flags so we don't mess up other tests.
156 # Reset compiler flags so we don't mess up other tests.
156 ip.compile.reset_compiler_flags()
157 ip.compile.reset_compiler_flags()
157
158
158 def test_can_pickle(self):
159 def test_can_pickle(self):
159 "Can we pickle objects defined interactively (GH-29)"
160 "Can we pickle objects defined interactively (GH-29)"
160 ip = get_ipython()
161 ip = get_ipython()
161 ip.reset()
162 ip.reset()
162 ip.run_cell(("class Mylist(list):\n"
163 ip.run_cell(("class Mylist(list):\n"
163 " def __init__(self,x=[]):\n"
164 " def __init__(self,x=[]):\n"
164 " list.__init__(self,x)"))
165 " list.__init__(self,x)"))
165 ip.run_cell("w=Mylist([1,2,3])")
166 ip.run_cell("w=Mylist([1,2,3])")
166
167
167 from cPickle import dumps
168 from cPickle import dumps
168
169
169 # We need to swap in our main module - this is only necessary
170 # We need to swap in our main module - this is only necessary
170 # inside the test framework, because IPython puts the interactive module
171 # inside the test framework, because IPython puts the interactive module
171 # in place (but the test framework undoes this).
172 # in place (but the test framework undoes this).
172 _main = sys.modules['__main__']
173 _main = sys.modules['__main__']
173 sys.modules['__main__'] = ip.user_module
174 sys.modules['__main__'] = ip.user_module
174 try:
175 try:
175 res = dumps(ip.user_ns["w"])
176 res = dumps(ip.user_ns["w"])
176 finally:
177 finally:
177 sys.modules['__main__'] = _main
178 sys.modules['__main__'] = _main
178 self.assertTrue(isinstance(res, bytes))
179 self.assertTrue(isinstance(res, bytes))
179
180
180 def test_global_ns(self):
181 def test_global_ns(self):
181 "Code in functions must be able to access variables outside them."
182 "Code in functions must be able to access variables outside them."
182 ip = get_ipython()
183 ip = get_ipython()
183 ip.run_cell("a = 10")
184 ip.run_cell("a = 10")
184 ip.run_cell(("def f(x):\n"
185 ip.run_cell(("def f(x):\n"
185 " return x + a"))
186 " return x + a"))
186 ip.run_cell("b = f(12)")
187 ip.run_cell("b = f(12)")
187 self.assertEqual(ip.user_ns["b"], 22)
188 self.assertEqual(ip.user_ns["b"], 22)
188
189
189 def test_bad_custom_tb(self):
190 def test_bad_custom_tb(self):
190 """Check that InteractiveShell is protected from bad custom exception handlers"""
191 """Check that InteractiveShell is protected from bad custom exception handlers"""
191 from IPython.utils import io
192 from IPython.utils import io
192 save_stderr = io.stderr
193 save_stderr = io.stderr
193 try:
194 try:
194 # capture stderr
195 # capture stderr
195 io.stderr = StringIO()
196 io.stderr = StringIO()
196 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
197 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
197 self.assertEqual(ip.custom_exceptions, (IOError,))
198 self.assertEqual(ip.custom_exceptions, (IOError,))
198 ip.run_cell(u'raise IOError("foo")')
199 ip.run_cell(u'raise IOError("foo")')
199 self.assertEqual(ip.custom_exceptions, ())
200 self.assertEqual(ip.custom_exceptions, ())
200 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
201 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
201 finally:
202 finally:
202 io.stderr = save_stderr
203 io.stderr = save_stderr
203
204
204 def test_bad_custom_tb_return(self):
205 def test_bad_custom_tb_return(self):
205 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
206 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
206 from IPython.utils import io
207 from IPython.utils import io
207 save_stderr = io.stderr
208 save_stderr = io.stderr
208 try:
209 try:
209 # capture stderr
210 # capture stderr
210 io.stderr = StringIO()
211 io.stderr = StringIO()
211 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
212 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
212 self.assertEqual(ip.custom_exceptions, (NameError,))
213 self.assertEqual(ip.custom_exceptions, (NameError,))
213 ip.run_cell(u'a=abracadabra')
214 ip.run_cell(u'a=abracadabra')
214 self.assertEqual(ip.custom_exceptions, ())
215 self.assertEqual(ip.custom_exceptions, ())
215 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
216 self.assertTrue("Custom TB Handler failed" in io.stderr.getvalue())
216 finally:
217 finally:
217 io.stderr = save_stderr
218 io.stderr = save_stderr
218
219
219 def test_drop_by_id(self):
220 def test_drop_by_id(self):
220 myvars = {"a":object(), "b":object(), "c": object()}
221 myvars = {"a":object(), "b":object(), "c": object()}
221 ip.push(myvars, interactive=False)
222 ip.push(myvars, interactive=False)
222 for name in myvars:
223 for name in myvars:
223 assert name in ip.user_ns, name
224 assert name in ip.user_ns, name
224 assert name in ip.user_ns_hidden, name
225 assert name in ip.user_ns_hidden, name
225 ip.user_ns['b'] = 12
226 ip.user_ns['b'] = 12
226 ip.drop_by_id(myvars)
227 ip.drop_by_id(myvars)
227 for name in ["a", "c"]:
228 for name in ["a", "c"]:
228 assert name not in ip.user_ns, name
229 assert name not in ip.user_ns, name
229 assert name not in ip.user_ns_hidden, name
230 assert name not in ip.user_ns_hidden, name
230 assert ip.user_ns['b'] == 12
231 assert ip.user_ns['b'] == 12
231 ip.reset()
232 ip.reset()
232
233
233 def test_var_expand(self):
234 def test_var_expand(self):
234 ip.user_ns['f'] = u'Ca\xf1o'
235 ip.user_ns['f'] = u'Ca\xf1o'
235 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
236 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
236 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
237 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
237 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
238 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
238 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
239 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
239
240
240 ip.user_ns['f'] = b'Ca\xc3\xb1o'
241 ip.user_ns['f'] = b'Ca\xc3\xb1o'
241 # This should not raise any exception:
242 # This should not raise any exception:
242 ip.var_expand(u'echo $f')
243 ip.var_expand(u'echo $f')
243
244
244 def test_var_expand_local(self):
245 def test_var_expand_local(self):
245 """Test local variable expansion in !system and %magic calls"""
246 """Test local variable expansion in !system and %magic calls"""
246 # !system
247 # !system
247 ip.run_cell('def test():\n'
248 ip.run_cell('def test():\n'
248 ' lvar = "ttt"\n'
249 ' lvar = "ttt"\n'
249 ' ret = !echo {lvar}\n'
250 ' ret = !echo {lvar}\n'
250 ' return ret[0]\n')
251 ' return ret[0]\n')
251 res = ip.user_ns['test']()
252 res = ip.user_ns['test']()
252 nt.assert_in('ttt', res)
253 nt.assert_in('ttt', res)
253
254
254 # %magic
255 # %magic
255 ip.run_cell('def makemacro():\n'
256 ip.run_cell('def makemacro():\n'
256 ' macroname = "macro_var_expand_locals"\n'
257 ' macroname = "macro_var_expand_locals"\n'
257 ' %macro {macroname} codestr\n')
258 ' %macro {macroname} codestr\n')
258 ip.user_ns['codestr'] = "str(12)"
259 ip.user_ns['codestr'] = "str(12)"
259 ip.run_cell('makemacro()')
260 ip.run_cell('makemacro()')
260 nt.assert_in('macro_var_expand_locals', ip.user_ns)
261 nt.assert_in('macro_var_expand_locals', ip.user_ns)
261
262
262 def test_var_expand_self(self):
263 def test_var_expand_self(self):
263 """Test variable expansion with the name 'self', which was failing.
264 """Test variable expansion with the name 'self', which was failing.
264
265
265 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
266 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
266 """
267 """
267 ip.run_cell('class cTest:\n'
268 ip.run_cell('class cTest:\n'
268 ' classvar="see me"\n'
269 ' classvar="see me"\n'
269 ' def test(self):\n'
270 ' def test(self):\n'
270 ' res = !echo Variable: {self.classvar}\n'
271 ' res = !echo Variable: {self.classvar}\n'
271 ' return res[0]\n')
272 ' return res[0]\n')
272 nt.assert_in('see me', ip.user_ns['cTest']().test())
273 nt.assert_in('see me', ip.user_ns['cTest']().test())
273
274
274 def test_bad_var_expand(self):
275 def test_bad_var_expand(self):
275 """var_expand on invalid formats shouldn't raise"""
276 """var_expand on invalid formats shouldn't raise"""
276 # SyntaxError
277 # SyntaxError
277 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
278 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
278 # NameError
279 # NameError
279 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
280 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
280 # ZeroDivisionError
281 # ZeroDivisionError
281 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
282 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
282
283
283 def test_silent_nopostexec(self):
284 def test_silent_nopostexec(self):
284 """run_cell(silent=True) doesn't invoke post-exec funcs"""
285 """run_cell(silent=True) doesn't invoke post-exec funcs"""
285 d = dict(called=False)
286 d = dict(called=False)
286 def set_called():
287 def set_called():
287 d['called'] = True
288 d['called'] = True
288
289
289 ip.register_post_execute(set_called)
290 ip.register_post_execute(set_called)
290 ip.run_cell("1", silent=True)
291 ip.run_cell("1", silent=True)
291 self.assertFalse(d['called'])
292 self.assertFalse(d['called'])
292 # double-check that non-silent exec did what we expected
293 # double-check that non-silent exec did what we expected
293 # silent to avoid
294 # silent to avoid
294 ip.run_cell("1")
295 ip.run_cell("1")
295 self.assertTrue(d['called'])
296 self.assertTrue(d['called'])
296 # remove post-exec
297 # remove post-exec
297 ip._post_execute.pop(set_called)
298 ip._post_execute.pop(set_called)
298
299
299 def test_silent_noadvance(self):
300 def test_silent_noadvance(self):
300 """run_cell(silent=True) doesn't advance execution_count"""
301 """run_cell(silent=True) doesn't advance execution_count"""
301 ec = ip.execution_count
302 ec = ip.execution_count
302 # silent should force store_history=False
303 # silent should force store_history=False
303 ip.run_cell("1", store_history=True, silent=True)
304 ip.run_cell("1", store_history=True, silent=True)
304
305
305 self.assertEqual(ec, ip.execution_count)
306 self.assertEqual(ec, ip.execution_count)
306 # double-check that non-silent exec did what we expected
307 # double-check that non-silent exec did what we expected
307 # silent to avoid
308 # silent to avoid
308 ip.run_cell("1", store_history=True)
309 ip.run_cell("1", store_history=True)
309 self.assertEqual(ec+1, ip.execution_count)
310 self.assertEqual(ec+1, ip.execution_count)
310
311
311 def test_silent_nodisplayhook(self):
312 def test_silent_nodisplayhook(self):
312 """run_cell(silent=True) doesn't trigger displayhook"""
313 """run_cell(silent=True) doesn't trigger displayhook"""
313 d = dict(called=False)
314 d = dict(called=False)
314
315
315 trap = ip.display_trap
316 trap = ip.display_trap
316 save_hook = trap.hook
317 save_hook = trap.hook
317
318
318 def failing_hook(*args, **kwargs):
319 def failing_hook(*args, **kwargs):
319 d['called'] = True
320 d['called'] = True
320
321
321 try:
322 try:
322 trap.hook = failing_hook
323 trap.hook = failing_hook
323 ip.run_cell("1", silent=True)
324 ip.run_cell("1", silent=True)
324 self.assertFalse(d['called'])
325 self.assertFalse(d['called'])
325 # double-check that non-silent exec did what we expected
326 # double-check that non-silent exec did what we expected
326 # silent to avoid
327 # silent to avoid
327 ip.run_cell("1")
328 ip.run_cell("1")
328 self.assertTrue(d['called'])
329 self.assertTrue(d['called'])
329 finally:
330 finally:
330 trap.hook = save_hook
331 trap.hook = save_hook
331
332
332 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
333 @skipif(sys.version_info[0] >= 3, "softspace removed in py3")
333 def test_print_softspace(self):
334 def test_print_softspace(self):
334 """Verify that softspace is handled correctly when executing multiple
335 """Verify that softspace is handled correctly when executing multiple
335 statements.
336 statements.
336
337
337 In [1]: print 1; print 2
338 In [1]: print 1; print 2
338 1
339 1
339 2
340 2
340
341
341 In [2]: print 1,; print 2
342 In [2]: print 1,; print 2
342 1 2
343 1 2
343 """
344 """
344
345
345 def test_ofind_line_magic(self):
346 def test_ofind_line_magic(self):
346 from IPython.core.magic import register_line_magic
347 from IPython.core.magic import register_line_magic
347
348
348 @register_line_magic
349 @register_line_magic
349 def lmagic(line):
350 def lmagic(line):
350 "A line magic"
351 "A line magic"
351
352
352 # Get info on line magic
353 # Get info on line magic
353 lfind = ip._ofind('lmagic')
354 lfind = ip._ofind('lmagic')
354 info = dict(found=True, isalias=False, ismagic=True,
355 info = dict(found=True, isalias=False, ismagic=True,
355 namespace = 'IPython internal', obj= lmagic.__wrapped__,
356 namespace = 'IPython internal', obj= lmagic.__wrapped__,
356 parent = None)
357 parent = None)
357 nt.assert_equal(lfind, info)
358 nt.assert_equal(lfind, info)
358
359
359 def test_ofind_cell_magic(self):
360 def test_ofind_cell_magic(self):
360 from IPython.core.magic import register_cell_magic
361 from IPython.core.magic import register_cell_magic
361
362
362 @register_cell_magic
363 @register_cell_magic
363 def cmagic(line, cell):
364 def cmagic(line, cell):
364 "A cell magic"
365 "A cell magic"
365
366
366 # Get info on cell magic
367 # Get info on cell magic
367 find = ip._ofind('cmagic')
368 find = ip._ofind('cmagic')
368 info = dict(found=True, isalias=False, ismagic=True,
369 info = dict(found=True, isalias=False, ismagic=True,
369 namespace = 'IPython internal', obj= cmagic.__wrapped__,
370 namespace = 'IPython internal', obj= cmagic.__wrapped__,
370 parent = None)
371 parent = None)
371 nt.assert_equal(find, info)
372 nt.assert_equal(find, info)
372
373
373 def test_custom_exception(self):
374 def test_custom_exception(self):
374 called = []
375 called = []
375 def my_handler(shell, etype, value, tb, tb_offset=None):
376 def my_handler(shell, etype, value, tb, tb_offset=None):
376 called.append(etype)
377 called.append(etype)
377 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
378 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
378
379
379 ip.set_custom_exc((ValueError,), my_handler)
380 ip.set_custom_exc((ValueError,), my_handler)
380 try:
381 try:
381 ip.run_cell("raise ValueError('test')")
382 ip.run_cell("raise ValueError('test')")
382 # Check that this was called, and only once.
383 # Check that this was called, and only once.
383 self.assertEqual(called, [ValueError])
384 self.assertEqual(called, [ValueError])
384 finally:
385 finally:
385 # Reset the custom exception hook
386 # Reset the custom exception hook
386 ip.set_custom_exc((), None)
387 ip.set_custom_exc((), None)
387
388
388 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
389 @skipif(sys.version_info[0] >= 3, "no differences with __future__ in py3")
389 def test_future_environment(self):
390 def test_future_environment(self):
390 "Can we run code with & without the shell's __future__ imports?"
391 "Can we run code with & without the shell's __future__ imports?"
391 ip.run_cell("from __future__ import division")
392 ip.run_cell("from __future__ import division")
392 ip.run_cell("a = 1/2", shell_futures=True)
393 ip.run_cell("a = 1/2", shell_futures=True)
393 self.assertEqual(ip.user_ns['a'], 0.5)
394 self.assertEqual(ip.user_ns['a'], 0.5)
394 ip.run_cell("b = 1/2", shell_futures=False)
395 ip.run_cell("b = 1/2", shell_futures=False)
395 self.assertEqual(ip.user_ns['b'], 0)
396 self.assertEqual(ip.user_ns['b'], 0)
396
397
397 ip.compile.reset_compiler_flags()
398 ip.compile.reset_compiler_flags()
398 # This shouldn't leak to the shell's compiler
399 # This shouldn't leak to the shell's compiler
399 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
400 ip.run_cell("from __future__ import division \nc=1/2", shell_futures=False)
400 self.assertEqual(ip.user_ns['c'], 0.5)
401 self.assertEqual(ip.user_ns['c'], 0.5)
401 ip.run_cell("d = 1/2", shell_futures=True)
402 ip.run_cell("d = 1/2", shell_futures=True)
402 self.assertEqual(ip.user_ns['d'], 0)
403 self.assertEqual(ip.user_ns['d'], 0)
403
404
404
405
405 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
406 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
406
407
407 @onlyif_unicode_paths
408 @onlyif_unicode_paths
408 def setUp(self):
409 def setUp(self):
409 self.BASETESTDIR = tempfile.mkdtemp()
410 self.BASETESTDIR = tempfile.mkdtemp()
410 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
411 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
411 os.mkdir(self.TESTDIR)
412 os.mkdir(self.TESTDIR)
412 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
413 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
413 sfile.write("pass\n")
414 sfile.write("pass\n")
414 self.oldpath = os.getcwdu()
415 self.oldpath = os.getcwdu()
415 os.chdir(self.TESTDIR)
416 os.chdir(self.TESTDIR)
416 self.fname = u"Γ₯Àâtestscript.py"
417 self.fname = u"Γ₯Àâtestscript.py"
417
418
418 def tearDown(self):
419 def tearDown(self):
419 os.chdir(self.oldpath)
420 os.chdir(self.oldpath)
420 shutil.rmtree(self.BASETESTDIR)
421 shutil.rmtree(self.BASETESTDIR)
421
422
422 @onlyif_unicode_paths
423 @onlyif_unicode_paths
423 def test_1(self):
424 def test_1(self):
424 """Test safe_execfile with non-ascii path
425 """Test safe_execfile with non-ascii path
425 """
426 """
426 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
427 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
427
428
429 class ExitCodeChecks(tt.TempFileMixin):
430 def test_exit_code_ok(self):
431 self.system('exit 0')
432 self.assertEqual(ip.user_ns['_exit_code'], 0)
433
434 def test_exit_code_error(self):
435 self.system('exit 1')
436 self.assertEqual(ip.user_ns['_exit_code'], 1)
437
438 @skipif(not hasattr(signal, 'SIGALRM'))
439 def test_exit_code_signal(self):
440 self.mktmp("import signal, time\n"
441 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
442 "time.sleep(1)\n")
443 self.system("%s %s" % (sys.executable, self.fname))
444 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
445
446 class TestSystemRaw(unittest.TestCase, ExitCodeChecks):
447 system = ip.system_raw
428
448
429 class TestSystemRaw(unittest.TestCase):
430 @onlyif_unicode_paths
449 @onlyif_unicode_paths
431 def test_1(self):
450 def test_1(self):
432 """Test system_raw with non-ascii cmd
451 """Test system_raw with non-ascii cmd
433 """
452 """
434 cmd = ur'''python -c "'Γ₯Àâ'" '''
453 cmd = u'''python -c "'Γ₯Àâ'" '''
435 ip.system_raw(cmd)
454 ip.system_raw(cmd)
436
437 def test_exit_code(self):
438 """Test that the exit code is parsed correctly."""
439 ip.system_raw('exit 1')
440 self.assertEqual(ip.user_ns['_exit_code'], 1)
441
455
442 class TestSystemPiped(unittest.TestCase):
456 # TODO: Exit codes are currently ignored on Windows.
443 # TODO: Exit codes are currently ignored on Windows.
457 class TestSystemPipedExitCode(unittest.TestCase, ExitCodeChecks):
458 system = ip.system_piped
459
444 @skip_win32
460 @skip_win32
445 def test_exit_code(self):
461 def test_exit_code_ok(self):
446 ip.system_piped('exit 1')
462 ExitCodeChecks.test_exit_code_ok(self)
447 self.assertEqual(ip.user_ns['_exit_code'], 1)
463
464 @skip_win32
465 def test_exit_code_error(self):
466 ExitCodeChecks.test_exit_code_error(self)
467
468 @skip_win32
469 def test_exit_code_signal(self):
470 ExitCodeChecks.test_exit_code_signal(self)
448
471
449 class TestModules(unittest.TestCase, tt.TempFileMixin):
472 class TestModules(unittest.TestCase, tt.TempFileMixin):
450 def test_extraneous_loads(self):
473 def test_extraneous_loads(self):
451 """Test we're not loading modules on startup that we shouldn't.
474 """Test we're not loading modules on startup that we shouldn't.
452 """
475 """
453 self.mktmp("import sys\n"
476 self.mktmp("import sys\n"
454 "print('numpy' in sys.modules)\n"
477 "print('numpy' in sys.modules)\n"
455 "print('IPython.parallel' in sys.modules)\n"
478 "print('IPython.parallel' in sys.modules)\n"
456 "print('IPython.kernel.zmq' in sys.modules)\n"
479 "print('IPython.kernel.zmq' in sys.modules)\n"
457 )
480 )
458 out = "False\nFalse\nFalse\n"
481 out = "False\nFalse\nFalse\n"
459 tt.ipexec_validate(self.fname, out)
482 tt.ipexec_validate(self.fname, out)
460
483
461 class Negator(ast.NodeTransformer):
484 class Negator(ast.NodeTransformer):
462 """Negates all number literals in an AST."""
485 """Negates all number literals in an AST."""
463 def visit_Num(self, node):
486 def visit_Num(self, node):
464 node.n = -node.n
487 node.n = -node.n
465 return node
488 return node
466
489
467 class TestAstTransform(unittest.TestCase):
490 class TestAstTransform(unittest.TestCase):
468 def setUp(self):
491 def setUp(self):
469 self.negator = Negator()
492 self.negator = Negator()
470 ip.ast_transformers.append(self.negator)
493 ip.ast_transformers.append(self.negator)
471
494
472 def tearDown(self):
495 def tearDown(self):
473 ip.ast_transformers.remove(self.negator)
496 ip.ast_transformers.remove(self.negator)
474
497
475 def test_run_cell(self):
498 def test_run_cell(self):
476 with tt.AssertPrints('-34'):
499 with tt.AssertPrints('-34'):
477 ip.run_cell('print (12 + 22)')
500 ip.run_cell('print (12 + 22)')
478
501
479 # A named reference to a number shouldn't be transformed.
502 # A named reference to a number shouldn't be transformed.
480 ip.user_ns['n'] = 55
503 ip.user_ns['n'] = 55
481 with tt.AssertNotPrints('-55'):
504 with tt.AssertNotPrints('-55'):
482 ip.run_cell('print (n)')
505 ip.run_cell('print (n)')
483
506
484 def test_timeit(self):
507 def test_timeit(self):
485 called = set()
508 called = set()
486 def f(x):
509 def f(x):
487 called.add(x)
510 called.add(x)
488 ip.push({'f':f})
511 ip.push({'f':f})
489
512
490 with tt.AssertPrints("best of "):
513 with tt.AssertPrints("best of "):
491 ip.run_line_magic("timeit", "-n1 f(1)")
514 ip.run_line_magic("timeit", "-n1 f(1)")
492 self.assertEqual(called, set([-1]))
515 self.assertEqual(called, set([-1]))
493 called.clear()
516 called.clear()
494
517
495 with tt.AssertPrints("best of "):
518 with tt.AssertPrints("best of "):
496 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
519 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
497 self.assertEqual(called, set([-2, -3]))
520 self.assertEqual(called, set([-2, -3]))
498
521
499 def test_time(self):
522 def test_time(self):
500 called = []
523 called = []
501 def f(x):
524 def f(x):
502 called.append(x)
525 called.append(x)
503 ip.push({'f':f})
526 ip.push({'f':f})
504
527
505 # Test with an expression
528 # Test with an expression
506 with tt.AssertPrints("Wall time: "):
529 with tt.AssertPrints("Wall time: "):
507 ip.run_line_magic("time", "f(5+9)")
530 ip.run_line_magic("time", "f(5+9)")
508 self.assertEqual(called, [-14])
531 self.assertEqual(called, [-14])
509 called[:] = []
532 called[:] = []
510
533
511 # Test with a statement (different code path)
534 # Test with a statement (different code path)
512 with tt.AssertPrints("Wall time: "):
535 with tt.AssertPrints("Wall time: "):
513 ip.run_line_magic("time", "a = f(-3 + -2)")
536 ip.run_line_magic("time", "a = f(-3 + -2)")
514 self.assertEqual(called, [5])
537 self.assertEqual(called, [5])
515
538
516 def test_macro(self):
539 def test_macro(self):
517 ip.push({'a':10})
540 ip.push({'a':10})
518 # The AST transformation makes this do a+=-1
541 # The AST transformation makes this do a+=-1
519 ip.define_macro("amacro", "a+=1\nprint(a)")
542 ip.define_macro("amacro", "a+=1\nprint(a)")
520
543
521 with tt.AssertPrints("9"):
544 with tt.AssertPrints("9"):
522 ip.run_cell("amacro")
545 ip.run_cell("amacro")
523 with tt.AssertPrints("8"):
546 with tt.AssertPrints("8"):
524 ip.run_cell("amacro")
547 ip.run_cell("amacro")
525
548
526 class IntegerWrapper(ast.NodeTransformer):
549 class IntegerWrapper(ast.NodeTransformer):
527 """Wraps all integers in a call to Integer()"""
550 """Wraps all integers in a call to Integer()"""
528 def visit_Num(self, node):
551 def visit_Num(self, node):
529 if isinstance(node.n, int):
552 if isinstance(node.n, int):
530 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
553 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
531 args=[node], keywords=[])
554 args=[node], keywords=[])
532 return node
555 return node
533
556
534 class TestAstTransform2(unittest.TestCase):
557 class TestAstTransform2(unittest.TestCase):
535 def setUp(self):
558 def setUp(self):
536 self.intwrapper = IntegerWrapper()
559 self.intwrapper = IntegerWrapper()
537 ip.ast_transformers.append(self.intwrapper)
560 ip.ast_transformers.append(self.intwrapper)
538
561
539 self.calls = []
562 self.calls = []
540 def Integer(*args):
563 def Integer(*args):
541 self.calls.append(args)
564 self.calls.append(args)
542 return args
565 return args
543 ip.push({"Integer": Integer})
566 ip.push({"Integer": Integer})
544
567
545 def tearDown(self):
568 def tearDown(self):
546 ip.ast_transformers.remove(self.intwrapper)
569 ip.ast_transformers.remove(self.intwrapper)
547 del ip.user_ns['Integer']
570 del ip.user_ns['Integer']
548
571
549 def test_run_cell(self):
572 def test_run_cell(self):
550 ip.run_cell("n = 2")
573 ip.run_cell("n = 2")
551 self.assertEqual(self.calls, [(2,)])
574 self.assertEqual(self.calls, [(2,)])
552
575
553 # This shouldn't throw an error
576 # This shouldn't throw an error
554 ip.run_cell("o = 2.0")
577 ip.run_cell("o = 2.0")
555 self.assertEqual(ip.user_ns['o'], 2.0)
578 self.assertEqual(ip.user_ns['o'], 2.0)
556
579
557 def test_timeit(self):
580 def test_timeit(self):
558 called = set()
581 called = set()
559 def f(x):
582 def f(x):
560 called.add(x)
583 called.add(x)
561 ip.push({'f':f})
584 ip.push({'f':f})
562
585
563 with tt.AssertPrints("best of "):
586 with tt.AssertPrints("best of "):
564 ip.run_line_magic("timeit", "-n1 f(1)")
587 ip.run_line_magic("timeit", "-n1 f(1)")
565 self.assertEqual(called, set([(1,)]))
588 self.assertEqual(called, set([(1,)]))
566 called.clear()
589 called.clear()
567
590
568 with tt.AssertPrints("best of "):
591 with tt.AssertPrints("best of "):
569 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
592 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
570 self.assertEqual(called, set([(2,), (3,)]))
593 self.assertEqual(called, set([(2,), (3,)]))
571
594
572 class ErrorTransformer(ast.NodeTransformer):
595 class ErrorTransformer(ast.NodeTransformer):
573 """Throws an error when it sees a number."""
596 """Throws an error when it sees a number."""
574 def visit_Num(self):
597 def visit_Num(self):
575 raise ValueError("test")
598 raise ValueError("test")
576
599
577 class TestAstTransformError(unittest.TestCase):
600 class TestAstTransformError(unittest.TestCase):
578 def test_unregistering(self):
601 def test_unregistering(self):
579 err_transformer = ErrorTransformer()
602 err_transformer = ErrorTransformer()
580 ip.ast_transformers.append(err_transformer)
603 ip.ast_transformers.append(err_transformer)
581
604
582 with tt.AssertPrints("unregister", channel='stderr'):
605 with tt.AssertPrints("unregister", channel='stderr'):
583 ip.run_cell("1 + 2")
606 ip.run_cell("1 + 2")
584
607
585 # This should have been removed.
608 # This should have been removed.
586 nt.assert_not_in(err_transformer, ip.ast_transformers)
609 nt.assert_not_in(err_transformer, ip.ast_transformers)
587
610
588 def test__IPYTHON__():
611 def test__IPYTHON__():
589 # This shouldn't raise a NameError, that's all
612 # This shouldn't raise a NameError, that's all
590 __IPYTHON__
613 __IPYTHON__
591
614
592
615
593 class DummyRepr(object):
616 class DummyRepr(object):
594 def __repr__(self):
617 def __repr__(self):
595 return "DummyRepr"
618 return "DummyRepr"
596
619
597 def _repr_html_(self):
620 def _repr_html_(self):
598 return "<b>dummy</b>"
621 return "<b>dummy</b>"
599
622
600 def _repr_javascript_(self):
623 def _repr_javascript_(self):
601 return "console.log('hi');", {'key': 'value'}
624 return "console.log('hi');", {'key': 'value'}
602
625
603
626
604 def test_user_variables():
627 def test_user_variables():
605 # enable all formatters
628 # enable all formatters
606 ip.display_formatter.active_types = ip.display_formatter.format_types
629 ip.display_formatter.active_types = ip.display_formatter.format_types
607
630
608 ip.user_ns['dummy'] = d = DummyRepr()
631 ip.user_ns['dummy'] = d = DummyRepr()
609 keys = set(['dummy', 'doesnotexist'])
632 keys = set(['dummy', 'doesnotexist'])
610 r = ip.user_variables(keys)
633 r = ip.user_variables(keys)
611
634
612 nt.assert_equal(keys, set(r.keys()))
635 nt.assert_equal(keys, set(r.keys()))
613 dummy = r['dummy']
636 dummy = r['dummy']
614 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
637 nt.assert_equal(set(['status', 'data', 'metadata']), set(dummy.keys()))
615 nt.assert_equal(dummy['status'], 'ok')
638 nt.assert_equal(dummy['status'], 'ok')
616 data = dummy['data']
639 data = dummy['data']
617 metadata = dummy['metadata']
640 metadata = dummy['metadata']
618 nt.assert_equal(data.get('text/html'), d._repr_html_())
641 nt.assert_equal(data.get('text/html'), d._repr_html_())
619 js, jsmd = d._repr_javascript_()
642 js, jsmd = d._repr_javascript_()
620 nt.assert_equal(data.get('application/javascript'), js)
643 nt.assert_equal(data.get('application/javascript'), js)
621 nt.assert_equal(metadata.get('application/javascript'), jsmd)
644 nt.assert_equal(metadata.get('application/javascript'), jsmd)
622
645
623 dne = r['doesnotexist']
646 dne = r['doesnotexist']
624 nt.assert_equal(dne['status'], 'error')
647 nt.assert_equal(dne['status'], 'error')
625 nt.assert_equal(dne['ename'], 'KeyError')
648 nt.assert_equal(dne['ename'], 'KeyError')
626
649
627 # back to text only
650 # back to text only
628 ip.display_formatter.active_types = ['text/plain']
651 ip.display_formatter.active_types = ['text/plain']
629
652
630 def test_user_expression():
653 def test_user_expression():
631 # enable all formatters
654 # enable all formatters
632 ip.display_formatter.active_types = ip.display_formatter.format_types
655 ip.display_formatter.active_types = ip.display_formatter.format_types
633 query = {
656 query = {
634 'a' : '1 + 2',
657 'a' : '1 + 2',
635 'b' : '1/0',
658 'b' : '1/0',
636 }
659 }
637 r = ip.user_expressions(query)
660 r = ip.user_expressions(query)
638 import pprint
661 import pprint
639 pprint.pprint(r)
662 pprint.pprint(r)
640 nt.assert_equal(r.keys(), query.keys())
663 nt.assert_equal(r.keys(), query.keys())
641 a = r['a']
664 a = r['a']
642 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
665 nt.assert_equal(set(['status', 'data', 'metadata']), set(a.keys()))
643 nt.assert_equal(a['status'], 'ok')
666 nt.assert_equal(a['status'], 'ok')
644 data = a['data']
667 data = a['data']
645 metadata = a['metadata']
668 metadata = a['metadata']
646 nt.assert_equal(data.get('text/plain'), '3')
669 nt.assert_equal(data.get('text/plain'), '3')
647
670
648 b = r['b']
671 b = r['b']
649 nt.assert_equal(b['status'], 'error')
672 nt.assert_equal(b['status'], 'error')
650 nt.assert_equal(b['ename'], 'ZeroDivisionError')
673 nt.assert_equal(b['ename'], 'ZeroDivisionError')
651
674
652 # back to text only
675 # back to text only
653 ip.display_formatter.active_types = ['text/plain']
676 ip.display_formatter.active_types = ['text/plain']
654
677
655
678
656
679
657
680
658
681
659
682
@@ -1,204 +1,204 b''
1 """Posix-specific implementation of process utilities.
1 """Posix-specific implementation of process utilities.
2
2
3 This file is only meant to be imported by process.py, not by end-users.
3 This file is only meant to be imported by process.py, not by end-users.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2010-2011 The IPython Development Team
7 # Copyright (C) 2010-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # Stdlib
18 # Stdlib
19 import subprocess as sp
19 import subprocess as sp
20 import sys
20 import sys
21
21
22 from IPython.external import pexpect
22 from IPython.external import pexpect
23
23
24 # Our own
24 # Our own
25 from ._process_common import getoutput, arg_split
25 from ._process_common import getoutput, arg_split
26 from IPython.utils import py3compat
26 from IPython.utils import py3compat
27 from IPython.utils.encoding import DEFAULT_ENCODING
27 from IPython.utils.encoding import DEFAULT_ENCODING
28
28
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30 # Function definitions
30 # Function definitions
31 #-----------------------------------------------------------------------------
31 #-----------------------------------------------------------------------------
32
32
33 def _find_cmd(cmd):
33 def _find_cmd(cmd):
34 """Find the full path to a command using which."""
34 """Find the full path to a command using which."""
35
35
36 path = sp.Popen(['/usr/bin/env', 'which', cmd],
36 path = sp.Popen(['/usr/bin/env', 'which', cmd],
37 stdout=sp.PIPE, stderr=sp.PIPE).communicate()[0]
37 stdout=sp.PIPE, stderr=sp.PIPE).communicate()[0]
38 return py3compat.bytes_to_str(path)
38 return py3compat.bytes_to_str(path)
39
39
40
40
41 class ProcessHandler(object):
41 class ProcessHandler(object):
42 """Execute subprocesses under the control of pexpect.
42 """Execute subprocesses under the control of pexpect.
43 """
43 """
44 # Timeout in seconds to wait on each reading of the subprocess' output.
44 # Timeout in seconds to wait on each reading of the subprocess' output.
45 # This should not be set too low to avoid cpu overusage from our side,
45 # This should not be set too low to avoid cpu overusage from our side,
46 # since we read in a loop whose period is controlled by this timeout.
46 # since we read in a loop whose period is controlled by this timeout.
47 read_timeout = 0.05
47 read_timeout = 0.05
48
48
49 # Timeout to give a process if we receive SIGINT, between sending the
49 # Timeout to give a process if we receive SIGINT, between sending the
50 # SIGINT to the process and forcefully terminating it.
50 # SIGINT to the process and forcefully terminating it.
51 terminate_timeout = 0.2
51 terminate_timeout = 0.2
52
52
53 # File object where stdout and stderr of the subprocess will be written
53 # File object where stdout and stderr of the subprocess will be written
54 logfile = None
54 logfile = None
55
55
56 # Shell to call for subprocesses to execute
56 # Shell to call for subprocesses to execute
57 _sh = None
57 _sh = None
58
58
59 @property
59 @property
60 def sh(self):
60 def sh(self):
61 if self._sh is None:
61 if self._sh is None:
62 self._sh = pexpect.which('sh')
62 self._sh = pexpect.which('sh')
63 if self._sh is None:
63 if self._sh is None:
64 raise OSError('"sh" shell not found')
64 raise OSError('"sh" shell not found')
65
65
66 return self._sh
66 return self._sh
67
67
68 def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None):
68 def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None):
69 """Arguments are used for pexpect calls."""
69 """Arguments are used for pexpect calls."""
70 self.read_timeout = (ProcessHandler.read_timeout if read_timeout is
70 self.read_timeout = (ProcessHandler.read_timeout if read_timeout is
71 None else read_timeout)
71 None else read_timeout)
72 self.terminate_timeout = (ProcessHandler.terminate_timeout if
72 self.terminate_timeout = (ProcessHandler.terminate_timeout if
73 terminate_timeout is None else
73 terminate_timeout is None else
74 terminate_timeout)
74 terminate_timeout)
75 self.logfile = sys.stdout if logfile is None else logfile
75 self.logfile = sys.stdout if logfile is None else logfile
76
76
77 def getoutput(self, cmd):
77 def getoutput(self, cmd):
78 """Run a command and return its stdout/stderr as a string.
78 """Run a command and return its stdout/stderr as a string.
79
79
80 Parameters
80 Parameters
81 ----------
81 ----------
82 cmd : str
82 cmd : str
83 A command to be executed in the system shell.
83 A command to be executed in the system shell.
84
84
85 Returns
85 Returns
86 -------
86 -------
87 output : str
87 output : str
88 A string containing the combination of stdout and stderr from the
88 A string containing the combination of stdout and stderr from the
89 subprocess, in whatever order the subprocess originally wrote to its
89 subprocess, in whatever order the subprocess originally wrote to its
90 file descriptors (so the order of the information in this string is the
90 file descriptors (so the order of the information in this string is the
91 correct order as would be seen if running the command in a terminal).
91 correct order as would be seen if running the command in a terminal).
92 """
92 """
93 try:
93 try:
94 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
94 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
95 except KeyboardInterrupt:
95 except KeyboardInterrupt:
96 print('^C', file=sys.stderr, end='')
96 print('^C', file=sys.stderr, end='')
97
97
98 def getoutput_pexpect(self, cmd):
98 def getoutput_pexpect(self, cmd):
99 """Run a command and return its stdout/stderr as a string.
99 """Run a command and return its stdout/stderr as a string.
100
100
101 Parameters
101 Parameters
102 ----------
102 ----------
103 cmd : str
103 cmd : str
104 A command to be executed in the system shell.
104 A command to be executed in the system shell.
105
105
106 Returns
106 Returns
107 -------
107 -------
108 output : str
108 output : str
109 A string containing the combination of stdout and stderr from the
109 A string containing the combination of stdout and stderr from the
110 subprocess, in whatever order the subprocess originally wrote to its
110 subprocess, in whatever order the subprocess originally wrote to its
111 file descriptors (so the order of the information in this string is the
111 file descriptors (so the order of the information in this string is the
112 correct order as would be seen if running the command in a terminal).
112 correct order as would be seen if running the command in a terminal).
113 """
113 """
114 try:
114 try:
115 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
115 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
116 except KeyboardInterrupt:
116 except KeyboardInterrupt:
117 print('^C', file=sys.stderr, end='')
117 print('^C', file=sys.stderr, end='')
118
118
119 def system(self, cmd):
119 def system(self, cmd):
120 """Execute a command in a subshell.
120 """Execute a command in a subshell.
121
121
122 Parameters
122 Parameters
123 ----------
123 ----------
124 cmd : str
124 cmd : str
125 A command to be executed in the system shell.
125 A command to be executed in the system shell.
126
126
127 Returns
127 Returns
128 -------
128 -------
129 int : child's exitstatus
129 int : child's exitstatus
130 """
130 """
131 # Get likely encoding for the output.
131 # Get likely encoding for the output.
132 enc = DEFAULT_ENCODING
132 enc = DEFAULT_ENCODING
133
133
134 # Patterns to match on the output, for pexpect. We read input and
134 # Patterns to match on the output, for pexpect. We read input and
135 # allow either a short timeout or EOF
135 # allow either a short timeout or EOF
136 patterns = [pexpect.TIMEOUT, pexpect.EOF]
136 patterns = [pexpect.TIMEOUT, pexpect.EOF]
137 # the index of the EOF pattern in the list.
137 # the index of the EOF pattern in the list.
138 # even though we know it's 1, this call means we don't have to worry if
138 # even though we know it's 1, this call means we don't have to worry if
139 # we change the above list, and forget to change this value:
139 # we change the above list, and forget to change this value:
140 EOF_index = patterns.index(pexpect.EOF)
140 EOF_index = patterns.index(pexpect.EOF)
141 # The size of the output stored so far in the process output buffer.
141 # The size of the output stored so far in the process output buffer.
142 # Since pexpect only appends to this buffer, each time we print we
142 # Since pexpect only appends to this buffer, each time we print we
143 # record how far we've printed, so that next time we only print *new*
143 # record how far we've printed, so that next time we only print *new*
144 # content from the buffer.
144 # content from the buffer.
145 out_size = 0
145 out_size = 0
146 try:
146 try:
147 # Since we're not really searching the buffer for text patterns, we
147 # Since we're not really searching the buffer for text patterns, we
148 # can set pexpect's search window to be tiny and it won't matter.
148 # can set pexpect's search window to be tiny and it won't matter.
149 # We only search for the 'patterns' timeout or EOF, which aren't in
149 # We only search for the 'patterns' timeout or EOF, which aren't in
150 # the text itself.
150 # the text itself.
151 #child = pexpect.spawn(pcmd, searchwindowsize=1)
151 #child = pexpect.spawn(pcmd, searchwindowsize=1)
152 if hasattr(pexpect, 'spawnb'):
152 if hasattr(pexpect, 'spawnb'):
153 child = pexpect.spawnb(self.sh, args=['-c', cmd]) # Pexpect-U
153 child = pexpect.spawnb(self.sh, args=['-c', cmd]) # Pexpect-U
154 else:
154 else:
155 child = pexpect.spawn(self.sh, args=['-c', cmd]) # Vanilla Pexpect
155 child = pexpect.spawn(self.sh, args=['-c', cmd]) # Vanilla Pexpect
156 flush = sys.stdout.flush
156 flush = sys.stdout.flush
157 while True:
157 while True:
158 # res is the index of the pattern that caused the match, so we
158 # res is the index of the pattern that caused the match, so we
159 # know whether we've finished (if we matched EOF) or not
159 # know whether we've finished (if we matched EOF) or not
160 res_idx = child.expect_list(patterns, self.read_timeout)
160 res_idx = child.expect_list(patterns, self.read_timeout)
161 print(child.before[out_size:].decode(enc, 'replace'), end='')
161 print(child.before[out_size:].decode(enc, 'replace'), end='')
162 flush()
162 flush()
163 if res_idx==EOF_index:
163 if res_idx==EOF_index:
164 break
164 break
165 # Update the pointer to what we've already printed
165 # Update the pointer to what we've already printed
166 out_size = len(child.before)
166 out_size = len(child.before)
167 except KeyboardInterrupt:
167 except KeyboardInterrupt:
168 # We need to send ^C to the process. The ascii code for '^C' is 3
168 # We need to send ^C to the process. The ascii code for '^C' is 3
169 # (the character is known as ETX for 'End of Text', see
169 # (the character is known as ETX for 'End of Text', see
170 # curses.ascii.ETX).
170 # curses.ascii.ETX).
171 child.sendline(chr(3))
171 child.sendline(chr(3))
172 # Read and print any more output the program might produce on its
172 # Read and print any more output the program might produce on its
173 # way out.
173 # way out.
174 try:
174 try:
175 out_size = len(child.before)
175 out_size = len(child.before)
176 child.expect_list(patterns, self.terminate_timeout)
176 child.expect_list(patterns, self.terminate_timeout)
177 print(child.before[out_size:].decode(enc, 'replace'), end='')
177 print(child.before[out_size:].decode(enc, 'replace'), end='')
178 sys.stdout.flush()
178 sys.stdout.flush()
179 except KeyboardInterrupt:
179 except KeyboardInterrupt:
180 # Impatient users tend to type it multiple times
180 # Impatient users tend to type it multiple times
181 pass
181 pass
182 finally:
182 finally:
183 # Ensure the subprocess really is terminated
183 # Ensure the subprocess really is terminated
184 child.terminate(force=True)
184 child.terminate(force=True)
185 # add isalive check, to ensure exitstatus is set:
185 # add isalive check, to ensure exitstatus is set:
186 child.isalive()
186 child.isalive()
187
187
188 # We follow the subprocess pattern, returning either the exit status
188 # We follow the subprocess pattern, returning either the exit status
189 # as a positive number, or the terminating signal as a negative
189 # as a positive number, or the terminating signal as a negative
190 # number
190 # number. sh returns 128+n for signals
191 if child.signalstatus is not None:
191 if child.exitstatus > 128:
192 return -child.signalstatus
192 return -(child.exitstatus - 128)
193 return child.exitstatus
193 return child.exitstatus
194
194
195
195
196 # Make system() with a functional interface for outside use. Note that we use
196 # Make system() with a functional interface for outside use. Note that we use
197 # getoutput() from the _common utils, which is built on top of popen(). Using
197 # getoutput() from the _common utils, which is built on top of popen(). Using
198 # pexpect to get subprocess output produces difficult to parse output, since
198 # pexpect to get subprocess output produces difficult to parse output, since
199 # programs think they are talking to a tty and produce highly formatted output
199 # programs think they are talking to a tty and produce highly formatted output
200 # (ls is a good example) that makes them hard.
200 # (ls is a good example) that makes them hard.
201 system = ProcessHandler().system
201 system = ProcessHandler().system
202
202
203
203
204
204
General Comments 0
You need to be logged in to leave comments. Login now