##// END OF EJS Templates
avoid deprecated asyncio.get_event_loop...
Min RK -
Show More
@@ -1,92 +1,103 b''
1 """
1 """
2 Async helper function that are invalid syntax on Python 3.5 and below.
2 Async helper function that are invalid syntax on Python 3.5 and below.
3
3
4 This code is best effort, and may have edge cases not behaving as expected. In
4 This code is best effort, and may have edge cases not behaving as expected. In
5 particular it contain a number of heuristics to detect whether code is
5 particular it contain a number of heuristics to detect whether code is
6 effectively async and need to run in an event loop or not.
6 effectively async and need to run in an event loop or not.
7
7
8 Some constructs (like top-level `return`, or `yield`) are taken care of
8 Some constructs (like top-level `return`, or `yield`) are taken care of
9 explicitly to actually raise a SyntaxError and stay as close as possible to
9 explicitly to actually raise a SyntaxError and stay as close as possible to
10 Python semantics.
10 Python semantics.
11 """
11 """
12
12
13
13
14 import ast
14 import ast
15 import asyncio
15 import inspect
16 import inspect
16
17
17
18
18 class _AsyncIORunner:
19 class _AsyncIORunner:
20 def __init__(self):
21 self._loop = None
22
23 @property
24 def loop(self):
25 """Always returns a non-closed event loop"""
26 if self._loop is None or self._loop.is_closed():
27 policy = asyncio.get_event_loop_policy()
28 self._loop = policy.new_event_loop()
29 policy.set_event_loop(self._loop)
30 return self._loop
31
19 def __call__(self, coro):
32 def __call__(self, coro):
20 """
33 """
21 Handler for asyncio autoawait
34 Handler for asyncio autoawait
22 """
35 """
23 import asyncio
36 return self.loop.run_until_complete(coro)
24
25 return asyncio.get_event_loop_policy().get_event_loop().run_until_complete(coro)
26
37
27 def __str__(self):
38 def __str__(self):
28 return "asyncio"
39 return "asyncio"
29
40
30
41
31 _asyncio_runner = _AsyncIORunner()
42 _asyncio_runner = _AsyncIORunner()
32
43
33
44
34 def _curio_runner(coroutine):
45 def _curio_runner(coroutine):
35 """
46 """
36 handler for curio autoawait
47 handler for curio autoawait
37 """
48 """
38 import curio
49 import curio
39
50
40 return curio.run(coroutine)
51 return curio.run(coroutine)
41
52
42
53
43 def _trio_runner(async_fn):
54 def _trio_runner(async_fn):
44 import trio
55 import trio
45
56
46 async def loc(coro):
57 async def loc(coro):
47 """
58 """
48 We need the dummy no-op async def to protect from
59 We need the dummy no-op async def to protect from
49 trio's internal. See https://github.com/python-trio/trio/issues/89
60 trio's internal. See https://github.com/python-trio/trio/issues/89
50 """
61 """
51 return await coro
62 return await coro
52
63
53 return trio.run(loc, async_fn)
64 return trio.run(loc, async_fn)
54
65
55
66
56 def _pseudo_sync_runner(coro):
67 def _pseudo_sync_runner(coro):
57 """
68 """
58 A runner that does not really allow async execution, and just advance the coroutine.
69 A runner that does not really allow async execution, and just advance the coroutine.
59
70
60 See discussion in https://github.com/python-trio/trio/issues/608,
71 See discussion in https://github.com/python-trio/trio/issues/608,
61
72
62 Credit to Nathaniel Smith
73 Credit to Nathaniel Smith
63 """
74 """
64 try:
75 try:
65 coro.send(None)
76 coro.send(None)
66 except StopIteration as exc:
77 except StopIteration as exc:
67 return exc.value
78 return exc.value
68 else:
79 else:
69 # TODO: do not raise but return an execution result with the right info.
80 # TODO: do not raise but return an execution result with the right info.
70 raise RuntimeError(
81 raise RuntimeError(
71 "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__)
82 "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__)
72 )
83 )
73
84
74
85
75 def _should_be_async(cell: str) -> bool:
86 def _should_be_async(cell: str) -> bool:
76 """Detect if a block of code need to be wrapped in an `async def`
87 """Detect if a block of code need to be wrapped in an `async def`
77
88
78 Attempt to parse the block of code, it it compile we're fine.
89 Attempt to parse the block of code, it it compile we're fine.
79 Otherwise we wrap if and try to compile.
90 Otherwise we wrap if and try to compile.
80
91
81 If it works, assume it should be async. Otherwise Return False.
92 If it works, assume it should be async. Otherwise Return False.
82
93
83 Not handled yet: If the block of code has a return statement as the top
94 Not handled yet: If the block of code has a return statement as the top
84 level, it will be seen as async. This is a know limitation.
95 level, it will be seen as async. This is a know limitation.
85 """
96 """
86 try:
97 try:
87 code = compile(
98 code = compile(
88 cell, "<>", "exec", flags=getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0)
99 cell, "<>", "exec", flags=getattr(ast, "PyCF_ALLOW_TOP_LEVEL_AWAIT", 0x0)
89 )
100 )
90 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
101 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
91 except (SyntaxError, MemoryError):
102 except (SyntaxError, MemoryError):
92 return False
103 return False
@@ -1,1082 +1,1098 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
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 asyncio
12 import asyncio
13 import ast
13 import ast
14 import os
14 import os
15 import signal
15 import signal
16 import shutil
16 import shutil
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19 import unittest
19 import unittest
20 from unittest import mock
20 from unittest import mock
21
21
22 from os.path import join
22 from os.path import join
23
23
24 from IPython.core.error import InputRejected
24 from IPython.core.error import InputRejected
25 from IPython.core.inputtransformer import InputTransformer
25 from IPython.core.inputtransformer import InputTransformer
26 from IPython.core import interactiveshell
26 from IPython.core import interactiveshell
27 from IPython.testing.decorators import (
27 from IPython.testing.decorators import (
28 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
28 skipif, skip_win32, onlyif_unicode_paths, onlyif_cmds_exist,
29 )
29 )
30 from IPython.testing import tools as tt
30 from IPython.testing import tools as tt
31 from IPython.utils.process import find_cmd
31 from IPython.utils.process import find_cmd
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Globals
34 # Globals
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # This is used by every single test, no point repeating it ad nauseam
36 # This is used by every single test, no point repeating it ad nauseam
37
37
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39 # Tests
39 # Tests
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41
41
42 class DerivedInterrupt(KeyboardInterrupt):
42 class DerivedInterrupt(KeyboardInterrupt):
43 pass
43 pass
44
44
45 class InteractiveShellTestCase(unittest.TestCase):
45 class InteractiveShellTestCase(unittest.TestCase):
46 def test_naked_string_cells(self):
46 def test_naked_string_cells(self):
47 """Test that cells with only naked strings are fully executed"""
47 """Test that cells with only naked strings are fully executed"""
48 # First, single-line inputs
48 # First, single-line inputs
49 ip.run_cell('"a"\n')
49 ip.run_cell('"a"\n')
50 self.assertEqual(ip.user_ns['_'], 'a')
50 self.assertEqual(ip.user_ns['_'], 'a')
51 # And also multi-line cells
51 # And also multi-line cells
52 ip.run_cell('"""a\nb"""\n')
52 ip.run_cell('"""a\nb"""\n')
53 self.assertEqual(ip.user_ns['_'], 'a\nb')
53 self.assertEqual(ip.user_ns['_'], 'a\nb')
54
54
55 def test_run_empty_cell(self):
55 def test_run_empty_cell(self):
56 """Just make sure we don't get a horrible error with a blank
56 """Just make sure we don't get a horrible error with a blank
57 cell of input. Yes, I did overlook that."""
57 cell of input. Yes, I did overlook that."""
58 old_xc = ip.execution_count
58 old_xc = ip.execution_count
59 res = ip.run_cell('')
59 res = ip.run_cell('')
60 self.assertEqual(ip.execution_count, old_xc)
60 self.assertEqual(ip.execution_count, old_xc)
61 self.assertEqual(res.execution_count, None)
61 self.assertEqual(res.execution_count, None)
62
62
63 def test_run_cell_multiline(self):
63 def test_run_cell_multiline(self):
64 """Multi-block, multi-line cells must execute correctly.
64 """Multi-block, multi-line cells must execute correctly.
65 """
65 """
66 src = '\n'.join(["x=1",
66 src = '\n'.join(["x=1",
67 "y=2",
67 "y=2",
68 "if 1:",
68 "if 1:",
69 " x += 1",
69 " x += 1",
70 " y += 1",])
70 " y += 1",])
71 res = ip.run_cell(src)
71 res = ip.run_cell(src)
72 self.assertEqual(ip.user_ns['x'], 2)
72 self.assertEqual(ip.user_ns['x'], 2)
73 self.assertEqual(ip.user_ns['y'], 3)
73 self.assertEqual(ip.user_ns['y'], 3)
74 self.assertEqual(res.success, True)
74 self.assertEqual(res.success, True)
75 self.assertEqual(res.result, None)
75 self.assertEqual(res.result, None)
76
76
77 def test_multiline_string_cells(self):
77 def test_multiline_string_cells(self):
78 "Code sprinkled with multiline strings should execute (GH-306)"
78 "Code sprinkled with multiline strings should execute (GH-306)"
79 ip.run_cell('tmp=0')
79 ip.run_cell('tmp=0')
80 self.assertEqual(ip.user_ns['tmp'], 0)
80 self.assertEqual(ip.user_ns['tmp'], 0)
81 res = ip.run_cell('tmp=1;"""a\nb"""\n')
81 res = ip.run_cell('tmp=1;"""a\nb"""\n')
82 self.assertEqual(ip.user_ns['tmp'], 1)
82 self.assertEqual(ip.user_ns['tmp'], 1)
83 self.assertEqual(res.success, True)
83 self.assertEqual(res.success, True)
84 self.assertEqual(res.result, "a\nb")
84 self.assertEqual(res.result, "a\nb")
85
85
86 def test_dont_cache_with_semicolon(self):
86 def test_dont_cache_with_semicolon(self):
87 "Ending a line with semicolon should not cache the returned object (GH-307)"
87 "Ending a line with semicolon should not cache the returned object (GH-307)"
88 oldlen = len(ip.user_ns['Out'])
88 oldlen = len(ip.user_ns['Out'])
89 for cell in ['1;', '1;1;']:
89 for cell in ['1;', '1;1;']:
90 res = ip.run_cell(cell, store_history=True)
90 res = ip.run_cell(cell, store_history=True)
91 newlen = len(ip.user_ns['Out'])
91 newlen = len(ip.user_ns['Out'])
92 self.assertEqual(oldlen, newlen)
92 self.assertEqual(oldlen, newlen)
93 self.assertIsNone(res.result)
93 self.assertIsNone(res.result)
94 i = 0
94 i = 0
95 #also test the default caching behavior
95 #also test the default caching behavior
96 for cell in ['1', '1;1']:
96 for cell in ['1', '1;1']:
97 ip.run_cell(cell, store_history=True)
97 ip.run_cell(cell, store_history=True)
98 newlen = len(ip.user_ns['Out'])
98 newlen = len(ip.user_ns['Out'])
99 i += 1
99 i += 1
100 self.assertEqual(oldlen+i, newlen)
100 self.assertEqual(oldlen+i, newlen)
101
101
102 def test_syntax_error(self):
102 def test_syntax_error(self):
103 res = ip.run_cell("raise = 3")
103 res = ip.run_cell("raise = 3")
104 self.assertIsInstance(res.error_before_exec, SyntaxError)
104 self.assertIsInstance(res.error_before_exec, SyntaxError)
105
105
106 def test_In_variable(self):
106 def test_In_variable(self):
107 "Verify that In variable grows with user input (GH-284)"
107 "Verify that In variable grows with user input (GH-284)"
108 oldlen = len(ip.user_ns['In'])
108 oldlen = len(ip.user_ns['In'])
109 ip.run_cell('1;', store_history=True)
109 ip.run_cell('1;', store_history=True)
110 newlen = len(ip.user_ns['In'])
110 newlen = len(ip.user_ns['In'])
111 self.assertEqual(oldlen+1, newlen)
111 self.assertEqual(oldlen+1, newlen)
112 self.assertEqual(ip.user_ns['In'][-1],'1;')
112 self.assertEqual(ip.user_ns['In'][-1],'1;')
113
113
114 def test_magic_names_in_string(self):
114 def test_magic_names_in_string(self):
115 ip.run_cell('a = """\n%exit\n"""')
115 ip.run_cell('a = """\n%exit\n"""')
116 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
116 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
117
117
118 def test_trailing_newline(self):
118 def test_trailing_newline(self):
119 """test that running !(command) does not raise a SyntaxError"""
119 """test that running !(command) does not raise a SyntaxError"""
120 ip.run_cell('!(true)\n', False)
120 ip.run_cell('!(true)\n', False)
121 ip.run_cell('!(true)\n\n\n', False)
121 ip.run_cell('!(true)\n\n\n', False)
122
122
123 def test_gh_597(self):
123 def test_gh_597(self):
124 """Pretty-printing lists of objects with non-ascii reprs may cause
124 """Pretty-printing lists of objects with non-ascii reprs may cause
125 problems."""
125 problems."""
126 class Spam(object):
126 class Spam(object):
127 def __repr__(self):
127 def __repr__(self):
128 return "\xe9"*50
128 return "\xe9"*50
129 import IPython.core.formatters
129 import IPython.core.formatters
130 f = IPython.core.formatters.PlainTextFormatter()
130 f = IPython.core.formatters.PlainTextFormatter()
131 f([Spam(),Spam()])
131 f([Spam(),Spam()])
132
132
133
133
134 def test_future_flags(self):
134 def test_future_flags(self):
135 """Check that future flags are used for parsing code (gh-777)"""
135 """Check that future flags are used for parsing code (gh-777)"""
136 ip.run_cell('from __future__ import barry_as_FLUFL')
136 ip.run_cell('from __future__ import barry_as_FLUFL')
137 try:
137 try:
138 ip.run_cell('prfunc_return_val = 1 <> 2')
138 ip.run_cell('prfunc_return_val = 1 <> 2')
139 assert 'prfunc_return_val' in ip.user_ns
139 assert 'prfunc_return_val' in ip.user_ns
140 finally:
140 finally:
141 # Reset compiler flags so we don't mess up other tests.
141 # Reset compiler flags so we don't mess up other tests.
142 ip.compile.reset_compiler_flags()
142 ip.compile.reset_compiler_flags()
143
143
144 def test_can_pickle(self):
144 def test_can_pickle(self):
145 "Can we pickle objects defined interactively (GH-29)"
145 "Can we pickle objects defined interactively (GH-29)"
146 ip = get_ipython()
146 ip = get_ipython()
147 ip.reset()
147 ip.reset()
148 ip.run_cell(("class Mylist(list):\n"
148 ip.run_cell(("class Mylist(list):\n"
149 " def __init__(self,x=[]):\n"
149 " def __init__(self,x=[]):\n"
150 " list.__init__(self,x)"))
150 " list.__init__(self,x)"))
151 ip.run_cell("w=Mylist([1,2,3])")
151 ip.run_cell("w=Mylist([1,2,3])")
152
152
153 from pickle import dumps
153 from pickle import dumps
154
154
155 # We need to swap in our main module - this is only necessary
155 # We need to swap in our main module - this is only necessary
156 # inside the test framework, because IPython puts the interactive module
156 # inside the test framework, because IPython puts the interactive module
157 # in place (but the test framework undoes this).
157 # in place (but the test framework undoes this).
158 _main = sys.modules['__main__']
158 _main = sys.modules['__main__']
159 sys.modules['__main__'] = ip.user_module
159 sys.modules['__main__'] = ip.user_module
160 try:
160 try:
161 res = dumps(ip.user_ns["w"])
161 res = dumps(ip.user_ns["w"])
162 finally:
162 finally:
163 sys.modules['__main__'] = _main
163 sys.modules['__main__'] = _main
164 self.assertTrue(isinstance(res, bytes))
164 self.assertTrue(isinstance(res, bytes))
165
165
166 def test_global_ns(self):
166 def test_global_ns(self):
167 "Code in functions must be able to access variables outside them."
167 "Code in functions must be able to access variables outside them."
168 ip = get_ipython()
168 ip = get_ipython()
169 ip.run_cell("a = 10")
169 ip.run_cell("a = 10")
170 ip.run_cell(("def f(x):\n"
170 ip.run_cell(("def f(x):\n"
171 " return x + a"))
171 " return x + a"))
172 ip.run_cell("b = f(12)")
172 ip.run_cell("b = f(12)")
173 self.assertEqual(ip.user_ns["b"], 22)
173 self.assertEqual(ip.user_ns["b"], 22)
174
174
175 def test_bad_custom_tb(self):
175 def test_bad_custom_tb(self):
176 """Check that InteractiveShell is protected from bad custom exception handlers"""
176 """Check that InteractiveShell is protected from bad custom exception handlers"""
177 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
177 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
178 self.assertEqual(ip.custom_exceptions, (IOError,))
178 self.assertEqual(ip.custom_exceptions, (IOError,))
179 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
179 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
180 ip.run_cell(u'raise IOError("foo")')
180 ip.run_cell(u'raise IOError("foo")')
181 self.assertEqual(ip.custom_exceptions, ())
181 self.assertEqual(ip.custom_exceptions, ())
182
182
183 def test_bad_custom_tb_return(self):
183 def test_bad_custom_tb_return(self):
184 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
184 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
185 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
185 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
186 self.assertEqual(ip.custom_exceptions, (NameError,))
186 self.assertEqual(ip.custom_exceptions, (NameError,))
187 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
187 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
188 ip.run_cell(u'a=abracadabra')
188 ip.run_cell(u'a=abracadabra')
189 self.assertEqual(ip.custom_exceptions, ())
189 self.assertEqual(ip.custom_exceptions, ())
190
190
191 def test_drop_by_id(self):
191 def test_drop_by_id(self):
192 myvars = {"a":object(), "b":object(), "c": object()}
192 myvars = {"a":object(), "b":object(), "c": object()}
193 ip.push(myvars, interactive=False)
193 ip.push(myvars, interactive=False)
194 for name in myvars:
194 for name in myvars:
195 assert name in ip.user_ns, name
195 assert name in ip.user_ns, name
196 assert name in ip.user_ns_hidden, name
196 assert name in ip.user_ns_hidden, name
197 ip.user_ns['b'] = 12
197 ip.user_ns['b'] = 12
198 ip.drop_by_id(myvars)
198 ip.drop_by_id(myvars)
199 for name in ["a", "c"]:
199 for name in ["a", "c"]:
200 assert name not in ip.user_ns, name
200 assert name not in ip.user_ns, name
201 assert name not in ip.user_ns_hidden, name
201 assert name not in ip.user_ns_hidden, name
202 assert ip.user_ns['b'] == 12
202 assert ip.user_ns['b'] == 12
203 ip.reset()
203 ip.reset()
204
204
205 def test_var_expand(self):
205 def test_var_expand(self):
206 ip.user_ns['f'] = u'Ca\xf1o'
206 ip.user_ns['f'] = u'Ca\xf1o'
207 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
207 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
208 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
208 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
209 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
209 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
210 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
210 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
211
211
212 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
212 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
213
213
214 ip.user_ns['f'] = b'Ca\xc3\xb1o'
214 ip.user_ns['f'] = b'Ca\xc3\xb1o'
215 # This should not raise any exception:
215 # This should not raise any exception:
216 ip.var_expand(u'echo $f')
216 ip.var_expand(u'echo $f')
217
217
218 def test_var_expand_local(self):
218 def test_var_expand_local(self):
219 """Test local variable expansion in !system and %magic calls"""
219 """Test local variable expansion in !system and %magic calls"""
220 # !system
220 # !system
221 ip.run_cell(
221 ip.run_cell(
222 "def test():\n"
222 "def test():\n"
223 ' lvar = "ttt"\n'
223 ' lvar = "ttt"\n'
224 " ret = !echo {lvar}\n"
224 " ret = !echo {lvar}\n"
225 " return ret[0]\n"
225 " return ret[0]\n"
226 )
226 )
227 res = ip.user_ns["test"]()
227 res = ip.user_ns["test"]()
228 self.assertIn("ttt", res)
228 self.assertIn("ttt", res)
229
229
230 # %magic
230 # %magic
231 ip.run_cell(
231 ip.run_cell(
232 "def makemacro():\n"
232 "def makemacro():\n"
233 ' macroname = "macro_var_expand_locals"\n'
233 ' macroname = "macro_var_expand_locals"\n'
234 " %macro {macroname} codestr\n"
234 " %macro {macroname} codestr\n"
235 )
235 )
236 ip.user_ns["codestr"] = "str(12)"
236 ip.user_ns["codestr"] = "str(12)"
237 ip.run_cell("makemacro()")
237 ip.run_cell("makemacro()")
238 self.assertIn("macro_var_expand_locals", ip.user_ns)
238 self.assertIn("macro_var_expand_locals", ip.user_ns)
239
239
240 def test_var_expand_self(self):
240 def test_var_expand_self(self):
241 """Test variable expansion with the name 'self', which was failing.
241 """Test variable expansion with the name 'self', which was failing.
242
242
243 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
243 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
244 """
244 """
245 ip.run_cell(
245 ip.run_cell(
246 "class cTest:\n"
246 "class cTest:\n"
247 ' classvar="see me"\n'
247 ' classvar="see me"\n'
248 " def test(self):\n"
248 " def test(self):\n"
249 " res = !echo Variable: {self.classvar}\n"
249 " res = !echo Variable: {self.classvar}\n"
250 " return res[0]\n"
250 " return res[0]\n"
251 )
251 )
252 self.assertIn("see me", ip.user_ns["cTest"]().test())
252 self.assertIn("see me", ip.user_ns["cTest"]().test())
253
253
254 def test_bad_var_expand(self):
254 def test_bad_var_expand(self):
255 """var_expand on invalid formats shouldn't raise"""
255 """var_expand on invalid formats shouldn't raise"""
256 # SyntaxError
256 # SyntaxError
257 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
257 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
258 # NameError
258 # NameError
259 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
259 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
260 # ZeroDivisionError
260 # ZeroDivisionError
261 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
261 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
262
262
263 def test_silent_postexec(self):
263 def test_silent_postexec(self):
264 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
264 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
265 pre_explicit = mock.Mock()
265 pre_explicit = mock.Mock()
266 pre_always = mock.Mock()
266 pre_always = mock.Mock()
267 post_explicit = mock.Mock()
267 post_explicit = mock.Mock()
268 post_always = mock.Mock()
268 post_always = mock.Mock()
269 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
269 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
270
270
271 ip.events.register('pre_run_cell', pre_explicit)
271 ip.events.register('pre_run_cell', pre_explicit)
272 ip.events.register('pre_execute', pre_always)
272 ip.events.register('pre_execute', pre_always)
273 ip.events.register('post_run_cell', post_explicit)
273 ip.events.register('post_run_cell', post_explicit)
274 ip.events.register('post_execute', post_always)
274 ip.events.register('post_execute', post_always)
275
275
276 try:
276 try:
277 ip.run_cell("1", silent=True)
277 ip.run_cell("1", silent=True)
278 assert pre_always.called
278 assert pre_always.called
279 assert not pre_explicit.called
279 assert not pre_explicit.called
280 assert post_always.called
280 assert post_always.called
281 assert not post_explicit.called
281 assert not post_explicit.called
282 # double-check that non-silent exec did what we expected
282 # double-check that non-silent exec did what we expected
283 # silent to avoid
283 # silent to avoid
284 ip.run_cell("1")
284 ip.run_cell("1")
285 assert pre_explicit.called
285 assert pre_explicit.called
286 assert post_explicit.called
286 assert post_explicit.called
287 info, = pre_explicit.call_args[0]
287 info, = pre_explicit.call_args[0]
288 result, = post_explicit.call_args[0]
288 result, = post_explicit.call_args[0]
289 self.assertEqual(info, result.info)
289 self.assertEqual(info, result.info)
290 # check that post hooks are always called
290 # check that post hooks are always called
291 [m.reset_mock() for m in all_mocks]
291 [m.reset_mock() for m in all_mocks]
292 ip.run_cell("syntax error")
292 ip.run_cell("syntax error")
293 assert pre_always.called
293 assert pre_always.called
294 assert pre_explicit.called
294 assert pre_explicit.called
295 assert post_always.called
295 assert post_always.called
296 assert post_explicit.called
296 assert post_explicit.called
297 info, = pre_explicit.call_args[0]
297 info, = pre_explicit.call_args[0]
298 result, = post_explicit.call_args[0]
298 result, = post_explicit.call_args[0]
299 self.assertEqual(info, result.info)
299 self.assertEqual(info, result.info)
300 finally:
300 finally:
301 # remove post-exec
301 # remove post-exec
302 ip.events.unregister('pre_run_cell', pre_explicit)
302 ip.events.unregister('pre_run_cell', pre_explicit)
303 ip.events.unregister('pre_execute', pre_always)
303 ip.events.unregister('pre_execute', pre_always)
304 ip.events.unregister('post_run_cell', post_explicit)
304 ip.events.unregister('post_run_cell', post_explicit)
305 ip.events.unregister('post_execute', post_always)
305 ip.events.unregister('post_execute', post_always)
306
306
307 def test_silent_noadvance(self):
307 def test_silent_noadvance(self):
308 """run_cell(silent=True) doesn't advance execution_count"""
308 """run_cell(silent=True) doesn't advance execution_count"""
309 ec = ip.execution_count
309 ec = ip.execution_count
310 # silent should force store_history=False
310 # silent should force store_history=False
311 ip.run_cell("1", store_history=True, silent=True)
311 ip.run_cell("1", store_history=True, silent=True)
312
312
313 self.assertEqual(ec, ip.execution_count)
313 self.assertEqual(ec, ip.execution_count)
314 # double-check that non-silent exec did what we expected
314 # double-check that non-silent exec did what we expected
315 # silent to avoid
315 # silent to avoid
316 ip.run_cell("1", store_history=True)
316 ip.run_cell("1", store_history=True)
317 self.assertEqual(ec+1, ip.execution_count)
317 self.assertEqual(ec+1, ip.execution_count)
318
318
319 def test_silent_nodisplayhook(self):
319 def test_silent_nodisplayhook(self):
320 """run_cell(silent=True) doesn't trigger displayhook"""
320 """run_cell(silent=True) doesn't trigger displayhook"""
321 d = dict(called=False)
321 d = dict(called=False)
322
322
323 trap = ip.display_trap
323 trap = ip.display_trap
324 save_hook = trap.hook
324 save_hook = trap.hook
325
325
326 def failing_hook(*args, **kwargs):
326 def failing_hook(*args, **kwargs):
327 d['called'] = True
327 d['called'] = True
328
328
329 try:
329 try:
330 trap.hook = failing_hook
330 trap.hook = failing_hook
331 res = ip.run_cell("1", silent=True)
331 res = ip.run_cell("1", silent=True)
332 self.assertFalse(d['called'])
332 self.assertFalse(d['called'])
333 self.assertIsNone(res.result)
333 self.assertIsNone(res.result)
334 # double-check that non-silent exec did what we expected
334 # double-check that non-silent exec did what we expected
335 # silent to avoid
335 # silent to avoid
336 ip.run_cell("1")
336 ip.run_cell("1")
337 self.assertTrue(d['called'])
337 self.assertTrue(d['called'])
338 finally:
338 finally:
339 trap.hook = save_hook
339 trap.hook = save_hook
340
340
341 def test_ofind_line_magic(self):
341 def test_ofind_line_magic(self):
342 from IPython.core.magic import register_line_magic
342 from IPython.core.magic import register_line_magic
343
343
344 @register_line_magic
344 @register_line_magic
345 def lmagic(line):
345 def lmagic(line):
346 "A line magic"
346 "A line magic"
347
347
348 # Get info on line magic
348 # Get info on line magic
349 lfind = ip._ofind("lmagic")
349 lfind = ip._ofind("lmagic")
350 info = dict(
350 info = dict(
351 found=True,
351 found=True,
352 isalias=False,
352 isalias=False,
353 ismagic=True,
353 ismagic=True,
354 namespace="IPython internal",
354 namespace="IPython internal",
355 obj=lmagic,
355 obj=lmagic,
356 parent=None,
356 parent=None,
357 )
357 )
358 self.assertEqual(lfind, info)
358 self.assertEqual(lfind, info)
359
359
360 def test_ofind_cell_magic(self):
360 def test_ofind_cell_magic(self):
361 from IPython.core.magic import register_cell_magic
361 from IPython.core.magic import register_cell_magic
362
362
363 @register_cell_magic
363 @register_cell_magic
364 def cmagic(line, cell):
364 def cmagic(line, cell):
365 "A cell magic"
365 "A cell magic"
366
366
367 # Get info on cell magic
367 # Get info on cell magic
368 find = ip._ofind("cmagic")
368 find = ip._ofind("cmagic")
369 info = dict(
369 info = dict(
370 found=True,
370 found=True,
371 isalias=False,
371 isalias=False,
372 ismagic=True,
372 ismagic=True,
373 namespace="IPython internal",
373 namespace="IPython internal",
374 obj=cmagic,
374 obj=cmagic,
375 parent=None,
375 parent=None,
376 )
376 )
377 self.assertEqual(find, info)
377 self.assertEqual(find, info)
378
378
379 def test_ofind_property_with_error(self):
379 def test_ofind_property_with_error(self):
380 class A(object):
380 class A(object):
381 @property
381 @property
382 def foo(self):
382 def foo(self):
383 raise NotImplementedError()
383 raise NotImplementedError()
384 a = A()
384 a = A()
385
385
386 found = ip._ofind('a.foo', [('locals', locals())])
386 found = ip._ofind('a.foo', [('locals', locals())])
387 info = dict(found=True, isalias=False, ismagic=False,
387 info = dict(found=True, isalias=False, ismagic=False,
388 namespace='locals', obj=A.foo, parent=a)
388 namespace='locals', obj=A.foo, parent=a)
389 self.assertEqual(found, info)
389 self.assertEqual(found, info)
390
390
391 def test_ofind_multiple_attribute_lookups(self):
391 def test_ofind_multiple_attribute_lookups(self):
392 class A(object):
392 class A(object):
393 @property
393 @property
394 def foo(self):
394 def foo(self):
395 raise NotImplementedError()
395 raise NotImplementedError()
396
396
397 a = A()
397 a = A()
398 a.a = A()
398 a.a = A()
399 a.a.a = A()
399 a.a.a = A()
400
400
401 found = ip._ofind('a.a.a.foo', [('locals', locals())])
401 found = ip._ofind('a.a.a.foo', [('locals', locals())])
402 info = dict(found=True, isalias=False, ismagic=False,
402 info = dict(found=True, isalias=False, ismagic=False,
403 namespace='locals', obj=A.foo, parent=a.a.a)
403 namespace='locals', obj=A.foo, parent=a.a.a)
404 self.assertEqual(found, info)
404 self.assertEqual(found, info)
405
405
406 def test_ofind_slotted_attributes(self):
406 def test_ofind_slotted_attributes(self):
407 class A(object):
407 class A(object):
408 __slots__ = ['foo']
408 __slots__ = ['foo']
409 def __init__(self):
409 def __init__(self):
410 self.foo = 'bar'
410 self.foo = 'bar'
411
411
412 a = A()
412 a = A()
413 found = ip._ofind('a.foo', [('locals', locals())])
413 found = ip._ofind('a.foo', [('locals', locals())])
414 info = dict(found=True, isalias=False, ismagic=False,
414 info = dict(found=True, isalias=False, ismagic=False,
415 namespace='locals', obj=a.foo, parent=a)
415 namespace='locals', obj=a.foo, parent=a)
416 self.assertEqual(found, info)
416 self.assertEqual(found, info)
417
417
418 found = ip._ofind('a.bar', [('locals', locals())])
418 found = ip._ofind('a.bar', [('locals', locals())])
419 info = dict(found=False, isalias=False, ismagic=False,
419 info = dict(found=False, isalias=False, ismagic=False,
420 namespace=None, obj=None, parent=a)
420 namespace=None, obj=None, parent=a)
421 self.assertEqual(found, info)
421 self.assertEqual(found, info)
422
422
423 def test_ofind_prefers_property_to_instance_level_attribute(self):
423 def test_ofind_prefers_property_to_instance_level_attribute(self):
424 class A(object):
424 class A(object):
425 @property
425 @property
426 def foo(self):
426 def foo(self):
427 return 'bar'
427 return 'bar'
428 a = A()
428 a = A()
429 a.__dict__["foo"] = "baz"
429 a.__dict__["foo"] = "baz"
430 self.assertEqual(a.foo, "bar")
430 self.assertEqual(a.foo, "bar")
431 found = ip._ofind("a.foo", [("locals", locals())])
431 found = ip._ofind("a.foo", [("locals", locals())])
432 self.assertIs(found["obj"], A.foo)
432 self.assertIs(found["obj"], A.foo)
433
433
434 def test_custom_syntaxerror_exception(self):
434 def test_custom_syntaxerror_exception(self):
435 called = []
435 called = []
436 def my_handler(shell, etype, value, tb, tb_offset=None):
436 def my_handler(shell, etype, value, tb, tb_offset=None):
437 called.append(etype)
437 called.append(etype)
438 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
438 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
439
439
440 ip.set_custom_exc((SyntaxError,), my_handler)
440 ip.set_custom_exc((SyntaxError,), my_handler)
441 try:
441 try:
442 ip.run_cell("1f")
442 ip.run_cell("1f")
443 # Check that this was called, and only once.
443 # Check that this was called, and only once.
444 self.assertEqual(called, [SyntaxError])
444 self.assertEqual(called, [SyntaxError])
445 finally:
445 finally:
446 # Reset the custom exception hook
446 # Reset the custom exception hook
447 ip.set_custom_exc((), None)
447 ip.set_custom_exc((), None)
448
448
449 def test_custom_exception(self):
449 def test_custom_exception(self):
450 called = []
450 called = []
451 def my_handler(shell, etype, value, tb, tb_offset=None):
451 def my_handler(shell, etype, value, tb, tb_offset=None):
452 called.append(etype)
452 called.append(etype)
453 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
453 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
454
454
455 ip.set_custom_exc((ValueError,), my_handler)
455 ip.set_custom_exc((ValueError,), my_handler)
456 try:
456 try:
457 res = ip.run_cell("raise ValueError('test')")
457 res = ip.run_cell("raise ValueError('test')")
458 # Check that this was called, and only once.
458 # Check that this was called, and only once.
459 self.assertEqual(called, [ValueError])
459 self.assertEqual(called, [ValueError])
460 # Check that the error is on the result object
460 # Check that the error is on the result object
461 self.assertIsInstance(res.error_in_exec, ValueError)
461 self.assertIsInstance(res.error_in_exec, ValueError)
462 finally:
462 finally:
463 # Reset the custom exception hook
463 # Reset the custom exception hook
464 ip.set_custom_exc((), None)
464 ip.set_custom_exc((), None)
465
465
466 @mock.patch("builtins.print")
466 @mock.patch("builtins.print")
467 def test_showtraceback_with_surrogates(self, mocked_print):
467 def test_showtraceback_with_surrogates(self, mocked_print):
468 values = []
468 values = []
469
469
470 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
470 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
471 values.append(value)
471 values.append(value)
472 if value == chr(0xD8FF):
472 if value == chr(0xD8FF):
473 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
473 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
474
474
475 # mock builtins.print
475 # mock builtins.print
476 mocked_print.side_effect = mock_print_func
476 mocked_print.side_effect = mock_print_func
477
477
478 # ip._showtraceback() is replaced in globalipapp.py.
478 # ip._showtraceback() is replaced in globalipapp.py.
479 # Call original method to test.
479 # Call original method to test.
480 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
480 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
481
481
482 self.assertEqual(mocked_print.call_count, 2)
482 self.assertEqual(mocked_print.call_count, 2)
483 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
483 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
484
484
485 def test_mktempfile(self):
485 def test_mktempfile(self):
486 filename = ip.mktempfile()
486 filename = ip.mktempfile()
487 # Check that we can open the file again on Windows
487 # Check that we can open the file again on Windows
488 with open(filename, 'w') as f:
488 with open(filename, 'w') as f:
489 f.write('abc')
489 f.write('abc')
490
490
491 filename = ip.mktempfile(data='blah')
491 filename = ip.mktempfile(data='blah')
492 with open(filename, 'r') as f:
492 with open(filename, 'r') as f:
493 self.assertEqual(f.read(), 'blah')
493 self.assertEqual(f.read(), 'blah')
494
494
495 def test_new_main_mod(self):
495 def test_new_main_mod(self):
496 # Smoketest to check that this accepts a unicode module name
496 # Smoketest to check that this accepts a unicode module name
497 name = u'jiefmw'
497 name = u'jiefmw'
498 mod = ip.new_main_mod(u'%s.py' % name, name)
498 mod = ip.new_main_mod(u'%s.py' % name, name)
499 self.assertEqual(mod.__name__, name)
499 self.assertEqual(mod.__name__, name)
500
500
501 def test_get_exception_only(self):
501 def test_get_exception_only(self):
502 try:
502 try:
503 raise KeyboardInterrupt
503 raise KeyboardInterrupt
504 except KeyboardInterrupt:
504 except KeyboardInterrupt:
505 msg = ip.get_exception_only()
505 msg = ip.get_exception_only()
506 self.assertEqual(msg, 'KeyboardInterrupt\n')
506 self.assertEqual(msg, 'KeyboardInterrupt\n')
507
507
508 try:
508 try:
509 raise DerivedInterrupt("foo")
509 raise DerivedInterrupt("foo")
510 except KeyboardInterrupt:
510 except KeyboardInterrupt:
511 msg = ip.get_exception_only()
511 msg = ip.get_exception_only()
512 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
512 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
513
513
514 def test_inspect_text(self):
514 def test_inspect_text(self):
515 ip.run_cell('a = 5')
515 ip.run_cell('a = 5')
516 text = ip.object_inspect_text('a')
516 text = ip.object_inspect_text('a')
517 self.assertIsInstance(text, str)
517 self.assertIsInstance(text, str)
518
518
519 def test_last_execution_result(self):
519 def test_last_execution_result(self):
520 """ Check that last execution result gets set correctly (GH-10702) """
520 """ Check that last execution result gets set correctly (GH-10702) """
521 result = ip.run_cell('a = 5; a')
521 result = ip.run_cell('a = 5; a')
522 self.assertTrue(ip.last_execution_succeeded)
522 self.assertTrue(ip.last_execution_succeeded)
523 self.assertEqual(ip.last_execution_result.result, 5)
523 self.assertEqual(ip.last_execution_result.result, 5)
524
524
525 result = ip.run_cell('a = x_invalid_id_x')
525 result = ip.run_cell('a = x_invalid_id_x')
526 self.assertFalse(ip.last_execution_succeeded)
526 self.assertFalse(ip.last_execution_succeeded)
527 self.assertFalse(ip.last_execution_result.success)
527 self.assertFalse(ip.last_execution_result.success)
528 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
528 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
529
529
530 def test_reset_aliasing(self):
530 def test_reset_aliasing(self):
531 """ Check that standard posix aliases work after %reset. """
531 """ Check that standard posix aliases work after %reset. """
532 if os.name != 'posix':
532 if os.name != 'posix':
533 return
533 return
534
534
535 ip.reset()
535 ip.reset()
536 for cmd in ('clear', 'more', 'less', 'man'):
536 for cmd in ('clear', 'more', 'less', 'man'):
537 res = ip.run_cell('%' + cmd)
537 res = ip.run_cell('%' + cmd)
538 self.assertEqual(res.success, True)
538 self.assertEqual(res.success, True)
539
539
540
540
541 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
541 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
542
542
543 @onlyif_unicode_paths
543 @onlyif_unicode_paths
544 def setUp(self):
544 def setUp(self):
545 self.BASETESTDIR = tempfile.mkdtemp()
545 self.BASETESTDIR = tempfile.mkdtemp()
546 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
546 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
547 os.mkdir(self.TESTDIR)
547 os.mkdir(self.TESTDIR)
548 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
548 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
549 sfile.write("pass\n")
549 sfile.write("pass\n")
550 self.oldpath = os.getcwd()
550 self.oldpath = os.getcwd()
551 os.chdir(self.TESTDIR)
551 os.chdir(self.TESTDIR)
552 self.fname = u"Γ₯Àâtestscript.py"
552 self.fname = u"Γ₯Àâtestscript.py"
553
553
554 def tearDown(self):
554 def tearDown(self):
555 os.chdir(self.oldpath)
555 os.chdir(self.oldpath)
556 shutil.rmtree(self.BASETESTDIR)
556 shutil.rmtree(self.BASETESTDIR)
557
557
558 @onlyif_unicode_paths
558 @onlyif_unicode_paths
559 def test_1(self):
559 def test_1(self):
560 """Test safe_execfile with non-ascii path
560 """Test safe_execfile with non-ascii path
561 """
561 """
562 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
562 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
563
563
564 class ExitCodeChecks(tt.TempFileMixin):
564 class ExitCodeChecks(tt.TempFileMixin):
565
565
566 def setUp(self):
566 def setUp(self):
567 self.system = ip.system_raw
567 self.system = ip.system_raw
568
568
569 def test_exit_code_ok(self):
569 def test_exit_code_ok(self):
570 self.system('exit 0')
570 self.system('exit 0')
571 self.assertEqual(ip.user_ns['_exit_code'], 0)
571 self.assertEqual(ip.user_ns['_exit_code'], 0)
572
572
573 def test_exit_code_error(self):
573 def test_exit_code_error(self):
574 self.system('exit 1')
574 self.system('exit 1')
575 self.assertEqual(ip.user_ns['_exit_code'], 1)
575 self.assertEqual(ip.user_ns['_exit_code'], 1)
576
576
577 @skipif(not hasattr(signal, 'SIGALRM'))
577 @skipif(not hasattr(signal, 'SIGALRM'))
578 def test_exit_code_signal(self):
578 def test_exit_code_signal(self):
579 self.mktmp("import signal, time\n"
579 self.mktmp("import signal, time\n"
580 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
580 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
581 "time.sleep(1)\n")
581 "time.sleep(1)\n")
582 self.system("%s %s" % (sys.executable, self.fname))
582 self.system("%s %s" % (sys.executable, self.fname))
583 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
583 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
584
584
585 @onlyif_cmds_exist("csh")
585 @onlyif_cmds_exist("csh")
586 def test_exit_code_signal_csh(self):
586 def test_exit_code_signal_csh(self):
587 SHELL = os.environ.get('SHELL', None)
587 SHELL = os.environ.get('SHELL', None)
588 os.environ['SHELL'] = find_cmd("csh")
588 os.environ['SHELL'] = find_cmd("csh")
589 try:
589 try:
590 self.test_exit_code_signal()
590 self.test_exit_code_signal()
591 finally:
591 finally:
592 if SHELL is not None:
592 if SHELL is not None:
593 os.environ['SHELL'] = SHELL
593 os.environ['SHELL'] = SHELL
594 else:
594 else:
595 del os.environ['SHELL']
595 del os.environ['SHELL']
596
596
597
597
598 class TestSystemRaw(ExitCodeChecks):
598 class TestSystemRaw(ExitCodeChecks):
599
599
600 def setUp(self):
600 def setUp(self):
601 super().setUp()
601 super().setUp()
602 self.system = ip.system_raw
602 self.system = ip.system_raw
603
603
604 @onlyif_unicode_paths
604 @onlyif_unicode_paths
605 def test_1(self):
605 def test_1(self):
606 """Test system_raw with non-ascii cmd
606 """Test system_raw with non-ascii cmd
607 """
607 """
608 cmd = u'''python -c "'Γ₯Àâ'" '''
608 cmd = u'''python -c "'Γ₯Àâ'" '''
609 ip.system_raw(cmd)
609 ip.system_raw(cmd)
610
610
611 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
611 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
612 @mock.patch('os.system', side_effect=KeyboardInterrupt)
612 @mock.patch('os.system', side_effect=KeyboardInterrupt)
613 def test_control_c(self, *mocks):
613 def test_control_c(self, *mocks):
614 try:
614 try:
615 self.system("sleep 1 # wont happen")
615 self.system("sleep 1 # wont happen")
616 except KeyboardInterrupt:
616 except KeyboardInterrupt:
617 self.fail(
617 self.fail(
618 "system call should intercept "
618 "system call should intercept "
619 "keyboard interrupt from subprocess.call"
619 "keyboard interrupt from subprocess.call"
620 )
620 )
621 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
621 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
622
622
623 def test_magic_warnings(self):
623 def test_magic_warnings(self):
624 for magic_cmd in ("ls", "pip", "conda", "cd"):
624 for magic_cmd in ("ls", "pip", "conda", "cd"):
625 with self.assertWarnsRegex(Warning, "You executed the system command"):
625 with self.assertWarnsRegex(Warning, "You executed the system command"):
626 ip.system_raw(magic_cmd)
626 ip.system_raw(magic_cmd)
627
627
628 # TODO: Exit codes are currently ignored on Windows.
628 # TODO: Exit codes are currently ignored on Windows.
629 class TestSystemPipedExitCode(ExitCodeChecks):
629 class TestSystemPipedExitCode(ExitCodeChecks):
630
630
631 def setUp(self):
631 def setUp(self):
632 super().setUp()
632 super().setUp()
633 self.system = ip.system_piped
633 self.system = ip.system_piped
634
634
635 @skip_win32
635 @skip_win32
636 def test_exit_code_ok(self):
636 def test_exit_code_ok(self):
637 ExitCodeChecks.test_exit_code_ok(self)
637 ExitCodeChecks.test_exit_code_ok(self)
638
638
639 @skip_win32
639 @skip_win32
640 def test_exit_code_error(self):
640 def test_exit_code_error(self):
641 ExitCodeChecks.test_exit_code_error(self)
641 ExitCodeChecks.test_exit_code_error(self)
642
642
643 @skip_win32
643 @skip_win32
644 def test_exit_code_signal(self):
644 def test_exit_code_signal(self):
645 ExitCodeChecks.test_exit_code_signal(self)
645 ExitCodeChecks.test_exit_code_signal(self)
646
646
647 class TestModules(tt.TempFileMixin):
647 class TestModules(tt.TempFileMixin):
648 def test_extraneous_loads(self):
648 def test_extraneous_loads(self):
649 """Test we're not loading modules on startup that we shouldn't.
649 """Test we're not loading modules on startup that we shouldn't.
650 """
650 """
651 self.mktmp("import sys\n"
651 self.mktmp("import sys\n"
652 "print('numpy' in sys.modules)\n"
652 "print('numpy' in sys.modules)\n"
653 "print('ipyparallel' in sys.modules)\n"
653 "print('ipyparallel' in sys.modules)\n"
654 "print('ipykernel' in sys.modules)\n"
654 "print('ipykernel' in sys.modules)\n"
655 )
655 )
656 out = "False\nFalse\nFalse\n"
656 out = "False\nFalse\nFalse\n"
657 tt.ipexec_validate(self.fname, out)
657 tt.ipexec_validate(self.fname, out)
658
658
659 class Negator(ast.NodeTransformer):
659 class Negator(ast.NodeTransformer):
660 """Negates all number literals in an AST."""
660 """Negates all number literals in an AST."""
661
661
662 # for python 3.7 and earlier
662 # for python 3.7 and earlier
663 def visit_Num(self, node):
663 def visit_Num(self, node):
664 node.n = -node.n
664 node.n = -node.n
665 return node
665 return node
666
666
667 # for python 3.8+
667 # for python 3.8+
668 def visit_Constant(self, node):
668 def visit_Constant(self, node):
669 if isinstance(node.value, int):
669 if isinstance(node.value, int):
670 return self.visit_Num(node)
670 return self.visit_Num(node)
671 return node
671 return node
672
672
673 class TestAstTransform(unittest.TestCase):
673 class TestAstTransform(unittest.TestCase):
674 def setUp(self):
674 def setUp(self):
675 self.negator = Negator()
675 self.negator = Negator()
676 ip.ast_transformers.append(self.negator)
676 ip.ast_transformers.append(self.negator)
677
677
678 def tearDown(self):
678 def tearDown(self):
679 ip.ast_transformers.remove(self.negator)
679 ip.ast_transformers.remove(self.negator)
680
680
681 def test_run_cell(self):
681 def test_run_cell(self):
682 with tt.AssertPrints('-34'):
682 with tt.AssertPrints('-34'):
683 ip.run_cell('print (12 + 22)')
683 ip.run_cell('print (12 + 22)')
684
684
685 # A named reference to a number shouldn't be transformed.
685 # A named reference to a number shouldn't be transformed.
686 ip.user_ns['n'] = 55
686 ip.user_ns['n'] = 55
687 with tt.AssertNotPrints('-55'):
687 with tt.AssertNotPrints('-55'):
688 ip.run_cell('print (n)')
688 ip.run_cell('print (n)')
689
689
690 def test_timeit(self):
690 def test_timeit(self):
691 called = set()
691 called = set()
692 def f(x):
692 def f(x):
693 called.add(x)
693 called.add(x)
694 ip.push({'f':f})
694 ip.push({'f':f})
695
695
696 with tt.AssertPrints("std. dev. of"):
696 with tt.AssertPrints("std. dev. of"):
697 ip.run_line_magic("timeit", "-n1 f(1)")
697 ip.run_line_magic("timeit", "-n1 f(1)")
698 self.assertEqual(called, {-1})
698 self.assertEqual(called, {-1})
699 called.clear()
699 called.clear()
700
700
701 with tt.AssertPrints("std. dev. of"):
701 with tt.AssertPrints("std. dev. of"):
702 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
702 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
703 self.assertEqual(called, {-2, -3})
703 self.assertEqual(called, {-2, -3})
704
704
705 def test_time(self):
705 def test_time(self):
706 called = []
706 called = []
707 def f(x):
707 def f(x):
708 called.append(x)
708 called.append(x)
709 ip.push({'f':f})
709 ip.push({'f':f})
710
710
711 # Test with an expression
711 # Test with an expression
712 with tt.AssertPrints("Wall time: "):
712 with tt.AssertPrints("Wall time: "):
713 ip.run_line_magic("time", "f(5+9)")
713 ip.run_line_magic("time", "f(5+9)")
714 self.assertEqual(called, [-14])
714 self.assertEqual(called, [-14])
715 called[:] = []
715 called[:] = []
716
716
717 # Test with a statement (different code path)
717 # Test with a statement (different code path)
718 with tt.AssertPrints("Wall time: "):
718 with tt.AssertPrints("Wall time: "):
719 ip.run_line_magic("time", "a = f(-3 + -2)")
719 ip.run_line_magic("time", "a = f(-3 + -2)")
720 self.assertEqual(called, [5])
720 self.assertEqual(called, [5])
721
721
722 def test_macro(self):
722 def test_macro(self):
723 ip.push({'a':10})
723 ip.push({'a':10})
724 # The AST transformation makes this do a+=-1
724 # The AST transformation makes this do a+=-1
725 ip.define_macro("amacro", "a+=1\nprint(a)")
725 ip.define_macro("amacro", "a+=1\nprint(a)")
726
726
727 with tt.AssertPrints("9"):
727 with tt.AssertPrints("9"):
728 ip.run_cell("amacro")
728 ip.run_cell("amacro")
729 with tt.AssertPrints("8"):
729 with tt.AssertPrints("8"):
730 ip.run_cell("amacro")
730 ip.run_cell("amacro")
731
731
732 class TestMiscTransform(unittest.TestCase):
732 class TestMiscTransform(unittest.TestCase):
733
733
734
734
735 def test_transform_only_once(self):
735 def test_transform_only_once(self):
736 cleanup = 0
736 cleanup = 0
737 line_t = 0
737 line_t = 0
738 def count_cleanup(lines):
738 def count_cleanup(lines):
739 nonlocal cleanup
739 nonlocal cleanup
740 cleanup += 1
740 cleanup += 1
741 return lines
741 return lines
742
742
743 def count_line_t(lines):
743 def count_line_t(lines):
744 nonlocal line_t
744 nonlocal line_t
745 line_t += 1
745 line_t += 1
746 return lines
746 return lines
747
747
748 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
748 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
749 ip.input_transformer_manager.line_transforms.append(count_line_t)
749 ip.input_transformer_manager.line_transforms.append(count_line_t)
750
750
751 ip.run_cell('1')
751 ip.run_cell('1')
752
752
753 assert cleanup == 1
753 assert cleanup == 1
754 assert line_t == 1
754 assert line_t == 1
755
755
756 class IntegerWrapper(ast.NodeTransformer):
756 class IntegerWrapper(ast.NodeTransformer):
757 """Wraps all integers in a call to Integer()"""
757 """Wraps all integers in a call to Integer()"""
758
758
759 # for Python 3.7 and earlier
759 # for Python 3.7 and earlier
760
760
761 # for Python 3.7 and earlier
761 # for Python 3.7 and earlier
762 def visit_Num(self, node):
762 def visit_Num(self, node):
763 if isinstance(node.n, int):
763 if isinstance(node.n, int):
764 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
764 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
765 args=[node], keywords=[])
765 args=[node], keywords=[])
766 return node
766 return node
767
767
768 # For Python 3.8+
768 # For Python 3.8+
769 def visit_Constant(self, node):
769 def visit_Constant(self, node):
770 if isinstance(node.value, int):
770 if isinstance(node.value, int):
771 return self.visit_Num(node)
771 return self.visit_Num(node)
772 return node
772 return node
773
773
774
774
775 class TestAstTransform2(unittest.TestCase):
775 class TestAstTransform2(unittest.TestCase):
776 def setUp(self):
776 def setUp(self):
777 self.intwrapper = IntegerWrapper()
777 self.intwrapper = IntegerWrapper()
778 ip.ast_transformers.append(self.intwrapper)
778 ip.ast_transformers.append(self.intwrapper)
779
779
780 self.calls = []
780 self.calls = []
781 def Integer(*args):
781 def Integer(*args):
782 self.calls.append(args)
782 self.calls.append(args)
783 return args
783 return args
784 ip.push({"Integer": Integer})
784 ip.push({"Integer": Integer})
785
785
786 def tearDown(self):
786 def tearDown(self):
787 ip.ast_transformers.remove(self.intwrapper)
787 ip.ast_transformers.remove(self.intwrapper)
788 del ip.user_ns['Integer']
788 del ip.user_ns['Integer']
789
789
790 def test_run_cell(self):
790 def test_run_cell(self):
791 ip.run_cell("n = 2")
791 ip.run_cell("n = 2")
792 self.assertEqual(self.calls, [(2,)])
792 self.assertEqual(self.calls, [(2,)])
793
793
794 # This shouldn't throw an error
794 # This shouldn't throw an error
795 ip.run_cell("o = 2.0")
795 ip.run_cell("o = 2.0")
796 self.assertEqual(ip.user_ns['o'], 2.0)
796 self.assertEqual(ip.user_ns['o'], 2.0)
797
797
798 def test_timeit(self):
798 def test_timeit(self):
799 called = set()
799 called = set()
800 def f(x):
800 def f(x):
801 called.add(x)
801 called.add(x)
802 ip.push({'f':f})
802 ip.push({'f':f})
803
803
804 with tt.AssertPrints("std. dev. of"):
804 with tt.AssertPrints("std. dev. of"):
805 ip.run_line_magic("timeit", "-n1 f(1)")
805 ip.run_line_magic("timeit", "-n1 f(1)")
806 self.assertEqual(called, {(1,)})
806 self.assertEqual(called, {(1,)})
807 called.clear()
807 called.clear()
808
808
809 with tt.AssertPrints("std. dev. of"):
809 with tt.AssertPrints("std. dev. of"):
810 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
810 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
811 self.assertEqual(called, {(2,), (3,)})
811 self.assertEqual(called, {(2,), (3,)})
812
812
813 class ErrorTransformer(ast.NodeTransformer):
813 class ErrorTransformer(ast.NodeTransformer):
814 """Throws an error when it sees a number."""
814 """Throws an error when it sees a number."""
815
815
816 # for Python 3.7 and earlier
816 # for Python 3.7 and earlier
817 def visit_Num(self, node):
817 def visit_Num(self, node):
818 raise ValueError("test")
818 raise ValueError("test")
819
819
820 # for Python 3.8+
820 # for Python 3.8+
821 def visit_Constant(self, node):
821 def visit_Constant(self, node):
822 if isinstance(node.value, int):
822 if isinstance(node.value, int):
823 return self.visit_Num(node)
823 return self.visit_Num(node)
824 return node
824 return node
825
825
826
826
827 class TestAstTransformError(unittest.TestCase):
827 class TestAstTransformError(unittest.TestCase):
828 def test_unregistering(self):
828 def test_unregistering(self):
829 err_transformer = ErrorTransformer()
829 err_transformer = ErrorTransformer()
830 ip.ast_transformers.append(err_transformer)
830 ip.ast_transformers.append(err_transformer)
831
831
832 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
832 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
833 ip.run_cell("1 + 2")
833 ip.run_cell("1 + 2")
834
834
835 # This should have been removed.
835 # This should have been removed.
836 self.assertNotIn(err_transformer, ip.ast_transformers)
836 self.assertNotIn(err_transformer, ip.ast_transformers)
837
837
838
838
839 class StringRejector(ast.NodeTransformer):
839 class StringRejector(ast.NodeTransformer):
840 """Throws an InputRejected when it sees a string literal.
840 """Throws an InputRejected when it sees a string literal.
841
841
842 Used to verify that NodeTransformers can signal that a piece of code should
842 Used to verify that NodeTransformers can signal that a piece of code should
843 not be executed by throwing an InputRejected.
843 not be executed by throwing an InputRejected.
844 """
844 """
845
845
846 #for python 3.7 and earlier
846 #for python 3.7 and earlier
847 def visit_Str(self, node):
847 def visit_Str(self, node):
848 raise InputRejected("test")
848 raise InputRejected("test")
849
849
850 # 3.8 only
850 # 3.8 only
851 def visit_Constant(self, node):
851 def visit_Constant(self, node):
852 if isinstance(node.value, str):
852 if isinstance(node.value, str):
853 raise InputRejected("test")
853 raise InputRejected("test")
854 return node
854 return node
855
855
856
856
857 class TestAstTransformInputRejection(unittest.TestCase):
857 class TestAstTransformInputRejection(unittest.TestCase):
858
858
859 def setUp(self):
859 def setUp(self):
860 self.transformer = StringRejector()
860 self.transformer = StringRejector()
861 ip.ast_transformers.append(self.transformer)
861 ip.ast_transformers.append(self.transformer)
862
862
863 def tearDown(self):
863 def tearDown(self):
864 ip.ast_transformers.remove(self.transformer)
864 ip.ast_transformers.remove(self.transformer)
865
865
866 def test_input_rejection(self):
866 def test_input_rejection(self):
867 """Check that NodeTransformers can reject input."""
867 """Check that NodeTransformers can reject input."""
868
868
869 expect_exception_tb = tt.AssertPrints("InputRejected: test")
869 expect_exception_tb = tt.AssertPrints("InputRejected: test")
870 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
870 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
871
871
872 # Run the same check twice to verify that the transformer is not
872 # Run the same check twice to verify that the transformer is not
873 # disabled after raising.
873 # disabled after raising.
874 with expect_exception_tb, expect_no_cell_output:
874 with expect_exception_tb, expect_no_cell_output:
875 ip.run_cell("'unsafe'")
875 ip.run_cell("'unsafe'")
876
876
877 with expect_exception_tb, expect_no_cell_output:
877 with expect_exception_tb, expect_no_cell_output:
878 res = ip.run_cell("'unsafe'")
878 res = ip.run_cell("'unsafe'")
879
879
880 self.assertIsInstance(res.error_before_exec, InputRejected)
880 self.assertIsInstance(res.error_before_exec, InputRejected)
881
881
882 def test__IPYTHON__():
882 def test__IPYTHON__():
883 # This shouldn't raise a NameError, that's all
883 # This shouldn't raise a NameError, that's all
884 __IPYTHON__
884 __IPYTHON__
885
885
886
886
887 class DummyRepr(object):
887 class DummyRepr(object):
888 def __repr__(self):
888 def __repr__(self):
889 return "DummyRepr"
889 return "DummyRepr"
890
890
891 def _repr_html_(self):
891 def _repr_html_(self):
892 return "<b>dummy</b>"
892 return "<b>dummy</b>"
893
893
894 def _repr_javascript_(self):
894 def _repr_javascript_(self):
895 return "console.log('hi');", {'key': 'value'}
895 return "console.log('hi');", {'key': 'value'}
896
896
897
897
898 def test_user_variables():
898 def test_user_variables():
899 # enable all formatters
899 # enable all formatters
900 ip.display_formatter.active_types = ip.display_formatter.format_types
900 ip.display_formatter.active_types = ip.display_formatter.format_types
901
901
902 ip.user_ns['dummy'] = d = DummyRepr()
902 ip.user_ns['dummy'] = d = DummyRepr()
903 keys = {'dummy', 'doesnotexist'}
903 keys = {'dummy', 'doesnotexist'}
904 r = ip.user_expressions({ key:key for key in keys})
904 r = ip.user_expressions({ key:key for key in keys})
905
905
906 assert keys == set(r.keys())
906 assert keys == set(r.keys())
907 dummy = r["dummy"]
907 dummy = r["dummy"]
908 assert {"status", "data", "metadata"} == set(dummy.keys())
908 assert {"status", "data", "metadata"} == set(dummy.keys())
909 assert dummy["status"] == "ok"
909 assert dummy["status"] == "ok"
910 data = dummy["data"]
910 data = dummy["data"]
911 metadata = dummy["metadata"]
911 metadata = dummy["metadata"]
912 assert data.get("text/html") == d._repr_html_()
912 assert data.get("text/html") == d._repr_html_()
913 js, jsmd = d._repr_javascript_()
913 js, jsmd = d._repr_javascript_()
914 assert data.get("application/javascript") == js
914 assert data.get("application/javascript") == js
915 assert metadata.get("application/javascript") == jsmd
915 assert metadata.get("application/javascript") == jsmd
916
916
917 dne = r["doesnotexist"]
917 dne = r["doesnotexist"]
918 assert dne["status"] == "error"
918 assert dne["status"] == "error"
919 assert dne["ename"] == "NameError"
919 assert dne["ename"] == "NameError"
920
920
921 # back to text only
921 # back to text only
922 ip.display_formatter.active_types = ['text/plain']
922 ip.display_formatter.active_types = ['text/plain']
923
923
924 def test_user_expression():
924 def test_user_expression():
925 # enable all formatters
925 # enable all formatters
926 ip.display_formatter.active_types = ip.display_formatter.format_types
926 ip.display_formatter.active_types = ip.display_formatter.format_types
927 query = {
927 query = {
928 'a' : '1 + 2',
928 'a' : '1 + 2',
929 'b' : '1/0',
929 'b' : '1/0',
930 }
930 }
931 r = ip.user_expressions(query)
931 r = ip.user_expressions(query)
932 import pprint
932 import pprint
933 pprint.pprint(r)
933 pprint.pprint(r)
934 assert set(r.keys()) == set(query.keys())
934 assert set(r.keys()) == set(query.keys())
935 a = r["a"]
935 a = r["a"]
936 assert {"status", "data", "metadata"} == set(a.keys())
936 assert {"status", "data", "metadata"} == set(a.keys())
937 assert a["status"] == "ok"
937 assert a["status"] == "ok"
938 data = a["data"]
938 data = a["data"]
939 metadata = a["metadata"]
939 metadata = a["metadata"]
940 assert data.get("text/plain") == "3"
940 assert data.get("text/plain") == "3"
941
941
942 b = r["b"]
942 b = r["b"]
943 assert b["status"] == "error"
943 assert b["status"] == "error"
944 assert b["ename"] == "ZeroDivisionError"
944 assert b["ename"] == "ZeroDivisionError"
945
945
946 # back to text only
946 # back to text only
947 ip.display_formatter.active_types = ['text/plain']
947 ip.display_formatter.active_types = ['text/plain']
948
948
949
949
950 class TestSyntaxErrorTransformer(unittest.TestCase):
950 class TestSyntaxErrorTransformer(unittest.TestCase):
951 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
951 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
952
952
953 @staticmethod
953 @staticmethod
954 def transformer(lines):
954 def transformer(lines):
955 for line in lines:
955 for line in lines:
956 pos = line.find('syntaxerror')
956 pos = line.find('syntaxerror')
957 if pos >= 0:
957 if pos >= 0:
958 e = SyntaxError('input contains "syntaxerror"')
958 e = SyntaxError('input contains "syntaxerror"')
959 e.text = line
959 e.text = line
960 e.offset = pos + 1
960 e.offset = pos + 1
961 raise e
961 raise e
962 return lines
962 return lines
963
963
964 def setUp(self):
964 def setUp(self):
965 ip.input_transformers_post.append(self.transformer)
965 ip.input_transformers_post.append(self.transformer)
966
966
967 def tearDown(self):
967 def tearDown(self):
968 ip.input_transformers_post.remove(self.transformer)
968 ip.input_transformers_post.remove(self.transformer)
969
969
970 def test_syntaxerror_input_transformer(self):
970 def test_syntaxerror_input_transformer(self):
971 with tt.AssertPrints('1234'):
971 with tt.AssertPrints('1234'):
972 ip.run_cell('1234')
972 ip.run_cell('1234')
973 with tt.AssertPrints('SyntaxError: invalid syntax'):
973 with tt.AssertPrints('SyntaxError: invalid syntax'):
974 ip.run_cell('1 2 3') # plain python syntax error
974 ip.run_cell('1 2 3') # plain python syntax error
975 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
975 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
976 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
976 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
977 with tt.AssertPrints('3456'):
977 with tt.AssertPrints('3456'):
978 ip.run_cell('3456')
978 ip.run_cell('3456')
979
979
980
980
981 class TestWarningSuppression(unittest.TestCase):
981 class TestWarningSuppression(unittest.TestCase):
982 def test_warning_suppression(self):
982 def test_warning_suppression(self):
983 ip.run_cell("import warnings")
983 ip.run_cell("import warnings")
984 try:
984 try:
985 with self.assertWarnsRegex(UserWarning, "asdf"):
985 with self.assertWarnsRegex(UserWarning, "asdf"):
986 ip.run_cell("warnings.warn('asdf')")
986 ip.run_cell("warnings.warn('asdf')")
987 # Here's the real test -- if we run that again, we should get the
987 # Here's the real test -- if we run that again, we should get the
988 # warning again. Traditionally, each warning was only issued once per
988 # warning again. Traditionally, each warning was only issued once per
989 # IPython session (approximately), even if the user typed in new and
989 # IPython session (approximately), even if the user typed in new and
990 # different code that should have also triggered the warning, leading
990 # different code that should have also triggered the warning, leading
991 # to much confusion.
991 # to much confusion.
992 with self.assertWarnsRegex(UserWarning, "asdf"):
992 with self.assertWarnsRegex(UserWarning, "asdf"):
993 ip.run_cell("warnings.warn('asdf')")
993 ip.run_cell("warnings.warn('asdf')")
994 finally:
994 finally:
995 ip.run_cell("del warnings")
995 ip.run_cell("del warnings")
996
996
997
997
998 def test_deprecation_warning(self):
998 def test_deprecation_warning(self):
999 ip.run_cell("""
999 ip.run_cell("""
1000 import warnings
1000 import warnings
1001 def wrn():
1001 def wrn():
1002 warnings.warn(
1002 warnings.warn(
1003 "I AM A WARNING",
1003 "I AM A WARNING",
1004 DeprecationWarning
1004 DeprecationWarning
1005 )
1005 )
1006 """)
1006 """)
1007 try:
1007 try:
1008 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1008 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1009 ip.run_cell("wrn()")
1009 ip.run_cell("wrn()")
1010 finally:
1010 finally:
1011 ip.run_cell("del warnings")
1011 ip.run_cell("del warnings")
1012 ip.run_cell("del wrn")
1012 ip.run_cell("del wrn")
1013
1013
1014
1014
1015 class TestImportNoDeprecate(tt.TempFileMixin):
1015 class TestImportNoDeprecate(tt.TempFileMixin):
1016
1016
1017 def setUp(self):
1017 def setUp(self):
1018 """Make a valid python temp file."""
1018 """Make a valid python temp file."""
1019 self.mktmp("""
1019 self.mktmp("""
1020 import warnings
1020 import warnings
1021 def wrn():
1021 def wrn():
1022 warnings.warn(
1022 warnings.warn(
1023 "I AM A WARNING",
1023 "I AM A WARNING",
1024 DeprecationWarning
1024 DeprecationWarning
1025 )
1025 )
1026 """)
1026 """)
1027 super().setUp()
1027 super().setUp()
1028
1028
1029 def test_no_dep(self):
1029 def test_no_dep(self):
1030 """
1030 """
1031 No deprecation warning should be raised from imported functions
1031 No deprecation warning should be raised from imported functions
1032 """
1032 """
1033 ip.run_cell("from {} import wrn".format(self.fname))
1033 ip.run_cell("from {} import wrn".format(self.fname))
1034
1034
1035 with tt.AssertNotPrints("I AM A WARNING"):
1035 with tt.AssertNotPrints("I AM A WARNING"):
1036 ip.run_cell("wrn()")
1036 ip.run_cell("wrn()")
1037 ip.run_cell("del wrn")
1037 ip.run_cell("del wrn")
1038
1038
1039
1039
1040 def test_custom_exc_count():
1040 def test_custom_exc_count():
1041 hook = mock.Mock(return_value=None)
1041 hook = mock.Mock(return_value=None)
1042 ip.set_custom_exc((SyntaxError,), hook)
1042 ip.set_custom_exc((SyntaxError,), hook)
1043 before = ip.execution_count
1043 before = ip.execution_count
1044 ip.run_cell("def foo()", store_history=True)
1044 ip.run_cell("def foo()", store_history=True)
1045 # restore default excepthook
1045 # restore default excepthook
1046 ip.set_custom_exc((), None)
1046 ip.set_custom_exc((), None)
1047 assert hook.call_count == 1
1047 assert hook.call_count == 1
1048 assert ip.execution_count == before + 1
1048 assert ip.execution_count == before + 1
1049
1049
1050
1050
1051 def test_run_cell_async():
1051 def test_run_cell_async():
1052 loop = asyncio.get_event_loop_policy().get_event_loop()
1052 loop = asyncio.get_event_loop_policy().get_event_loop()
1053 ip.run_cell("import asyncio")
1053 ip.run_cell("import asyncio")
1054 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1054 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1055 assert asyncio.iscoroutine(coro)
1055 assert asyncio.iscoroutine(coro)
1056 result = loop.run_until_complete(coro)
1056 result = loop.run_until_complete(coro)
1057 assert isinstance(result, interactiveshell.ExecutionResult)
1057 assert isinstance(result, interactiveshell.ExecutionResult)
1058 assert result.result == 5
1058 assert result.result == 5
1059
1059
1060
1060
1061 def test_run_cell_await():
1062 ip.run_cell("import asyncio")
1063 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1064 assert ip.user_ns["_"] == 10
1065
1066
1067 def test_run_cell_asyncio_run():
1068 ip.run_cell("import asyncio")
1069 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1070 assert ip.user_ns["_"] == 1
1071 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1072 assert ip.user_ns["_"] == 2
1073 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1074 assert ip.user_ns["_"] == 3
1075
1076
1061 def test_should_run_async():
1077 def test_should_run_async():
1062 assert not ip.should_run_async("a = 5")
1078 assert not ip.should_run_async("a = 5")
1063 assert ip.should_run_async("await x")
1079 assert ip.should_run_async("await x")
1064 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1080 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1065
1081
1066
1082
1067 def test_set_custom_completer():
1083 def test_set_custom_completer():
1068 num_completers = len(ip.Completer.matchers)
1084 num_completers = len(ip.Completer.matchers)
1069
1085
1070 def foo(*args, **kwargs):
1086 def foo(*args, **kwargs):
1071 return "I'm a completer!"
1087 return "I'm a completer!"
1072
1088
1073 ip.set_custom_completer(foo, 0)
1089 ip.set_custom_completer(foo, 0)
1074
1090
1075 # check that we've really added a new completer
1091 # check that we've really added a new completer
1076 assert len(ip.Completer.matchers) == num_completers + 1
1092 assert len(ip.Completer.matchers) == num_completers + 1
1077
1093
1078 # check that the first completer is the function we defined
1094 # check that the first completer is the function we defined
1079 assert ip.Completer.matchers[0]() == "I'm a completer!"
1095 assert ip.Completer.matchers[0]() == "I'm a completer!"
1080
1096
1081 # clean up
1097 # clean up
1082 ip.Completer.custom_matchers.pop()
1098 ip.Completer.custom_matchers.pop()
@@ -1,1354 +1,1358 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for various magic functions."""
2 """Tests for various magic functions."""
3
3
4 import asyncio
4 import asyncio
5 import io
5 import io
6 import os
6 import os
7 import re
7 import re
8 import shlex
8 import shlex
9 import sys
9 import sys
10 import warnings
10 import warnings
11 from importlib import invalidate_caches
11 from importlib import invalidate_caches
12 from io import StringIO
12 from io import StringIO
13 from pathlib import Path
13 from pathlib import Path
14 from textwrap import dedent
14 from textwrap import dedent
15 from unittest import TestCase, mock
15 from unittest import TestCase, mock
16
16
17 import pytest
17 import pytest
18
18
19 from IPython import get_ipython
19 from IPython import get_ipython
20 from IPython.core import magic
20 from IPython.core import magic
21 from IPython.core.error import UsageError
21 from IPython.core.error import UsageError
22 from IPython.core.magic import (
22 from IPython.core.magic import (
23 Magics,
23 Magics,
24 cell_magic,
24 cell_magic,
25 line_magic,
25 line_magic,
26 magics_class,
26 magics_class,
27 register_cell_magic,
27 register_cell_magic,
28 register_line_magic,
28 register_line_magic,
29 )
29 )
30 from IPython.core.magics import code, execution, logging, osm, script
30 from IPython.core.magics import code, execution, logging, osm, script
31 from IPython.testing import decorators as dec
31 from IPython.testing import decorators as dec
32 from IPython.testing import tools as tt
32 from IPython.testing import tools as tt
33 from IPython.utils.io import capture_output
33 from IPython.utils.io import capture_output
34 from IPython.utils.process import find_cmd
34 from IPython.utils.process import find_cmd
35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
35 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
36
36
37 from .test_debugger import PdbTestInput
37 from .test_debugger import PdbTestInput
38
38
39
39
40 @magic.magics_class
40 @magic.magics_class
41 class DummyMagics(magic.Magics): pass
41 class DummyMagics(magic.Magics): pass
42
42
43 def test_extract_code_ranges():
43 def test_extract_code_ranges():
44 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
44 instr = "1 3 5-6 7-9 10:15 17: :10 10- -13 :"
45 expected = [
45 expected = [
46 (0, 1),
46 (0, 1),
47 (2, 3),
47 (2, 3),
48 (4, 6),
48 (4, 6),
49 (6, 9),
49 (6, 9),
50 (9, 14),
50 (9, 14),
51 (16, None),
51 (16, None),
52 (None, 9),
52 (None, 9),
53 (9, None),
53 (9, None),
54 (None, 13),
54 (None, 13),
55 (None, None),
55 (None, None),
56 ]
56 ]
57 actual = list(code.extract_code_ranges(instr))
57 actual = list(code.extract_code_ranges(instr))
58 assert actual == expected
58 assert actual == expected
59
59
60 def test_extract_symbols():
60 def test_extract_symbols():
61 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
61 source = """import foo\na = 10\ndef b():\n return 42\n\n\nclass A: pass\n\n\n"""
62 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
62 symbols_args = ["a", "b", "A", "A,b", "A,a", "z"]
63 expected = [([], ['a']),
63 expected = [([], ['a']),
64 (["def b():\n return 42\n"], []),
64 (["def b():\n return 42\n"], []),
65 (["class A: pass\n"], []),
65 (["class A: pass\n"], []),
66 (["class A: pass\n", "def b():\n return 42\n"], []),
66 (["class A: pass\n", "def b():\n return 42\n"], []),
67 (["class A: pass\n"], ['a']),
67 (["class A: pass\n"], ['a']),
68 ([], ['z'])]
68 ([], ['z'])]
69 for symbols, exp in zip(symbols_args, expected):
69 for symbols, exp in zip(symbols_args, expected):
70 assert code.extract_symbols(source, symbols) == exp
70 assert code.extract_symbols(source, symbols) == exp
71
71
72
72
73 def test_extract_symbols_raises_exception_with_non_python_code():
73 def test_extract_symbols_raises_exception_with_non_python_code():
74 source = ("=begin A Ruby program :)=end\n"
74 source = ("=begin A Ruby program :)=end\n"
75 "def hello\n"
75 "def hello\n"
76 "puts 'Hello world'\n"
76 "puts 'Hello world'\n"
77 "end")
77 "end")
78 with pytest.raises(SyntaxError):
78 with pytest.raises(SyntaxError):
79 code.extract_symbols(source, "hello")
79 code.extract_symbols(source, "hello")
80
80
81
81
82 def test_magic_not_found():
82 def test_magic_not_found():
83 # magic not found raises UsageError
83 # magic not found raises UsageError
84 with pytest.raises(UsageError):
84 with pytest.raises(UsageError):
85 _ip.magic('doesntexist')
85 _ip.magic('doesntexist')
86
86
87 # ensure result isn't success when a magic isn't found
87 # ensure result isn't success when a magic isn't found
88 result = _ip.run_cell('%doesntexist')
88 result = _ip.run_cell('%doesntexist')
89 assert isinstance(result.error_in_exec, UsageError)
89 assert isinstance(result.error_in_exec, UsageError)
90
90
91
91
92 def test_cell_magic_not_found():
92 def test_cell_magic_not_found():
93 # magic not found raises UsageError
93 # magic not found raises UsageError
94 with pytest.raises(UsageError):
94 with pytest.raises(UsageError):
95 _ip.run_cell_magic('doesntexist', 'line', 'cell')
95 _ip.run_cell_magic('doesntexist', 'line', 'cell')
96
96
97 # ensure result isn't success when a magic isn't found
97 # ensure result isn't success when a magic isn't found
98 result = _ip.run_cell('%%doesntexist')
98 result = _ip.run_cell('%%doesntexist')
99 assert isinstance(result.error_in_exec, UsageError)
99 assert isinstance(result.error_in_exec, UsageError)
100
100
101
101
102 def test_magic_error_status():
102 def test_magic_error_status():
103 def fail(shell):
103 def fail(shell):
104 1/0
104 1/0
105 _ip.register_magic_function(fail)
105 _ip.register_magic_function(fail)
106 result = _ip.run_cell('%fail')
106 result = _ip.run_cell('%fail')
107 assert isinstance(result.error_in_exec, ZeroDivisionError)
107 assert isinstance(result.error_in_exec, ZeroDivisionError)
108
108
109
109
110 def test_config():
110 def test_config():
111 """ test that config magic does not raise
111 """ test that config magic does not raise
112 can happen if Configurable init is moved too early into
112 can happen if Configurable init is moved too early into
113 Magics.__init__ as then a Config object will be registered as a
113 Magics.__init__ as then a Config object will be registered as a
114 magic.
114 magic.
115 """
115 """
116 ## should not raise.
116 ## should not raise.
117 _ip.magic('config')
117 _ip.magic('config')
118
118
119 def test_config_available_configs():
119 def test_config_available_configs():
120 """ test that config magic prints available configs in unique and
120 """ test that config magic prints available configs in unique and
121 sorted order. """
121 sorted order. """
122 with capture_output() as captured:
122 with capture_output() as captured:
123 _ip.magic('config')
123 _ip.magic('config')
124
124
125 stdout = captured.stdout
125 stdout = captured.stdout
126 config_classes = stdout.strip().split('\n')[1:]
126 config_classes = stdout.strip().split('\n')[1:]
127 assert config_classes == sorted(set(config_classes))
127 assert config_classes == sorted(set(config_classes))
128
128
129 def test_config_print_class():
129 def test_config_print_class():
130 """ test that config with a classname prints the class's options. """
130 """ test that config with a classname prints the class's options. """
131 with capture_output() as captured:
131 with capture_output() as captured:
132 _ip.magic('config TerminalInteractiveShell')
132 _ip.magic('config TerminalInteractiveShell')
133
133
134 stdout = captured.stdout
134 stdout = captured.stdout
135 assert re.match(
135 assert re.match(
136 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
136 "TerminalInteractiveShell.* options", stdout.splitlines()[0]
137 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
137 ), f"{stdout}\n\n1st line of stdout not like 'TerminalInteractiveShell.* options'"
138
138
139
139
140 def test_rehashx():
140 def test_rehashx():
141 # clear up everything
141 # clear up everything
142 _ip.alias_manager.clear_aliases()
142 _ip.alias_manager.clear_aliases()
143 del _ip.db['syscmdlist']
143 del _ip.db['syscmdlist']
144
144
145 _ip.magic('rehashx')
145 _ip.magic('rehashx')
146 # Practically ALL ipython development systems will have more than 10 aliases
146 # Practically ALL ipython development systems will have more than 10 aliases
147
147
148 assert len(_ip.alias_manager.aliases) > 10
148 assert len(_ip.alias_manager.aliases) > 10
149 for name, cmd in _ip.alias_manager.aliases:
149 for name, cmd in _ip.alias_manager.aliases:
150 # we must strip dots from alias names
150 # we must strip dots from alias names
151 assert "." not in name
151 assert "." not in name
152
152
153 # rehashx must fill up syscmdlist
153 # rehashx must fill up syscmdlist
154 scoms = _ip.db['syscmdlist']
154 scoms = _ip.db['syscmdlist']
155 assert len(scoms) > 10
155 assert len(scoms) > 10
156
156
157
157
158 def test_magic_parse_options():
158 def test_magic_parse_options():
159 """Test that we don't mangle paths when parsing magic options."""
159 """Test that we don't mangle paths when parsing magic options."""
160 ip = get_ipython()
160 ip = get_ipython()
161 path = 'c:\\x'
161 path = 'c:\\x'
162 m = DummyMagics(ip)
162 m = DummyMagics(ip)
163 opts = m.parse_options('-f %s' % path,'f:')[0]
163 opts = m.parse_options('-f %s' % path,'f:')[0]
164 # argv splitting is os-dependent
164 # argv splitting is os-dependent
165 if os.name == 'posix':
165 if os.name == 'posix':
166 expected = 'c:x'
166 expected = 'c:x'
167 else:
167 else:
168 expected = path
168 expected = path
169 assert opts["f"] == expected
169 assert opts["f"] == expected
170
170
171
171
172 def test_magic_parse_long_options():
172 def test_magic_parse_long_options():
173 """Magic.parse_options can handle --foo=bar long options"""
173 """Magic.parse_options can handle --foo=bar long options"""
174 ip = get_ipython()
174 ip = get_ipython()
175 m = DummyMagics(ip)
175 m = DummyMagics(ip)
176 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
176 opts, _ = m.parse_options("--foo --bar=bubble", "a", "foo", "bar=")
177 assert "foo" in opts
177 assert "foo" in opts
178 assert "bar" in opts
178 assert "bar" in opts
179 assert opts["bar"] == "bubble"
179 assert opts["bar"] == "bubble"
180
180
181
181
182 def doctest_hist_f():
182 def doctest_hist_f():
183 """Test %hist -f with temporary filename.
183 """Test %hist -f with temporary filename.
184
184
185 In [9]: import tempfile
185 In [9]: import tempfile
186
186
187 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
187 In [10]: tfile = tempfile.mktemp('.py','tmp-ipython-')
188
188
189 In [11]: %hist -nl -f $tfile 3
189 In [11]: %hist -nl -f $tfile 3
190
190
191 In [13]: import os; os.unlink(tfile)
191 In [13]: import os; os.unlink(tfile)
192 """
192 """
193
193
194
194
195 def doctest_hist_op():
195 def doctest_hist_op():
196 """Test %hist -op
196 """Test %hist -op
197
197
198 In [1]: class b(float):
198 In [1]: class b(float):
199 ...: pass
199 ...: pass
200 ...:
200 ...:
201
201
202 In [2]: class s(object):
202 In [2]: class s(object):
203 ...: def __str__(self):
203 ...: def __str__(self):
204 ...: return 's'
204 ...: return 's'
205 ...:
205 ...:
206
206
207 In [3]:
207 In [3]:
208
208
209 In [4]: class r(b):
209 In [4]: class r(b):
210 ...: def __repr__(self):
210 ...: def __repr__(self):
211 ...: return 'r'
211 ...: return 'r'
212 ...:
212 ...:
213
213
214 In [5]: class sr(s,r): pass
214 In [5]: class sr(s,r): pass
215 ...:
215 ...:
216
216
217 In [6]:
217 In [6]:
218
218
219 In [7]: bb=b()
219 In [7]: bb=b()
220
220
221 In [8]: ss=s()
221 In [8]: ss=s()
222
222
223 In [9]: rr=r()
223 In [9]: rr=r()
224
224
225 In [10]: ssrr=sr()
225 In [10]: ssrr=sr()
226
226
227 In [11]: 4.5
227 In [11]: 4.5
228 Out[11]: 4.5
228 Out[11]: 4.5
229
229
230 In [12]: str(ss)
230 In [12]: str(ss)
231 Out[12]: 's'
231 Out[12]: 's'
232
232
233 In [13]:
233 In [13]:
234
234
235 In [14]: %hist -op
235 In [14]: %hist -op
236 >>> class b:
236 >>> class b:
237 ... pass
237 ... pass
238 ...
238 ...
239 >>> class s(b):
239 >>> class s(b):
240 ... def __str__(self):
240 ... def __str__(self):
241 ... return 's'
241 ... return 's'
242 ...
242 ...
243 >>>
243 >>>
244 >>> class r(b):
244 >>> class r(b):
245 ... def __repr__(self):
245 ... def __repr__(self):
246 ... return 'r'
246 ... return 'r'
247 ...
247 ...
248 >>> class sr(s,r): pass
248 >>> class sr(s,r): pass
249 >>>
249 >>>
250 >>> bb=b()
250 >>> bb=b()
251 >>> ss=s()
251 >>> ss=s()
252 >>> rr=r()
252 >>> rr=r()
253 >>> ssrr=sr()
253 >>> ssrr=sr()
254 >>> 4.5
254 >>> 4.5
255 4.5
255 4.5
256 >>> str(ss)
256 >>> str(ss)
257 's'
257 's'
258 >>>
258 >>>
259 """
259 """
260
260
261 def test_hist_pof():
261 def test_hist_pof():
262 ip = get_ipython()
262 ip = get_ipython()
263 ip.run_cell("1+2", store_history=True)
263 ip.run_cell("1+2", store_history=True)
264 #raise Exception(ip.history_manager.session_number)
264 #raise Exception(ip.history_manager.session_number)
265 #raise Exception(list(ip.history_manager._get_range_session()))
265 #raise Exception(list(ip.history_manager._get_range_session()))
266 with TemporaryDirectory() as td:
266 with TemporaryDirectory() as td:
267 tf = os.path.join(td, 'hist.py')
267 tf = os.path.join(td, 'hist.py')
268 ip.run_line_magic('history', '-pof %s' % tf)
268 ip.run_line_magic('history', '-pof %s' % tf)
269 assert os.path.isfile(tf)
269 assert os.path.isfile(tf)
270
270
271
271
272 def test_macro():
272 def test_macro():
273 ip = get_ipython()
273 ip = get_ipython()
274 ip.history_manager.reset() # Clear any existing history.
274 ip.history_manager.reset() # Clear any existing history.
275 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
275 cmds = ["a=1", "def b():\n return a**2", "print(a,b())"]
276 for i, cmd in enumerate(cmds, start=1):
276 for i, cmd in enumerate(cmds, start=1):
277 ip.history_manager.store_inputs(i, cmd)
277 ip.history_manager.store_inputs(i, cmd)
278 ip.magic("macro test 1-3")
278 ip.magic("macro test 1-3")
279 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
279 assert ip.user_ns["test"].value == "\n".join(cmds) + "\n"
280
280
281 # List macros
281 # List macros
282 assert "test" in ip.magic("macro")
282 assert "test" in ip.magic("macro")
283
283
284
284
285 def test_macro_run():
285 def test_macro_run():
286 """Test that we can run a multi-line macro successfully."""
286 """Test that we can run a multi-line macro successfully."""
287 ip = get_ipython()
287 ip = get_ipython()
288 ip.history_manager.reset()
288 ip.history_manager.reset()
289 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
289 cmds = ["a=10", "a+=1", "print(a)", "%macro test 2-3"]
290 for cmd in cmds:
290 for cmd in cmds:
291 ip.run_cell(cmd, store_history=True)
291 ip.run_cell(cmd, store_history=True)
292 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
292 assert ip.user_ns["test"].value == "a+=1\nprint(a)\n"
293 with tt.AssertPrints("12"):
293 with tt.AssertPrints("12"):
294 ip.run_cell("test")
294 ip.run_cell("test")
295 with tt.AssertPrints("13"):
295 with tt.AssertPrints("13"):
296 ip.run_cell("test")
296 ip.run_cell("test")
297
297
298
298
299 def test_magic_magic():
299 def test_magic_magic():
300 """Test %magic"""
300 """Test %magic"""
301 ip = get_ipython()
301 ip = get_ipython()
302 with capture_output() as captured:
302 with capture_output() as captured:
303 ip.magic("magic")
303 ip.magic("magic")
304
304
305 stdout = captured.stdout
305 stdout = captured.stdout
306 assert "%magic" in stdout
306 assert "%magic" in stdout
307 assert "IPython" in stdout
307 assert "IPython" in stdout
308 assert "Available" in stdout
308 assert "Available" in stdout
309
309
310
310
311 @dec.skipif_not_numpy
311 @dec.skipif_not_numpy
312 def test_numpy_reset_array_undec():
312 def test_numpy_reset_array_undec():
313 "Test '%reset array' functionality"
313 "Test '%reset array' functionality"
314 _ip.ex("import numpy as np")
314 _ip.ex("import numpy as np")
315 _ip.ex("a = np.empty(2)")
315 _ip.ex("a = np.empty(2)")
316 assert "a" in _ip.user_ns
316 assert "a" in _ip.user_ns
317 _ip.magic("reset -f array")
317 _ip.magic("reset -f array")
318 assert "a" not in _ip.user_ns
318 assert "a" not in _ip.user_ns
319
319
320
320
321 def test_reset_out():
321 def test_reset_out():
322 "Test '%reset out' magic"
322 "Test '%reset out' magic"
323 _ip.run_cell("parrot = 'dead'", store_history=True)
323 _ip.run_cell("parrot = 'dead'", store_history=True)
324 # test '%reset -f out', make an Out prompt
324 # test '%reset -f out', make an Out prompt
325 _ip.run_cell("parrot", store_history=True)
325 _ip.run_cell("parrot", store_history=True)
326 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
326 assert "dead" in [_ip.user_ns[x] for x in ("_", "__", "___")]
327 _ip.magic("reset -f out")
327 _ip.magic("reset -f out")
328 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
328 assert "dead" not in [_ip.user_ns[x] for x in ("_", "__", "___")]
329 assert len(_ip.user_ns["Out"]) == 0
329 assert len(_ip.user_ns["Out"]) == 0
330
330
331
331
332 def test_reset_in():
332 def test_reset_in():
333 "Test '%reset in' magic"
333 "Test '%reset in' magic"
334 # test '%reset -f in'
334 # test '%reset -f in'
335 _ip.run_cell("parrot", store_history=True)
335 _ip.run_cell("parrot", store_history=True)
336 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
336 assert "parrot" in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
337 _ip.magic("%reset -f in")
337 _ip.magic("%reset -f in")
338 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
338 assert "parrot" not in [_ip.user_ns[x] for x in ("_i", "_ii", "_iii")]
339 assert len(set(_ip.user_ns["In"])) == 1
339 assert len(set(_ip.user_ns["In"])) == 1
340
340
341
341
342 def test_reset_dhist():
342 def test_reset_dhist():
343 "Test '%reset dhist' magic"
343 "Test '%reset dhist' magic"
344 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
344 _ip.run_cell("tmp = [d for d in _dh]") # copy before clearing
345 _ip.magic("cd " + os.path.dirname(pytest.__file__))
345 _ip.magic("cd " + os.path.dirname(pytest.__file__))
346 _ip.magic("cd -")
346 _ip.magic("cd -")
347 assert len(_ip.user_ns["_dh"]) > 0
347 assert len(_ip.user_ns["_dh"]) > 0
348 _ip.magic("reset -f dhist")
348 _ip.magic("reset -f dhist")
349 assert len(_ip.user_ns["_dh"]) == 0
349 assert len(_ip.user_ns["_dh"]) == 0
350 _ip.run_cell("_dh = [d for d in tmp]") # restore
350 _ip.run_cell("_dh = [d for d in tmp]") # restore
351
351
352
352
353 def test_reset_in_length():
353 def test_reset_in_length():
354 "Test that '%reset in' preserves In[] length"
354 "Test that '%reset in' preserves In[] length"
355 _ip.run_cell("print 'foo'")
355 _ip.run_cell("print 'foo'")
356 _ip.run_cell("reset -f in")
356 _ip.run_cell("reset -f in")
357 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
357 assert len(_ip.user_ns["In"]) == _ip.displayhook.prompt_count + 1
358
358
359
359
360 class TestResetErrors(TestCase):
360 class TestResetErrors(TestCase):
361
361
362 def test_reset_redefine(self):
362 def test_reset_redefine(self):
363
363
364 @magics_class
364 @magics_class
365 class KernelMagics(Magics):
365 class KernelMagics(Magics):
366 @line_magic
366 @line_magic
367 def less(self, shell): pass
367 def less(self, shell): pass
368
368
369 _ip.register_magics(KernelMagics)
369 _ip.register_magics(KernelMagics)
370
370
371 with self.assertLogs() as cm:
371 with self.assertLogs() as cm:
372 # hack, we want to just capture logs, but assertLogs fails if not
372 # hack, we want to just capture logs, but assertLogs fails if not
373 # logs get produce.
373 # logs get produce.
374 # so log one things we ignore.
374 # so log one things we ignore.
375 import logging as log_mod
375 import logging as log_mod
376 log = log_mod.getLogger()
376 log = log_mod.getLogger()
377 log.info('Nothing')
377 log.info('Nothing')
378 # end hack.
378 # end hack.
379 _ip.run_cell("reset -f")
379 _ip.run_cell("reset -f")
380
380
381 assert len(cm.output) == 1
381 assert len(cm.output) == 1
382 for out in cm.output:
382 for out in cm.output:
383 assert "Invalid alias" not in out
383 assert "Invalid alias" not in out
384
384
385 def test_tb_syntaxerror():
385 def test_tb_syntaxerror():
386 """test %tb after a SyntaxError"""
386 """test %tb after a SyntaxError"""
387 ip = get_ipython()
387 ip = get_ipython()
388 ip.run_cell("for")
388 ip.run_cell("for")
389
389
390 # trap and validate stdout
390 # trap and validate stdout
391 save_stdout = sys.stdout
391 save_stdout = sys.stdout
392 try:
392 try:
393 sys.stdout = StringIO()
393 sys.stdout = StringIO()
394 ip.run_cell("%tb")
394 ip.run_cell("%tb")
395 out = sys.stdout.getvalue()
395 out = sys.stdout.getvalue()
396 finally:
396 finally:
397 sys.stdout = save_stdout
397 sys.stdout = save_stdout
398 # trim output, and only check the last line
398 # trim output, and only check the last line
399 last_line = out.rstrip().splitlines()[-1].strip()
399 last_line = out.rstrip().splitlines()[-1].strip()
400 assert last_line == "SyntaxError: invalid syntax"
400 assert last_line == "SyntaxError: invalid syntax"
401
401
402
402
403 def test_time():
403 def test_time():
404 ip = get_ipython()
404 ip = get_ipython()
405
405
406 with tt.AssertPrints("Wall time: "):
406 with tt.AssertPrints("Wall time: "):
407 ip.run_cell("%time None")
407 ip.run_cell("%time None")
408
408
409 ip.run_cell("def f(kmjy):\n"
409 ip.run_cell("def f(kmjy):\n"
410 " %time print (2*kmjy)")
410 " %time print (2*kmjy)")
411
411
412 with tt.AssertPrints("Wall time: "):
412 with tt.AssertPrints("Wall time: "):
413 with tt.AssertPrints("hihi", suppress=False):
413 with tt.AssertPrints("hihi", suppress=False):
414 ip.run_cell("f('hi')")
414 ip.run_cell("f('hi')")
415
415
416 def test_time_last_not_expression():
416 def test_time_last_not_expression():
417 ip.run_cell("%%time\n"
417 ip.run_cell("%%time\n"
418 "var_1 = 1\n"
418 "var_1 = 1\n"
419 "var_2 = 2\n")
419 "var_2 = 2\n")
420 assert ip.user_ns['var_1'] == 1
420 assert ip.user_ns['var_1'] == 1
421 del ip.user_ns['var_1']
421 del ip.user_ns['var_1']
422 assert ip.user_ns['var_2'] == 2
422 assert ip.user_ns['var_2'] == 2
423 del ip.user_ns['var_2']
423 del ip.user_ns['var_2']
424
424
425
425
426 @dec.skip_win32
426 @dec.skip_win32
427 def test_time2():
427 def test_time2():
428 ip = get_ipython()
428 ip = get_ipython()
429
429
430 with tt.AssertPrints("CPU times: user "):
430 with tt.AssertPrints("CPU times: user "):
431 ip.run_cell("%time None")
431 ip.run_cell("%time None")
432
432
433 def test_time3():
433 def test_time3():
434 """Erroneous magic function calls, issue gh-3334"""
434 """Erroneous magic function calls, issue gh-3334"""
435 ip = get_ipython()
435 ip = get_ipython()
436 ip.user_ns.pop('run', None)
436 ip.user_ns.pop('run', None)
437
437
438 with tt.AssertNotPrints("not found", channel='stderr'):
438 with tt.AssertNotPrints("not found", channel='stderr'):
439 ip.run_cell("%%time\n"
439 ip.run_cell("%%time\n"
440 "run = 0\n"
440 "run = 0\n"
441 "run += 1")
441 "run += 1")
442
442
443 def test_multiline_time():
443 def test_multiline_time():
444 """Make sure last statement from time return a value."""
444 """Make sure last statement from time return a value."""
445 ip = get_ipython()
445 ip = get_ipython()
446 ip.user_ns.pop('run', None)
446 ip.user_ns.pop('run', None)
447
447
448 ip.run_cell(dedent("""\
448 ip.run_cell(dedent("""\
449 %%time
449 %%time
450 a = "ho"
450 a = "ho"
451 b = "hey"
451 b = "hey"
452 a+b
452 a+b
453 """
453 """
454 )
454 )
455 )
455 )
456 assert ip.user_ns_hidden["_"] == "hohey"
456 assert ip.user_ns_hidden["_"] == "hohey"
457
457
458
458
459 def test_time_local_ns():
459 def test_time_local_ns():
460 """
460 """
461 Test that local_ns is actually global_ns when running a cell magic
461 Test that local_ns is actually global_ns when running a cell magic
462 """
462 """
463 ip = get_ipython()
463 ip = get_ipython()
464 ip.run_cell("%%time\n" "myvar = 1")
464 ip.run_cell("%%time\n" "myvar = 1")
465 assert ip.user_ns["myvar"] == 1
465 assert ip.user_ns["myvar"] == 1
466 del ip.user_ns["myvar"]
466 del ip.user_ns["myvar"]
467
467
468
468
469 def test_doctest_mode():
469 def test_doctest_mode():
470 "Toggle doctest_mode twice, it should be a no-op and run without error"
470 "Toggle doctest_mode twice, it should be a no-op and run without error"
471 _ip.magic('doctest_mode')
471 _ip.magic('doctest_mode')
472 _ip.magic('doctest_mode')
472 _ip.magic('doctest_mode')
473
473
474
474
475 def test_parse_options():
475 def test_parse_options():
476 """Tests for basic options parsing in magics."""
476 """Tests for basic options parsing in magics."""
477 # These are only the most minimal of tests, more should be added later. At
477 # These are only the most minimal of tests, more should be added later. At
478 # the very least we check that basic text/unicode calls work OK.
478 # the very least we check that basic text/unicode calls work OK.
479 m = DummyMagics(_ip)
479 m = DummyMagics(_ip)
480 assert m.parse_options("foo", "")[1] == "foo"
480 assert m.parse_options("foo", "")[1] == "foo"
481 assert m.parse_options("foo", "")[1] == "foo"
481 assert m.parse_options("foo", "")[1] == "foo"
482
482
483
483
484 def test_parse_options_preserve_non_option_string():
484 def test_parse_options_preserve_non_option_string():
485 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
485 """Test to assert preservation of non-option part of magic-block, while parsing magic options."""
486 m = DummyMagics(_ip)
486 m = DummyMagics(_ip)
487 opts, stmt = m.parse_options(
487 opts, stmt = m.parse_options(
488 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
488 " -n1 -r 13 _ = 314 + foo", "n:r:", preserve_non_opts=True
489 )
489 )
490 assert opts == {"n": "1", "r": "13"}
490 assert opts == {"n": "1", "r": "13"}
491 assert stmt == "_ = 314 + foo"
491 assert stmt == "_ = 314 + foo"
492
492
493
493
494 def test_run_magic_preserve_code_block():
494 def test_run_magic_preserve_code_block():
495 """Test to assert preservation of non-option part of magic-block, while running magic."""
495 """Test to assert preservation of non-option part of magic-block, while running magic."""
496 _ip.user_ns["spaces"] = []
496 _ip.user_ns["spaces"] = []
497 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
497 _ip.magic("timeit -n1 -r1 spaces.append([s.count(' ') for s in ['document']])")
498 assert _ip.user_ns["spaces"] == [[0]]
498 assert _ip.user_ns["spaces"] == [[0]]
499
499
500
500
501 def test_dirops():
501 def test_dirops():
502 """Test various directory handling operations."""
502 """Test various directory handling operations."""
503 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
503 # curpath = lambda :os.path.splitdrive(os.getcwd())[1].replace('\\','/')
504 curpath = os.getcwd
504 curpath = os.getcwd
505 startdir = os.getcwd()
505 startdir = os.getcwd()
506 ipdir = os.path.realpath(_ip.ipython_dir)
506 ipdir = os.path.realpath(_ip.ipython_dir)
507 try:
507 try:
508 _ip.magic('cd "%s"' % ipdir)
508 _ip.magic('cd "%s"' % ipdir)
509 assert curpath() == ipdir
509 assert curpath() == ipdir
510 _ip.magic('cd -')
510 _ip.magic('cd -')
511 assert curpath() == startdir
511 assert curpath() == startdir
512 _ip.magic('pushd "%s"' % ipdir)
512 _ip.magic('pushd "%s"' % ipdir)
513 assert curpath() == ipdir
513 assert curpath() == ipdir
514 _ip.magic('popd')
514 _ip.magic('popd')
515 assert curpath() == startdir
515 assert curpath() == startdir
516 finally:
516 finally:
517 os.chdir(startdir)
517 os.chdir(startdir)
518
518
519
519
520 def test_cd_force_quiet():
520 def test_cd_force_quiet():
521 """Test OSMagics.cd_force_quiet option"""
521 """Test OSMagics.cd_force_quiet option"""
522 _ip.config.OSMagics.cd_force_quiet = True
522 _ip.config.OSMagics.cd_force_quiet = True
523 osmagics = osm.OSMagics(shell=_ip)
523 osmagics = osm.OSMagics(shell=_ip)
524
524
525 startdir = os.getcwd()
525 startdir = os.getcwd()
526 ipdir = os.path.realpath(_ip.ipython_dir)
526 ipdir = os.path.realpath(_ip.ipython_dir)
527
527
528 try:
528 try:
529 with tt.AssertNotPrints(ipdir):
529 with tt.AssertNotPrints(ipdir):
530 osmagics.cd('"%s"' % ipdir)
530 osmagics.cd('"%s"' % ipdir)
531 with tt.AssertNotPrints(startdir):
531 with tt.AssertNotPrints(startdir):
532 osmagics.cd('-')
532 osmagics.cd('-')
533 finally:
533 finally:
534 os.chdir(startdir)
534 os.chdir(startdir)
535
535
536
536
537 def test_xmode():
537 def test_xmode():
538 # Calling xmode three times should be a no-op
538 # Calling xmode three times should be a no-op
539 xmode = _ip.InteractiveTB.mode
539 xmode = _ip.InteractiveTB.mode
540 for i in range(4):
540 for i in range(4):
541 _ip.magic("xmode")
541 _ip.magic("xmode")
542 assert _ip.InteractiveTB.mode == xmode
542 assert _ip.InteractiveTB.mode == xmode
543
543
544 def test_reset_hard():
544 def test_reset_hard():
545 monitor = []
545 monitor = []
546 class A(object):
546 class A(object):
547 def __del__(self):
547 def __del__(self):
548 monitor.append(1)
548 monitor.append(1)
549 def __repr__(self):
549 def __repr__(self):
550 return "<A instance>"
550 return "<A instance>"
551
551
552 _ip.user_ns["a"] = A()
552 _ip.user_ns["a"] = A()
553 _ip.run_cell("a")
553 _ip.run_cell("a")
554
554
555 assert monitor == []
555 assert monitor == []
556 _ip.magic("reset -f")
556 _ip.magic("reset -f")
557 assert monitor == [1]
557 assert monitor == [1]
558
558
559 class TestXdel(tt.TempFileMixin):
559 class TestXdel(tt.TempFileMixin):
560 def test_xdel(self):
560 def test_xdel(self):
561 """Test that references from %run are cleared by xdel."""
561 """Test that references from %run are cleared by xdel."""
562 src = ("class A(object):\n"
562 src = ("class A(object):\n"
563 " monitor = []\n"
563 " monitor = []\n"
564 " def __del__(self):\n"
564 " def __del__(self):\n"
565 " self.monitor.append(1)\n"
565 " self.monitor.append(1)\n"
566 "a = A()\n")
566 "a = A()\n")
567 self.mktmp(src)
567 self.mktmp(src)
568 # %run creates some hidden references...
568 # %run creates some hidden references...
569 _ip.magic("run %s" % self.fname)
569 _ip.magic("run %s" % self.fname)
570 # ... as does the displayhook.
570 # ... as does the displayhook.
571 _ip.run_cell("a")
571 _ip.run_cell("a")
572
572
573 monitor = _ip.user_ns["A"].monitor
573 monitor = _ip.user_ns["A"].monitor
574 assert monitor == []
574 assert monitor == []
575
575
576 _ip.magic("xdel a")
576 _ip.magic("xdel a")
577
577
578 # Check that a's __del__ method has been called.
578 # Check that a's __del__ method has been called.
579 assert monitor == [1]
579 assert monitor == [1]
580
580
581 def doctest_who():
581 def doctest_who():
582 """doctest for %who
582 """doctest for %who
583
583
584 In [1]: %reset -sf
584 In [1]: %reset -sf
585
585
586 In [2]: alpha = 123
586 In [2]: alpha = 123
587
587
588 In [3]: beta = 'beta'
588 In [3]: beta = 'beta'
589
589
590 In [4]: %who int
590 In [4]: %who int
591 alpha
591 alpha
592
592
593 In [5]: %who str
593 In [5]: %who str
594 beta
594 beta
595
595
596 In [6]: %whos
596 In [6]: %whos
597 Variable Type Data/Info
597 Variable Type Data/Info
598 ----------------------------
598 ----------------------------
599 alpha int 123
599 alpha int 123
600 beta str beta
600 beta str beta
601
601
602 In [7]: %who_ls
602 In [7]: %who_ls
603 Out[7]: ['alpha', 'beta']
603 Out[7]: ['alpha', 'beta']
604 """
604 """
605
605
606 def test_whos():
606 def test_whos():
607 """Check that whos is protected against objects where repr() fails."""
607 """Check that whos is protected against objects where repr() fails."""
608 class A(object):
608 class A(object):
609 def __repr__(self):
609 def __repr__(self):
610 raise Exception()
610 raise Exception()
611 _ip.user_ns['a'] = A()
611 _ip.user_ns['a'] = A()
612 _ip.magic("whos")
612 _ip.magic("whos")
613
613
614 def doctest_precision():
614 def doctest_precision():
615 """doctest for %precision
615 """doctest for %precision
616
616
617 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
617 In [1]: f = get_ipython().display_formatter.formatters['text/plain']
618
618
619 In [2]: %precision 5
619 In [2]: %precision 5
620 Out[2]: '%.5f'
620 Out[2]: '%.5f'
621
621
622 In [3]: f.float_format
622 In [3]: f.float_format
623 Out[3]: '%.5f'
623 Out[3]: '%.5f'
624
624
625 In [4]: %precision %e
625 In [4]: %precision %e
626 Out[4]: '%e'
626 Out[4]: '%e'
627
627
628 In [5]: f(3.1415927)
628 In [5]: f(3.1415927)
629 Out[5]: '3.141593e+00'
629 Out[5]: '3.141593e+00'
630 """
630 """
631
631
632 def test_debug_magic():
632 def test_debug_magic():
633 """Test debugging a small code with %debug
633 """Test debugging a small code with %debug
634
634
635 In [1]: with PdbTestInput(['c']):
635 In [1]: with PdbTestInput(['c']):
636 ...: %debug print("a b") #doctest: +ELLIPSIS
636 ...: %debug print("a b") #doctest: +ELLIPSIS
637 ...:
637 ...:
638 ...
638 ...
639 ipdb> c
639 ipdb> c
640 a b
640 a b
641 In [2]:
641 In [2]:
642 """
642 """
643
643
644 def test_psearch():
644 def test_psearch():
645 with tt.AssertPrints("dict.fromkeys"):
645 with tt.AssertPrints("dict.fromkeys"):
646 _ip.run_cell("dict.fr*?")
646 _ip.run_cell("dict.fr*?")
647 with tt.AssertPrints("Ο€.is_integer"):
647 with tt.AssertPrints("Ο€.is_integer"):
648 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
648 _ip.run_cell("Ο€ = 3.14;\nΟ€.is_integ*?")
649
649
650 def test_timeit_shlex():
650 def test_timeit_shlex():
651 """test shlex issues with timeit (#1109)"""
651 """test shlex issues with timeit (#1109)"""
652 _ip.ex("def f(*a,**kw): pass")
652 _ip.ex("def f(*a,**kw): pass")
653 _ip.magic('timeit -n1 "this is a bug".count(" ")')
653 _ip.magic('timeit -n1 "this is a bug".count(" ")')
654 _ip.magic('timeit -r1 -n1 f(" ", 1)')
654 _ip.magic('timeit -r1 -n1 f(" ", 1)')
655 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
655 _ip.magic('timeit -r1 -n1 f(" ", 1, " ", 2, " ")')
656 _ip.magic('timeit -r1 -n1 ("a " + "b")')
656 _ip.magic('timeit -r1 -n1 ("a " + "b")')
657 _ip.magic('timeit -r1 -n1 f("a " + "b")')
657 _ip.magic('timeit -r1 -n1 f("a " + "b")')
658 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
658 _ip.magic('timeit -r1 -n1 f("a " + "b ")')
659
659
660
660
661 def test_timeit_special_syntax():
661 def test_timeit_special_syntax():
662 "Test %%timeit with IPython special syntax"
662 "Test %%timeit with IPython special syntax"
663 @register_line_magic
663 @register_line_magic
664 def lmagic(line):
664 def lmagic(line):
665 ip = get_ipython()
665 ip = get_ipython()
666 ip.user_ns['lmagic_out'] = line
666 ip.user_ns['lmagic_out'] = line
667
667
668 # line mode test
668 # line mode test
669 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
669 _ip.run_line_magic("timeit", "-n1 -r1 %lmagic my line")
670 assert _ip.user_ns["lmagic_out"] == "my line"
670 assert _ip.user_ns["lmagic_out"] == "my line"
671 # cell mode test
671 # cell mode test
672 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
672 _ip.run_cell_magic("timeit", "-n1 -r1", "%lmagic my line2")
673 assert _ip.user_ns["lmagic_out"] == "my line2"
673 assert _ip.user_ns["lmagic_out"] == "my line2"
674
674
675
675
676 def test_timeit_return():
676 def test_timeit_return():
677 """
677 """
678 test whether timeit -o return object
678 test whether timeit -o return object
679 """
679 """
680
680
681 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
681 res = _ip.run_line_magic('timeit','-n10 -r10 -o 1')
682 assert(res is not None)
682 assert(res is not None)
683
683
684 def test_timeit_quiet():
684 def test_timeit_quiet():
685 """
685 """
686 test quiet option of timeit magic
686 test quiet option of timeit magic
687 """
687 """
688 with tt.AssertNotPrints("loops"):
688 with tt.AssertNotPrints("loops"):
689 _ip.run_cell("%timeit -n1 -r1 -q 1")
689 _ip.run_cell("%timeit -n1 -r1 -q 1")
690
690
691 def test_timeit_return_quiet():
691 def test_timeit_return_quiet():
692 with tt.AssertNotPrints("loops"):
692 with tt.AssertNotPrints("loops"):
693 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
693 res = _ip.run_line_magic('timeit', '-n1 -r1 -q -o 1')
694 assert (res is not None)
694 assert (res is not None)
695
695
696 def test_timeit_invalid_return():
696 def test_timeit_invalid_return():
697 with pytest.raises(SyntaxError):
697 with pytest.raises(SyntaxError):
698 _ip.run_line_magic('timeit', 'return')
698 _ip.run_line_magic('timeit', 'return')
699
699
700 @dec.skipif(execution.profile is None)
700 @dec.skipif(execution.profile is None)
701 def test_prun_special_syntax():
701 def test_prun_special_syntax():
702 "Test %%prun with IPython special syntax"
702 "Test %%prun with IPython special syntax"
703 @register_line_magic
703 @register_line_magic
704 def lmagic(line):
704 def lmagic(line):
705 ip = get_ipython()
705 ip = get_ipython()
706 ip.user_ns['lmagic_out'] = line
706 ip.user_ns['lmagic_out'] = line
707
707
708 # line mode test
708 # line mode test
709 _ip.run_line_magic("prun", "-q %lmagic my line")
709 _ip.run_line_magic("prun", "-q %lmagic my line")
710 assert _ip.user_ns["lmagic_out"] == "my line"
710 assert _ip.user_ns["lmagic_out"] == "my line"
711 # cell mode test
711 # cell mode test
712 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
712 _ip.run_cell_magic("prun", "-q", "%lmagic my line2")
713 assert _ip.user_ns["lmagic_out"] == "my line2"
713 assert _ip.user_ns["lmagic_out"] == "my line2"
714
714
715
715
716 @dec.skipif(execution.profile is None)
716 @dec.skipif(execution.profile is None)
717 def test_prun_quotes():
717 def test_prun_quotes():
718 "Test that prun does not clobber string escapes (GH #1302)"
718 "Test that prun does not clobber string escapes (GH #1302)"
719 _ip.magic(r"prun -q x = '\t'")
719 _ip.magic(r"prun -q x = '\t'")
720 assert _ip.user_ns["x"] == "\t"
720 assert _ip.user_ns["x"] == "\t"
721
721
722
722
723 def test_extension():
723 def test_extension():
724 # Debugging information for failures of this test
724 # Debugging information for failures of this test
725 print('sys.path:')
725 print('sys.path:')
726 for p in sys.path:
726 for p in sys.path:
727 print(' ', p)
727 print(' ', p)
728 print('CWD', os.getcwd())
728 print('CWD', os.getcwd())
729
729
730 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
730 pytest.raises(ImportError, _ip.magic, "load_ext daft_extension")
731 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
731 daft_path = os.path.join(os.path.dirname(__file__), "daft_extension")
732 sys.path.insert(0, daft_path)
732 sys.path.insert(0, daft_path)
733 try:
733 try:
734 _ip.user_ns.pop('arq', None)
734 _ip.user_ns.pop('arq', None)
735 invalidate_caches() # Clear import caches
735 invalidate_caches() # Clear import caches
736 _ip.magic("load_ext daft_extension")
736 _ip.magic("load_ext daft_extension")
737 assert _ip.user_ns["arq"] == 185
737 assert _ip.user_ns["arq"] == 185
738 _ip.magic("unload_ext daft_extension")
738 _ip.magic("unload_ext daft_extension")
739 assert 'arq' not in _ip.user_ns
739 assert 'arq' not in _ip.user_ns
740 finally:
740 finally:
741 sys.path.remove(daft_path)
741 sys.path.remove(daft_path)
742
742
743
743
744 def test_notebook_export_json():
744 def test_notebook_export_json():
745 pytest.importorskip("nbformat")
745 pytest.importorskip("nbformat")
746 _ip = get_ipython()
746 _ip = get_ipython()
747 _ip.history_manager.reset() # Clear any existing history.
747 _ip.history_manager.reset() # Clear any existing history.
748 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
748 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
749 for i, cmd in enumerate(cmds, start=1):
749 for i, cmd in enumerate(cmds, start=1):
750 _ip.history_manager.store_inputs(i, cmd)
750 _ip.history_manager.store_inputs(i, cmd)
751 with TemporaryDirectory() as td:
751 with TemporaryDirectory() as td:
752 outfile = os.path.join(td, "nb.ipynb")
752 outfile = os.path.join(td, "nb.ipynb")
753 _ip.magic("notebook -e %s" % outfile)
753 _ip.magic("notebook -e %s" % outfile)
754
754
755
755
756 class TestEnv(TestCase):
756 class TestEnv(TestCase):
757
757
758 def test_env(self):
758 def test_env(self):
759 env = _ip.magic("env")
759 env = _ip.magic("env")
760 self.assertTrue(isinstance(env, dict))
760 self.assertTrue(isinstance(env, dict))
761
761
762 def test_env_secret(self):
762 def test_env_secret(self):
763 env = _ip.magic("env")
763 env = _ip.magic("env")
764 hidden = "<hidden>"
764 hidden = "<hidden>"
765 with mock.patch.dict(
765 with mock.patch.dict(
766 os.environ,
766 os.environ,
767 {
767 {
768 "API_KEY": "abc123",
768 "API_KEY": "abc123",
769 "SECRET_THING": "ssshhh",
769 "SECRET_THING": "ssshhh",
770 "JUPYTER_TOKEN": "",
770 "JUPYTER_TOKEN": "",
771 "VAR": "abc"
771 "VAR": "abc"
772 }
772 }
773 ):
773 ):
774 env = _ip.magic("env")
774 env = _ip.magic("env")
775 assert env["API_KEY"] == hidden
775 assert env["API_KEY"] == hidden
776 assert env["SECRET_THING"] == hidden
776 assert env["SECRET_THING"] == hidden
777 assert env["JUPYTER_TOKEN"] == hidden
777 assert env["JUPYTER_TOKEN"] == hidden
778 assert env["VAR"] == "abc"
778 assert env["VAR"] == "abc"
779
779
780 def test_env_get_set_simple(self):
780 def test_env_get_set_simple(self):
781 env = _ip.magic("env var val1")
781 env = _ip.magic("env var val1")
782 self.assertEqual(env, None)
782 self.assertEqual(env, None)
783 self.assertEqual(os.environ['var'], 'val1')
783 self.assertEqual(os.environ['var'], 'val1')
784 self.assertEqual(_ip.magic("env var"), 'val1')
784 self.assertEqual(_ip.magic("env var"), 'val1')
785 env = _ip.magic("env var=val2")
785 env = _ip.magic("env var=val2")
786 self.assertEqual(env, None)
786 self.assertEqual(env, None)
787 self.assertEqual(os.environ['var'], 'val2')
787 self.assertEqual(os.environ['var'], 'val2')
788
788
789 def test_env_get_set_complex(self):
789 def test_env_get_set_complex(self):
790 env = _ip.magic("env var 'val1 '' 'val2")
790 env = _ip.magic("env var 'val1 '' 'val2")
791 self.assertEqual(env, None)
791 self.assertEqual(env, None)
792 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
792 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
793 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
793 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
794 env = _ip.magic('env var=val2 val3="val4')
794 env = _ip.magic('env var=val2 val3="val4')
795 self.assertEqual(env, None)
795 self.assertEqual(env, None)
796 self.assertEqual(os.environ['var'], 'val2 val3="val4')
796 self.assertEqual(os.environ['var'], 'val2 val3="val4')
797
797
798 def test_env_set_bad_input(self):
798 def test_env_set_bad_input(self):
799 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
799 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
800
800
801 def test_env_set_whitespace(self):
801 def test_env_set_whitespace(self):
802 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
802 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
803
803
804
804
805 class CellMagicTestCase(TestCase):
805 class CellMagicTestCase(TestCase):
806
806
807 def check_ident(self, magic):
807 def check_ident(self, magic):
808 # Manually called, we get the result
808 # Manually called, we get the result
809 out = _ip.run_cell_magic(magic, "a", "b")
809 out = _ip.run_cell_magic(magic, "a", "b")
810 assert out == ("a", "b")
810 assert out == ("a", "b")
811 # Via run_cell, it goes into the user's namespace via displayhook
811 # Via run_cell, it goes into the user's namespace via displayhook
812 _ip.run_cell("%%" + magic + " c\nd\n")
812 _ip.run_cell("%%" + magic + " c\nd\n")
813 assert _ip.user_ns["_"] == ("c", "d\n")
813 assert _ip.user_ns["_"] == ("c", "d\n")
814
814
815 def test_cell_magic_func_deco(self):
815 def test_cell_magic_func_deco(self):
816 "Cell magic using simple decorator"
816 "Cell magic using simple decorator"
817 @register_cell_magic
817 @register_cell_magic
818 def cellm(line, cell):
818 def cellm(line, cell):
819 return line, cell
819 return line, cell
820
820
821 self.check_ident('cellm')
821 self.check_ident('cellm')
822
822
823 def test_cell_magic_reg(self):
823 def test_cell_magic_reg(self):
824 "Cell magic manually registered"
824 "Cell magic manually registered"
825 def cellm(line, cell):
825 def cellm(line, cell):
826 return line, cell
826 return line, cell
827
827
828 _ip.register_magic_function(cellm, 'cell', 'cellm2')
828 _ip.register_magic_function(cellm, 'cell', 'cellm2')
829 self.check_ident('cellm2')
829 self.check_ident('cellm2')
830
830
831 def test_cell_magic_class(self):
831 def test_cell_magic_class(self):
832 "Cell magics declared via a class"
832 "Cell magics declared via a class"
833 @magics_class
833 @magics_class
834 class MyMagics(Magics):
834 class MyMagics(Magics):
835
835
836 @cell_magic
836 @cell_magic
837 def cellm3(self, line, cell):
837 def cellm3(self, line, cell):
838 return line, cell
838 return line, cell
839
839
840 _ip.register_magics(MyMagics)
840 _ip.register_magics(MyMagics)
841 self.check_ident('cellm3')
841 self.check_ident('cellm3')
842
842
843 def test_cell_magic_class2(self):
843 def test_cell_magic_class2(self):
844 "Cell magics declared via a class, #2"
844 "Cell magics declared via a class, #2"
845 @magics_class
845 @magics_class
846 class MyMagics2(Magics):
846 class MyMagics2(Magics):
847
847
848 @cell_magic('cellm4')
848 @cell_magic('cellm4')
849 def cellm33(self, line, cell):
849 def cellm33(self, line, cell):
850 return line, cell
850 return line, cell
851
851
852 _ip.register_magics(MyMagics2)
852 _ip.register_magics(MyMagics2)
853 self.check_ident('cellm4')
853 self.check_ident('cellm4')
854 # Check that nothing is registered as 'cellm33'
854 # Check that nothing is registered as 'cellm33'
855 c33 = _ip.find_cell_magic('cellm33')
855 c33 = _ip.find_cell_magic('cellm33')
856 assert c33 == None
856 assert c33 == None
857
857
858 def test_file():
858 def test_file():
859 """Basic %%writefile"""
859 """Basic %%writefile"""
860 ip = get_ipython()
860 ip = get_ipython()
861 with TemporaryDirectory() as td:
861 with TemporaryDirectory() as td:
862 fname = os.path.join(td, 'file1')
862 fname = os.path.join(td, 'file1')
863 ip.run_cell_magic("writefile", fname, u'\n'.join([
863 ip.run_cell_magic("writefile", fname, u'\n'.join([
864 'line1',
864 'line1',
865 'line2',
865 'line2',
866 ]))
866 ]))
867 s = Path(fname).read_text()
867 s = Path(fname).read_text()
868 assert "line1\n" in s
868 assert "line1\n" in s
869 assert "line2" in s
869 assert "line2" in s
870
870
871
871
872 @dec.skip_win32
872 @dec.skip_win32
873 def test_file_single_quote():
873 def test_file_single_quote():
874 """Basic %%writefile with embedded single quotes"""
874 """Basic %%writefile with embedded single quotes"""
875 ip = get_ipython()
875 ip = get_ipython()
876 with TemporaryDirectory() as td:
876 with TemporaryDirectory() as td:
877 fname = os.path.join(td, '\'file1\'')
877 fname = os.path.join(td, '\'file1\'')
878 ip.run_cell_magic("writefile", fname, u'\n'.join([
878 ip.run_cell_magic("writefile", fname, u'\n'.join([
879 'line1',
879 'line1',
880 'line2',
880 'line2',
881 ]))
881 ]))
882 s = Path(fname).read_text()
882 s = Path(fname).read_text()
883 assert "line1\n" in s
883 assert "line1\n" in s
884 assert "line2" in s
884 assert "line2" in s
885
885
886
886
887 @dec.skip_win32
887 @dec.skip_win32
888 def test_file_double_quote():
888 def test_file_double_quote():
889 """Basic %%writefile with embedded double quotes"""
889 """Basic %%writefile with embedded double quotes"""
890 ip = get_ipython()
890 ip = get_ipython()
891 with TemporaryDirectory() as td:
891 with TemporaryDirectory() as td:
892 fname = os.path.join(td, '"file1"')
892 fname = os.path.join(td, '"file1"')
893 ip.run_cell_magic("writefile", fname, u'\n'.join([
893 ip.run_cell_magic("writefile", fname, u'\n'.join([
894 'line1',
894 'line1',
895 'line2',
895 'line2',
896 ]))
896 ]))
897 s = Path(fname).read_text()
897 s = Path(fname).read_text()
898 assert "line1\n" in s
898 assert "line1\n" in s
899 assert "line2" in s
899 assert "line2" in s
900
900
901
901
902 def test_file_var_expand():
902 def test_file_var_expand():
903 """%%writefile $filename"""
903 """%%writefile $filename"""
904 ip = get_ipython()
904 ip = get_ipython()
905 with TemporaryDirectory() as td:
905 with TemporaryDirectory() as td:
906 fname = os.path.join(td, 'file1')
906 fname = os.path.join(td, 'file1')
907 ip.user_ns['filename'] = fname
907 ip.user_ns['filename'] = fname
908 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
908 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
909 'line1',
909 'line1',
910 'line2',
910 'line2',
911 ]))
911 ]))
912 s = Path(fname).read_text()
912 s = Path(fname).read_text()
913 assert "line1\n" in s
913 assert "line1\n" in s
914 assert "line2" in s
914 assert "line2" in s
915
915
916
916
917 def test_file_unicode():
917 def test_file_unicode():
918 """%%writefile with unicode cell"""
918 """%%writefile with unicode cell"""
919 ip = get_ipython()
919 ip = get_ipython()
920 with TemporaryDirectory() as td:
920 with TemporaryDirectory() as td:
921 fname = os.path.join(td, 'file1')
921 fname = os.path.join(td, 'file1')
922 ip.run_cell_magic("writefile", fname, u'\n'.join([
922 ip.run_cell_magic("writefile", fname, u'\n'.join([
923 u'linΓ©1',
923 u'linΓ©1',
924 u'linΓ©2',
924 u'linΓ©2',
925 ]))
925 ]))
926 with io.open(fname, encoding='utf-8') as f:
926 with io.open(fname, encoding='utf-8') as f:
927 s = f.read()
927 s = f.read()
928 assert "linΓ©1\n" in s
928 assert "linΓ©1\n" in s
929 assert "linΓ©2" in s
929 assert "linΓ©2" in s
930
930
931
931
932 def test_file_amend():
932 def test_file_amend():
933 """%%writefile -a amends files"""
933 """%%writefile -a amends files"""
934 ip = get_ipython()
934 ip = get_ipython()
935 with TemporaryDirectory() as td:
935 with TemporaryDirectory() as td:
936 fname = os.path.join(td, 'file2')
936 fname = os.path.join(td, 'file2')
937 ip.run_cell_magic("writefile", fname, u'\n'.join([
937 ip.run_cell_magic("writefile", fname, u'\n'.join([
938 'line1',
938 'line1',
939 'line2',
939 'line2',
940 ]))
940 ]))
941 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
941 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
942 'line3',
942 'line3',
943 'line4',
943 'line4',
944 ]))
944 ]))
945 s = Path(fname).read_text()
945 s = Path(fname).read_text()
946 assert "line1\n" in s
946 assert "line1\n" in s
947 assert "line3\n" in s
947 assert "line3\n" in s
948
948
949
949
950 def test_file_spaces():
950 def test_file_spaces():
951 """%%file with spaces in filename"""
951 """%%file with spaces in filename"""
952 ip = get_ipython()
952 ip = get_ipython()
953 with TemporaryWorkingDirectory() as td:
953 with TemporaryWorkingDirectory() as td:
954 fname = "file name"
954 fname = "file name"
955 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
955 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
956 'line1',
956 'line1',
957 'line2',
957 'line2',
958 ]))
958 ]))
959 s = Path(fname).read_text()
959 s = Path(fname).read_text()
960 assert "line1\n" in s
960 assert "line1\n" in s
961 assert "line2" in s
961 assert "line2" in s
962
962
963
963
964 def test_script_config():
964 def test_script_config():
965 ip = get_ipython()
965 ip = get_ipython()
966 ip.config.ScriptMagics.script_magics = ['whoda']
966 ip.config.ScriptMagics.script_magics = ['whoda']
967 sm = script.ScriptMagics(shell=ip)
967 sm = script.ScriptMagics(shell=ip)
968 assert "whoda" in sm.magics["cell"]
968 assert "whoda" in sm.magics["cell"]
969
969
970
970
971 @pytest.fixture
971 @pytest.fixture
972 def event_loop():
972 def event_loop():
973 yield asyncio.get_event_loop_policy().get_event_loop()
973 policy = asyncio.get_event_loop_policy()
974 loop = policy.new_event_loop()
975 policy.set_event_loop(loop)
976 yield loop
977 loop.close()
974
978
975
979
976 @dec.skip_win32
980 @dec.skip_win32
977 @pytest.mark.skipif(
981 @pytest.mark.skipif(
978 sys.platform == "win32", reason="This test does not run under Windows"
982 sys.platform == "win32", reason="This test does not run under Windows"
979 )
983 )
980 def test_script_out(event_loop):
984 def test_script_out(event_loop):
981 assert event_loop.is_running() is False
985 assert event_loop.is_running() is False
982
986
983 ip = get_ipython()
987 ip = get_ipython()
984 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
988 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
985 assert event_loop.is_running() is False
989 assert event_loop.is_running() is False
986 assert ip.user_ns["output"] == "hi\n"
990 assert ip.user_ns["output"] == "hi\n"
987
991
988
992
989 @dec.skip_win32
993 @dec.skip_win32
990 @pytest.mark.skipif(
994 @pytest.mark.skipif(
991 sys.platform == "win32", reason="This test does not run under Windows"
995 sys.platform == "win32", reason="This test does not run under Windows"
992 )
996 )
993 def test_script_err(event_loop):
997 def test_script_err(event_loop):
994 ip = get_ipython()
998 ip = get_ipython()
995 assert event_loop.is_running() is False
999 assert event_loop.is_running() is False
996 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
1000 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
997 assert event_loop.is_running() is False
1001 assert event_loop.is_running() is False
998 assert ip.user_ns["error"] == "hello\n"
1002 assert ip.user_ns["error"] == "hello\n"
999
1003
1000
1004
1001 @dec.skip_win32
1005 @dec.skip_win32
1002 @pytest.mark.skipif(
1006 @pytest.mark.skipif(
1003 sys.platform == "win32", reason="This test does not run under Windows"
1007 sys.platform == "win32", reason="This test does not run under Windows"
1004 )
1008 )
1005 def test_script_out_err():
1009 def test_script_out_err():
1006
1010
1007 ip = get_ipython()
1011 ip = get_ipython()
1008 ip.run_cell_magic(
1012 ip.run_cell_magic(
1009 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1013 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1010 )
1014 )
1011 assert ip.user_ns["output"] == "hi\n"
1015 assert ip.user_ns["output"] == "hi\n"
1012 assert ip.user_ns["error"] == "hello\n"
1016 assert ip.user_ns["error"] == "hello\n"
1013
1017
1014
1018
1015 @dec.skip_win32
1019 @dec.skip_win32
1016 @pytest.mark.skipif(
1020 @pytest.mark.skipif(
1017 sys.platform == "win32", reason="This test does not run under Windows"
1021 sys.platform == "win32", reason="This test does not run under Windows"
1018 )
1022 )
1019 async def test_script_bg_out(event_loop):
1023 async def test_script_bg_out(event_loop):
1020 ip = get_ipython()
1024 ip = get_ipython()
1021 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1025 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1022 assert (await ip.user_ns["output"].read()) == b"hi\n"
1026 assert (await ip.user_ns["output"].read()) == b"hi\n"
1023 ip.user_ns["output"].close()
1027 ip.user_ns["output"].close()
1024 event_loop.stop()
1028 event_loop.stop()
1025
1029
1026
1030
1027 @dec.skip_win32
1031 @dec.skip_win32
1028 @pytest.mark.skipif(
1032 @pytest.mark.skipif(
1029 sys.platform == "win32", reason="This test does not run under Windows"
1033 sys.platform == "win32", reason="This test does not run under Windows"
1030 )
1034 )
1031 async def test_script_bg_err():
1035 async def test_script_bg_err():
1032 ip = get_ipython()
1036 ip = get_ipython()
1033 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1037 ip.run_cell_magic("script", "--bg --err error sh", "echo 'hello' >&2")
1034 assert (await ip.user_ns["error"].read()) == b"hello\n"
1038 assert (await ip.user_ns["error"].read()) == b"hello\n"
1035 ip.user_ns["error"].close()
1039 ip.user_ns["error"].close()
1036
1040
1037
1041
1038 @dec.skip_win32
1042 @dec.skip_win32
1039 @pytest.mark.skipif(
1043 @pytest.mark.skipif(
1040 sys.platform == "win32", reason="This test does not run under Windows"
1044 sys.platform == "win32", reason="This test does not run under Windows"
1041 )
1045 )
1042 async def test_script_bg_out_err():
1046 async def test_script_bg_out_err():
1043 ip = get_ipython()
1047 ip = get_ipython()
1044 ip.run_cell_magic(
1048 ip.run_cell_magic(
1045 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1049 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1046 )
1050 )
1047 assert (await ip.user_ns["output"].read()) == b"hi\n"
1051 assert (await ip.user_ns["output"].read()) == b"hi\n"
1048 assert (await ip.user_ns["error"].read()) == b"hello\n"
1052 assert (await ip.user_ns["error"].read()) == b"hello\n"
1049 ip.user_ns["output"].close()
1053 ip.user_ns["output"].close()
1050 ip.user_ns["error"].close()
1054 ip.user_ns["error"].close()
1051
1055
1052
1056
1053 def test_script_defaults():
1057 def test_script_defaults():
1054 ip = get_ipython()
1058 ip = get_ipython()
1055 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1059 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1056 try:
1060 try:
1057 find_cmd(cmd)
1061 find_cmd(cmd)
1058 except Exception:
1062 except Exception:
1059 pass
1063 pass
1060 else:
1064 else:
1061 assert cmd in ip.magics_manager.magics["cell"]
1065 assert cmd in ip.magics_manager.magics["cell"]
1062
1066
1063
1067
1064 @magics_class
1068 @magics_class
1065 class FooFoo(Magics):
1069 class FooFoo(Magics):
1066 """class with both %foo and %%foo magics"""
1070 """class with both %foo and %%foo magics"""
1067 @line_magic('foo')
1071 @line_magic('foo')
1068 def line_foo(self, line):
1072 def line_foo(self, line):
1069 "I am line foo"
1073 "I am line foo"
1070 pass
1074 pass
1071
1075
1072 @cell_magic("foo")
1076 @cell_magic("foo")
1073 def cell_foo(self, line, cell):
1077 def cell_foo(self, line, cell):
1074 "I am cell foo, not line foo"
1078 "I am cell foo, not line foo"
1075 pass
1079 pass
1076
1080
1077 def test_line_cell_info():
1081 def test_line_cell_info():
1078 """%%foo and %foo magics are distinguishable to inspect"""
1082 """%%foo and %foo magics are distinguishable to inspect"""
1079 ip = get_ipython()
1083 ip = get_ipython()
1080 ip.magics_manager.register(FooFoo)
1084 ip.magics_manager.register(FooFoo)
1081 oinfo = ip.object_inspect("foo")
1085 oinfo = ip.object_inspect("foo")
1082 assert oinfo["found"] is True
1086 assert oinfo["found"] is True
1083 assert oinfo["ismagic"] is True
1087 assert oinfo["ismagic"] is True
1084
1088
1085 oinfo = ip.object_inspect("%%foo")
1089 oinfo = ip.object_inspect("%%foo")
1086 assert oinfo["found"] is True
1090 assert oinfo["found"] is True
1087 assert oinfo["ismagic"] is True
1091 assert oinfo["ismagic"] is True
1088 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1092 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1089
1093
1090 oinfo = ip.object_inspect("%foo")
1094 oinfo = ip.object_inspect("%foo")
1091 assert oinfo["found"] is True
1095 assert oinfo["found"] is True
1092 assert oinfo["ismagic"] is True
1096 assert oinfo["ismagic"] is True
1093 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1097 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1094
1098
1095
1099
1096 def test_multiple_magics():
1100 def test_multiple_magics():
1097 ip = get_ipython()
1101 ip = get_ipython()
1098 foo1 = FooFoo(ip)
1102 foo1 = FooFoo(ip)
1099 foo2 = FooFoo(ip)
1103 foo2 = FooFoo(ip)
1100 mm = ip.magics_manager
1104 mm = ip.magics_manager
1101 mm.register(foo1)
1105 mm.register(foo1)
1102 assert mm.magics["line"]["foo"].__self__ is foo1
1106 assert mm.magics["line"]["foo"].__self__ is foo1
1103 mm.register(foo2)
1107 mm.register(foo2)
1104 assert mm.magics["line"]["foo"].__self__ is foo2
1108 assert mm.magics["line"]["foo"].__self__ is foo2
1105
1109
1106
1110
1107 def test_alias_magic():
1111 def test_alias_magic():
1108 """Test %alias_magic."""
1112 """Test %alias_magic."""
1109 ip = get_ipython()
1113 ip = get_ipython()
1110 mm = ip.magics_manager
1114 mm = ip.magics_manager
1111
1115
1112 # Basic operation: both cell and line magics are created, if possible.
1116 # Basic operation: both cell and line magics are created, if possible.
1113 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1117 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1114 assert "timeit_alias" in mm.magics["line"]
1118 assert "timeit_alias" in mm.magics["line"]
1115 assert "timeit_alias" in mm.magics["cell"]
1119 assert "timeit_alias" in mm.magics["cell"]
1116
1120
1117 # --cell is specified, line magic not created.
1121 # --cell is specified, line magic not created.
1118 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1122 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1119 assert "timeit_cell_alias" not in mm.magics["line"]
1123 assert "timeit_cell_alias" not in mm.magics["line"]
1120 assert "timeit_cell_alias" in mm.magics["cell"]
1124 assert "timeit_cell_alias" in mm.magics["cell"]
1121
1125
1122 # Test that line alias is created successfully.
1126 # Test that line alias is created successfully.
1123 ip.run_line_magic("alias_magic", "--line env_alias env")
1127 ip.run_line_magic("alias_magic", "--line env_alias env")
1124 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1128 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1125
1129
1126 # Test that line alias with parameters passed in is created successfully.
1130 # Test that line alias with parameters passed in is created successfully.
1127 ip.run_line_magic(
1131 ip.run_line_magic(
1128 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1132 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1129 )
1133 )
1130 assert "history_alias" in mm.magics["line"]
1134 assert "history_alias" in mm.magics["line"]
1131
1135
1132
1136
1133 def test_save():
1137 def test_save():
1134 """Test %save."""
1138 """Test %save."""
1135 ip = get_ipython()
1139 ip = get_ipython()
1136 ip.history_manager.reset() # Clear any existing history.
1140 ip.history_manager.reset() # Clear any existing history.
1137 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1141 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1138 for i, cmd in enumerate(cmds, start=1):
1142 for i, cmd in enumerate(cmds, start=1):
1139 ip.history_manager.store_inputs(i, cmd)
1143 ip.history_manager.store_inputs(i, cmd)
1140 with TemporaryDirectory() as tmpdir:
1144 with TemporaryDirectory() as tmpdir:
1141 file = os.path.join(tmpdir, "testsave.py")
1145 file = os.path.join(tmpdir, "testsave.py")
1142 ip.run_line_magic("save", "%s 1-10" % file)
1146 ip.run_line_magic("save", "%s 1-10" % file)
1143 content = Path(file).read_text()
1147 content = Path(file).read_text()
1144 assert content.count(cmds[0]) == 1
1148 assert content.count(cmds[0]) == 1
1145 assert "coding: utf-8" in content
1149 assert "coding: utf-8" in content
1146 ip.run_line_magic("save", "-a %s 1-10" % file)
1150 ip.run_line_magic("save", "-a %s 1-10" % file)
1147 content = Path(file).read_text()
1151 content = Path(file).read_text()
1148 assert content.count(cmds[0]) == 2
1152 assert content.count(cmds[0]) == 2
1149 assert "coding: utf-8" in content
1153 assert "coding: utf-8" in content
1150
1154
1151
1155
1152 def test_save_with_no_args():
1156 def test_save_with_no_args():
1153 ip = get_ipython()
1157 ip = get_ipython()
1154 ip.history_manager.reset() # Clear any existing history.
1158 ip.history_manager.reset() # Clear any existing history.
1155 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1159 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1156 for i, cmd in enumerate(cmds, start=1):
1160 for i, cmd in enumerate(cmds, start=1):
1157 ip.history_manager.store_inputs(i, cmd)
1161 ip.history_manager.store_inputs(i, cmd)
1158
1162
1159 with TemporaryDirectory() as tmpdir:
1163 with TemporaryDirectory() as tmpdir:
1160 path = os.path.join(tmpdir, "testsave.py")
1164 path = os.path.join(tmpdir, "testsave.py")
1161 ip.run_line_magic("save", path)
1165 ip.run_line_magic("save", path)
1162 content = Path(path).read_text()
1166 content = Path(path).read_text()
1163 expected_content = dedent(
1167 expected_content = dedent(
1164 """\
1168 """\
1165 # coding: utf-8
1169 # coding: utf-8
1166 a=1
1170 a=1
1167 def b():
1171 def b():
1168 return a**2
1172 return a**2
1169 print(a, b())
1173 print(a, b())
1170 """
1174 """
1171 )
1175 )
1172 assert content == expected_content
1176 assert content == expected_content
1173
1177
1174
1178
1175 def test_store():
1179 def test_store():
1176 """Test %store."""
1180 """Test %store."""
1177 ip = get_ipython()
1181 ip = get_ipython()
1178 ip.run_line_magic('load_ext', 'storemagic')
1182 ip.run_line_magic('load_ext', 'storemagic')
1179
1183
1180 # make sure the storage is empty
1184 # make sure the storage is empty
1181 ip.run_line_magic("store", "-z")
1185 ip.run_line_magic("store", "-z")
1182 ip.user_ns["var"] = 42
1186 ip.user_ns["var"] = 42
1183 ip.run_line_magic("store", "var")
1187 ip.run_line_magic("store", "var")
1184 ip.user_ns["var"] = 39
1188 ip.user_ns["var"] = 39
1185 ip.run_line_magic("store", "-r")
1189 ip.run_line_magic("store", "-r")
1186 assert ip.user_ns["var"] == 42
1190 assert ip.user_ns["var"] == 42
1187
1191
1188 ip.run_line_magic("store", "-d var")
1192 ip.run_line_magic("store", "-d var")
1189 ip.user_ns["var"] = 39
1193 ip.user_ns["var"] = 39
1190 ip.run_line_magic("store", "-r")
1194 ip.run_line_magic("store", "-r")
1191 assert ip.user_ns["var"] == 39
1195 assert ip.user_ns["var"] == 39
1192
1196
1193
1197
1194 def _run_edit_test(arg_s, exp_filename=None,
1198 def _run_edit_test(arg_s, exp_filename=None,
1195 exp_lineno=-1,
1199 exp_lineno=-1,
1196 exp_contents=None,
1200 exp_contents=None,
1197 exp_is_temp=None):
1201 exp_is_temp=None):
1198 ip = get_ipython()
1202 ip = get_ipython()
1199 M = code.CodeMagics(ip)
1203 M = code.CodeMagics(ip)
1200 last_call = ['','']
1204 last_call = ['','']
1201 opts,args = M.parse_options(arg_s,'prxn:')
1205 opts,args = M.parse_options(arg_s,'prxn:')
1202 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1206 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1203
1207
1204 if exp_filename is not None:
1208 if exp_filename is not None:
1205 assert exp_filename == filename
1209 assert exp_filename == filename
1206 if exp_contents is not None:
1210 if exp_contents is not None:
1207 with io.open(filename, 'r', encoding='utf-8') as f:
1211 with io.open(filename, 'r', encoding='utf-8') as f:
1208 contents = f.read()
1212 contents = f.read()
1209 assert exp_contents == contents
1213 assert exp_contents == contents
1210 if exp_lineno != -1:
1214 if exp_lineno != -1:
1211 assert exp_lineno == lineno
1215 assert exp_lineno == lineno
1212 if exp_is_temp is not None:
1216 if exp_is_temp is not None:
1213 assert exp_is_temp == is_temp
1217 assert exp_is_temp == is_temp
1214
1218
1215
1219
1216 def test_edit_interactive():
1220 def test_edit_interactive():
1217 """%edit on interactively defined objects"""
1221 """%edit on interactively defined objects"""
1218 ip = get_ipython()
1222 ip = get_ipython()
1219 n = ip.execution_count
1223 n = ip.execution_count
1220 ip.run_cell("def foo(): return 1", store_history=True)
1224 ip.run_cell("def foo(): return 1", store_history=True)
1221
1225
1222 with pytest.raises(code.InteractivelyDefined) as e:
1226 with pytest.raises(code.InteractivelyDefined) as e:
1223 _run_edit_test("foo")
1227 _run_edit_test("foo")
1224 assert e.value.index == n
1228 assert e.value.index == n
1225
1229
1226
1230
1227 def test_edit_cell():
1231 def test_edit_cell():
1228 """%edit [cell id]"""
1232 """%edit [cell id]"""
1229 ip = get_ipython()
1233 ip = get_ipython()
1230
1234
1231 ip.run_cell("def foo(): return 1", store_history=True)
1235 ip.run_cell("def foo(): return 1", store_history=True)
1232
1236
1233 # test
1237 # test
1234 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1238 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1235
1239
1236 def test_edit_fname():
1240 def test_edit_fname():
1237 """%edit file"""
1241 """%edit file"""
1238 # test
1242 # test
1239 _run_edit_test("test file.py", exp_filename="test file.py")
1243 _run_edit_test("test file.py", exp_filename="test file.py")
1240
1244
1241 def test_bookmark():
1245 def test_bookmark():
1242 ip = get_ipython()
1246 ip = get_ipython()
1243 ip.run_line_magic('bookmark', 'bmname')
1247 ip.run_line_magic('bookmark', 'bmname')
1244 with tt.AssertPrints('bmname'):
1248 with tt.AssertPrints('bmname'):
1245 ip.run_line_magic('bookmark', '-l')
1249 ip.run_line_magic('bookmark', '-l')
1246 ip.run_line_magic('bookmark', '-d bmname')
1250 ip.run_line_magic('bookmark', '-d bmname')
1247
1251
1248 def test_ls_magic():
1252 def test_ls_magic():
1249 ip = get_ipython()
1253 ip = get_ipython()
1250 json_formatter = ip.display_formatter.formatters['application/json']
1254 json_formatter = ip.display_formatter.formatters['application/json']
1251 json_formatter.enabled = True
1255 json_formatter.enabled = True
1252 lsmagic = ip.magic('lsmagic')
1256 lsmagic = ip.magic('lsmagic')
1253 with warnings.catch_warnings(record=True) as w:
1257 with warnings.catch_warnings(record=True) as w:
1254 j = json_formatter(lsmagic)
1258 j = json_formatter(lsmagic)
1255 assert sorted(j) == ["cell", "line"]
1259 assert sorted(j) == ["cell", "line"]
1256 assert w == [] # no warnings
1260 assert w == [] # no warnings
1257
1261
1258
1262
1259 def test_strip_initial_indent():
1263 def test_strip_initial_indent():
1260 def sii(s):
1264 def sii(s):
1261 lines = s.splitlines()
1265 lines = s.splitlines()
1262 return '\n'.join(code.strip_initial_indent(lines))
1266 return '\n'.join(code.strip_initial_indent(lines))
1263
1267
1264 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1268 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1265 assert sii(" a\n b\nc") == "a\n b\nc"
1269 assert sii(" a\n b\nc") == "a\n b\nc"
1266 assert sii("a\n b") == "a\n b"
1270 assert sii("a\n b") == "a\n b"
1267
1271
1268 def test_logging_magic_quiet_from_arg():
1272 def test_logging_magic_quiet_from_arg():
1269 _ip.config.LoggingMagics.quiet = False
1273 _ip.config.LoggingMagics.quiet = False
1270 lm = logging.LoggingMagics(shell=_ip)
1274 lm = logging.LoggingMagics(shell=_ip)
1271 with TemporaryDirectory() as td:
1275 with TemporaryDirectory() as td:
1272 try:
1276 try:
1273 with tt.AssertNotPrints(re.compile("Activating.*")):
1277 with tt.AssertNotPrints(re.compile("Activating.*")):
1274 lm.logstart('-q {}'.format(
1278 lm.logstart('-q {}'.format(
1275 os.path.join(td, "quiet_from_arg.log")))
1279 os.path.join(td, "quiet_from_arg.log")))
1276 finally:
1280 finally:
1277 _ip.logger.logstop()
1281 _ip.logger.logstop()
1278
1282
1279 def test_logging_magic_quiet_from_config():
1283 def test_logging_magic_quiet_from_config():
1280 _ip.config.LoggingMagics.quiet = True
1284 _ip.config.LoggingMagics.quiet = True
1281 lm = logging.LoggingMagics(shell=_ip)
1285 lm = logging.LoggingMagics(shell=_ip)
1282 with TemporaryDirectory() as td:
1286 with TemporaryDirectory() as td:
1283 try:
1287 try:
1284 with tt.AssertNotPrints(re.compile("Activating.*")):
1288 with tt.AssertNotPrints(re.compile("Activating.*")):
1285 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1289 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1286 finally:
1290 finally:
1287 _ip.logger.logstop()
1291 _ip.logger.logstop()
1288
1292
1289
1293
1290 def test_logging_magic_not_quiet():
1294 def test_logging_magic_not_quiet():
1291 _ip.config.LoggingMagics.quiet = False
1295 _ip.config.LoggingMagics.quiet = False
1292 lm = logging.LoggingMagics(shell=_ip)
1296 lm = logging.LoggingMagics(shell=_ip)
1293 with TemporaryDirectory() as td:
1297 with TemporaryDirectory() as td:
1294 try:
1298 try:
1295 with tt.AssertPrints(re.compile("Activating.*")):
1299 with tt.AssertPrints(re.compile("Activating.*")):
1296 lm.logstart(os.path.join(td, "not_quiet.log"))
1300 lm.logstart(os.path.join(td, "not_quiet.log"))
1297 finally:
1301 finally:
1298 _ip.logger.logstop()
1302 _ip.logger.logstop()
1299
1303
1300
1304
1301 def test_time_no_var_expand():
1305 def test_time_no_var_expand():
1302 _ip.user_ns['a'] = 5
1306 _ip.user_ns['a'] = 5
1303 _ip.user_ns['b'] = []
1307 _ip.user_ns['b'] = []
1304 _ip.magic('time b.append("{a}")')
1308 _ip.magic('time b.append("{a}")')
1305 assert _ip.user_ns['b'] == ['{a}']
1309 assert _ip.user_ns['b'] == ['{a}']
1306
1310
1307
1311
1308 # this is slow, put at the end for local testing.
1312 # this is slow, put at the end for local testing.
1309 def test_timeit_arguments():
1313 def test_timeit_arguments():
1310 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1314 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1311 _ip.magic("timeit -n1 -r1 a=('#')")
1315 _ip.magic("timeit -n1 -r1 a=('#')")
1312
1316
1313
1317
1314 TEST_MODULE = """
1318 TEST_MODULE = """
1315 print('Loaded my_tmp')
1319 print('Loaded my_tmp')
1316 if __name__ == "__main__":
1320 if __name__ == "__main__":
1317 print('I just ran a script')
1321 print('I just ran a script')
1318 """
1322 """
1319
1323
1320
1324
1321 def test_run_module_from_import_hook():
1325 def test_run_module_from_import_hook():
1322 "Test that a module can be loaded via an import hook"
1326 "Test that a module can be loaded via an import hook"
1323 with TemporaryDirectory() as tmpdir:
1327 with TemporaryDirectory() as tmpdir:
1324 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1328 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1325 Path(fullpath).write_text(TEST_MODULE)
1329 Path(fullpath).write_text(TEST_MODULE)
1326
1330
1327 import importlib.abc
1331 import importlib.abc
1328 import importlib.util
1332 import importlib.util
1329
1333
1330 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1334 class MyTempImporter(importlib.abc.MetaPathFinder, importlib.abc.SourceLoader):
1331 def find_spec(self, fullname, path, target=None):
1335 def find_spec(self, fullname, path, target=None):
1332 if fullname == "my_tmp":
1336 if fullname == "my_tmp":
1333 return importlib.util.spec_from_loader(fullname, self)
1337 return importlib.util.spec_from_loader(fullname, self)
1334
1338
1335 def get_filename(self, fullname):
1339 def get_filename(self, fullname):
1336 if fullname != "my_tmp":
1340 if fullname != "my_tmp":
1337 raise ImportError(f"unexpected module name '{fullname}'")
1341 raise ImportError(f"unexpected module name '{fullname}'")
1338 return fullpath
1342 return fullpath
1339
1343
1340 def get_data(self, path):
1344 def get_data(self, path):
1341 if not Path(path).samefile(fullpath):
1345 if not Path(path).samefile(fullpath):
1342 raise OSError(f"expected path '{fullpath}', got '{path}'")
1346 raise OSError(f"expected path '{fullpath}', got '{path}'")
1343 return Path(fullpath).read_text()
1347 return Path(fullpath).read_text()
1344
1348
1345 sys.meta_path.insert(0, MyTempImporter())
1349 sys.meta_path.insert(0, MyTempImporter())
1346
1350
1347 with capture_output() as captured:
1351 with capture_output() as captured:
1348 _ip.magic("run -m my_tmp")
1352 _ip.magic("run -m my_tmp")
1349 _ip.run_cell("import my_tmp")
1353 _ip.run_cell("import my_tmp")
1350
1354
1351 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1355 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1352 assert output == captured.stdout
1356 assert output == captured.stdout
1353
1357
1354 sys.meta_path.pop(0)
1358 sys.meta_path.pop(0)
@@ -1,695 +1,698 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import asyncio
3 import asyncio
4 import os
4 import os
5 import sys
5 import sys
6 import warnings
6 import warnings
7 from warnings import warn
7 from warnings import warn
8
8
9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
10 from IPython.utils import io
10 from IPython.utils import io
11 from IPython.utils.py3compat import input
11 from IPython.utils.py3compat import input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
13 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import (
14 from traitlets import (
15 Bool,
15 Bool,
16 Unicode,
16 Unicode,
17 Dict,
17 Dict,
18 Integer,
18 Integer,
19 observe,
19 observe,
20 Instance,
20 Instance,
21 Type,
21 Type,
22 default,
22 default,
23 Enum,
23 Enum,
24 Union,
24 Union,
25 Any,
25 Any,
26 validate,
26 validate,
27 Float,
27 Float,
28 )
28 )
29
29
30 from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
30 from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
31 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
31 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
32 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
32 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
33 from prompt_toolkit.formatted_text import PygmentsTokens
33 from prompt_toolkit.formatted_text import PygmentsTokens
34 from prompt_toolkit.history import InMemoryHistory
34 from prompt_toolkit.history import InMemoryHistory
35 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
35 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
36 from prompt_toolkit.output import ColorDepth
36 from prompt_toolkit.output import ColorDepth
37 from prompt_toolkit.patch_stdout import patch_stdout
37 from prompt_toolkit.patch_stdout import patch_stdout
38 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
38 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
39 from prompt_toolkit.styles import DynamicStyle, merge_styles
39 from prompt_toolkit.styles import DynamicStyle, merge_styles
40 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
40 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
41 from prompt_toolkit import __version__ as ptk_version
41 from prompt_toolkit import __version__ as ptk_version
42
42
43 from pygments.styles import get_style_by_name
43 from pygments.styles import get_style_by_name
44 from pygments.style import Style
44 from pygments.style import Style
45 from pygments.token import Token
45 from pygments.token import Token
46
46
47 from .debugger import TerminalPdb, Pdb
47 from .debugger import TerminalPdb, Pdb
48 from .magics import TerminalMagics
48 from .magics import TerminalMagics
49 from .pt_inputhooks import get_inputhook_name_and_func
49 from .pt_inputhooks import get_inputhook_name_and_func
50 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
50 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
51 from .ptutils import IPythonPTCompleter, IPythonPTLexer
51 from .ptutils import IPythonPTCompleter, IPythonPTLexer
52 from .shortcuts import create_ipython_shortcuts
52 from .shortcuts import create_ipython_shortcuts
53
53
54 DISPLAY_BANNER_DEPRECATED = object()
54 DISPLAY_BANNER_DEPRECATED = object()
55 PTK3 = ptk_version.startswith('3.')
55 PTK3 = ptk_version.startswith('3.')
56
56
57
57
58 class _NoStyle(Style): pass
58 class _NoStyle(Style): pass
59
59
60
60
61
61
62 _style_overrides_light_bg = {
62 _style_overrides_light_bg = {
63 Token.Prompt: '#ansibrightblue',
63 Token.Prompt: '#ansibrightblue',
64 Token.PromptNum: '#ansiblue bold',
64 Token.PromptNum: '#ansiblue bold',
65 Token.OutPrompt: '#ansibrightred',
65 Token.OutPrompt: '#ansibrightred',
66 Token.OutPromptNum: '#ansired bold',
66 Token.OutPromptNum: '#ansired bold',
67 }
67 }
68
68
69 _style_overrides_linux = {
69 _style_overrides_linux = {
70 Token.Prompt: '#ansibrightgreen',
70 Token.Prompt: '#ansibrightgreen',
71 Token.PromptNum: '#ansigreen bold',
71 Token.PromptNum: '#ansigreen bold',
72 Token.OutPrompt: '#ansibrightred',
72 Token.OutPrompt: '#ansibrightred',
73 Token.OutPromptNum: '#ansired bold',
73 Token.OutPromptNum: '#ansired bold',
74 }
74 }
75
75
76 def get_default_editor():
76 def get_default_editor():
77 try:
77 try:
78 return os.environ['EDITOR']
78 return os.environ['EDITOR']
79 except KeyError:
79 except KeyError:
80 pass
80 pass
81 except UnicodeError:
81 except UnicodeError:
82 warn("$EDITOR environment variable is not pure ASCII. Using platform "
82 warn("$EDITOR environment variable is not pure ASCII. Using platform "
83 "default editor.")
83 "default editor.")
84
84
85 if os.name == 'posix':
85 if os.name == 'posix':
86 return 'vi' # the only one guaranteed to be there!
86 return 'vi' # the only one guaranteed to be there!
87 else:
87 else:
88 return 'notepad' # same in Windows!
88 return 'notepad' # same in Windows!
89
89
90 # conservatively check for tty
90 # conservatively check for tty
91 # overridden streams can result in things like:
91 # overridden streams can result in things like:
92 # - sys.stdin = None
92 # - sys.stdin = None
93 # - no isatty method
93 # - no isatty method
94 for _name in ('stdin', 'stdout', 'stderr'):
94 for _name in ('stdin', 'stdout', 'stderr'):
95 _stream = getattr(sys, _name)
95 _stream = getattr(sys, _name)
96 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
96 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
97 _is_tty = False
97 _is_tty = False
98 break
98 break
99 else:
99 else:
100 _is_tty = True
100 _is_tty = True
101
101
102
102
103 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
103 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
104
104
105 def black_reformat_handler(text_before_cursor):
105 def black_reformat_handler(text_before_cursor):
106 import black
106 import black
107 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
107 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
108 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
108 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
109 formatted_text = formatted_text[:-1]
109 formatted_text = formatted_text[:-1]
110 return formatted_text
110 return formatted_text
111
111
112
112
113 class TerminalInteractiveShell(InteractiveShell):
113 class TerminalInteractiveShell(InteractiveShell):
114 mime_renderers = Dict().tag(config=True)
114 mime_renderers = Dict().tag(config=True)
115
115
116 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
116 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
117 'to reserve for the tab completion menu, '
117 'to reserve for the tab completion menu, '
118 'search history, ...etc, the height of '
118 'search history, ...etc, the height of '
119 'these menus will at most this value. '
119 'these menus will at most this value. '
120 'Increase it is you prefer long and skinny '
120 'Increase it is you prefer long and skinny '
121 'menus, decrease for short and wide.'
121 'menus, decrease for short and wide.'
122 ).tag(config=True)
122 ).tag(config=True)
123
123
124 pt_app = None
124 pt_app = None
125 debugger_history = None
125 debugger_history = None
126
126
127 debugger_history_file = Unicode(
127 debugger_history_file = Unicode(
128 "~/.pdbhistory", help="File in which to store and read history"
128 "~/.pdbhistory", help="File in which to store and read history"
129 ).tag(config=True)
129 ).tag(config=True)
130
130
131 simple_prompt = Bool(_use_simple_prompt,
131 simple_prompt = Bool(_use_simple_prompt,
132 help="""Use `raw_input` for the REPL, without completion and prompt colors.
132 help="""Use `raw_input` for the REPL, without completion and prompt colors.
133
133
134 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
134 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
135 IPython own testing machinery, and emacs inferior-shell integration through elpy.
135 IPython own testing machinery, and emacs inferior-shell integration through elpy.
136
136
137 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
137 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
138 environment variable is set, or the current terminal is not a tty."""
138 environment variable is set, or the current terminal is not a tty."""
139 ).tag(config=True)
139 ).tag(config=True)
140
140
141 @property
141 @property
142 def debugger_cls(self):
142 def debugger_cls(self):
143 return Pdb if self.simple_prompt else TerminalPdb
143 return Pdb if self.simple_prompt else TerminalPdb
144
144
145 confirm_exit = Bool(True,
145 confirm_exit = Bool(True,
146 help="""
146 help="""
147 Set to confirm when you try to exit IPython with an EOF (Control-D
147 Set to confirm when you try to exit IPython with an EOF (Control-D
148 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
148 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
149 you can force a direct exit without any confirmation.""",
149 you can force a direct exit without any confirmation.""",
150 ).tag(config=True)
150 ).tag(config=True)
151
151
152 editing_mode = Unicode('emacs',
152 editing_mode = Unicode('emacs',
153 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
153 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
154 ).tag(config=True)
154 ).tag(config=True)
155
155
156 emacs_bindings_in_vi_insert_mode = Bool(
156 emacs_bindings_in_vi_insert_mode = Bool(
157 True,
157 True,
158 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
158 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
159 ).tag(config=True)
159 ).tag(config=True)
160
160
161 modal_cursor = Bool(
161 modal_cursor = Bool(
162 True,
162 True,
163 help="""
163 help="""
164 Cursor shape changes depending on vi mode: beam in vi insert mode,
164 Cursor shape changes depending on vi mode: beam in vi insert mode,
165 block in nav mode, underscore in replace mode.""",
165 block in nav mode, underscore in replace mode.""",
166 ).tag(config=True)
166 ).tag(config=True)
167
167
168 ttimeoutlen = Float(
168 ttimeoutlen = Float(
169 0.01,
169 0.01,
170 help="""The time in milliseconds that is waited for a key code
170 help="""The time in milliseconds that is waited for a key code
171 to complete.""",
171 to complete.""",
172 ).tag(config=True)
172 ).tag(config=True)
173
173
174 timeoutlen = Float(
174 timeoutlen = Float(
175 0.5,
175 0.5,
176 help="""The time in milliseconds that is waited for a mapped key
176 help="""The time in milliseconds that is waited for a mapped key
177 sequence to complete.""",
177 sequence to complete.""",
178 ).tag(config=True)
178 ).tag(config=True)
179
179
180 autoformatter = Unicode(None,
180 autoformatter = Unicode(None,
181 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
181 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
182 allow_none=True
182 allow_none=True
183 ).tag(config=True)
183 ).tag(config=True)
184
184
185 mouse_support = Bool(False,
185 mouse_support = Bool(False,
186 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
186 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
187 ).tag(config=True)
187 ).tag(config=True)
188
188
189 # We don't load the list of styles for the help string, because loading
189 # We don't load the list of styles for the help string, because loading
190 # Pygments plugins takes time and can cause unexpected errors.
190 # Pygments plugins takes time and can cause unexpected errors.
191 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
191 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
192 help="""The name or class of a Pygments style to use for syntax
192 help="""The name or class of a Pygments style to use for syntax
193 highlighting. To see available styles, run `pygmentize -L styles`."""
193 highlighting. To see available styles, run `pygmentize -L styles`."""
194 ).tag(config=True)
194 ).tag(config=True)
195
195
196 @validate('editing_mode')
196 @validate('editing_mode')
197 def _validate_editing_mode(self, proposal):
197 def _validate_editing_mode(self, proposal):
198 if proposal['value'].lower() == 'vim':
198 if proposal['value'].lower() == 'vim':
199 proposal['value']= 'vi'
199 proposal['value']= 'vi'
200 elif proposal['value'].lower() == 'default':
200 elif proposal['value'].lower() == 'default':
201 proposal['value']= 'emacs'
201 proposal['value']= 'emacs'
202
202
203 if hasattr(EditingMode, proposal['value'].upper()):
203 if hasattr(EditingMode, proposal['value'].upper()):
204 return proposal['value'].lower()
204 return proposal['value'].lower()
205
205
206 return self.editing_mode
206 return self.editing_mode
207
207
208
208
209 @observe('editing_mode')
209 @observe('editing_mode')
210 def _editing_mode(self, change):
210 def _editing_mode(self, change):
211 if self.pt_app:
211 if self.pt_app:
212 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
212 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
213
213
214 @observe('autoformatter')
214 @observe('autoformatter')
215 def _autoformatter_changed(self, change):
215 def _autoformatter_changed(self, change):
216 formatter = change.new
216 formatter = change.new
217 if formatter is None:
217 if formatter is None:
218 self.reformat_handler = lambda x:x
218 self.reformat_handler = lambda x:x
219 elif formatter == 'black':
219 elif formatter == 'black':
220 self.reformat_handler = black_reformat_handler
220 self.reformat_handler = black_reformat_handler
221 else:
221 else:
222 raise ValueError
222 raise ValueError
223
223
224 @observe('highlighting_style')
224 @observe('highlighting_style')
225 @observe('colors')
225 @observe('colors')
226 def _highlighting_style_changed(self, change):
226 def _highlighting_style_changed(self, change):
227 self.refresh_style()
227 self.refresh_style()
228
228
229 def refresh_style(self):
229 def refresh_style(self):
230 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
230 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
231
231
232
232
233 highlighting_style_overrides = Dict(
233 highlighting_style_overrides = Dict(
234 help="Override highlighting format for specific tokens"
234 help="Override highlighting format for specific tokens"
235 ).tag(config=True)
235 ).tag(config=True)
236
236
237 true_color = Bool(False,
237 true_color = Bool(False,
238 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
238 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
239 If your terminal supports true color, the following command should
239 If your terminal supports true color, the following command should
240 print ``TRUECOLOR`` in orange::
240 print ``TRUECOLOR`` in orange::
241
241
242 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
242 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
243 """,
243 """,
244 ).tag(config=True)
244 ).tag(config=True)
245
245
246 editor = Unicode(get_default_editor(),
246 editor = Unicode(get_default_editor(),
247 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
247 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
248 ).tag(config=True)
248 ).tag(config=True)
249
249
250 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
250 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
251
251
252 prompts = Instance(Prompts)
252 prompts = Instance(Prompts)
253
253
254 @default('prompts')
254 @default('prompts')
255 def _prompts_default(self):
255 def _prompts_default(self):
256 return self.prompts_class(self)
256 return self.prompts_class(self)
257
257
258 # @observe('prompts')
258 # @observe('prompts')
259 # def _(self, change):
259 # def _(self, change):
260 # self._update_layout()
260 # self._update_layout()
261
261
262 @default('displayhook_class')
262 @default('displayhook_class')
263 def _displayhook_class_default(self):
263 def _displayhook_class_default(self):
264 return RichPromptDisplayHook
264 return RichPromptDisplayHook
265
265
266 term_title = Bool(True,
266 term_title = Bool(True,
267 help="Automatically set the terminal title"
267 help="Automatically set the terminal title"
268 ).tag(config=True)
268 ).tag(config=True)
269
269
270 term_title_format = Unicode("IPython: {cwd}",
270 term_title_format = Unicode("IPython: {cwd}",
271 help="Customize the terminal title format. This is a python format string. " +
271 help="Customize the terminal title format. This is a python format string. " +
272 "Available substitutions are: {cwd}."
272 "Available substitutions are: {cwd}."
273 ).tag(config=True)
273 ).tag(config=True)
274
274
275 display_completions = Enum(('column', 'multicolumn','readlinelike'),
275 display_completions = Enum(('column', 'multicolumn','readlinelike'),
276 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
276 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
277 "'readlinelike'. These options are for `prompt_toolkit`, see "
277 "'readlinelike'. These options are for `prompt_toolkit`, see "
278 "`prompt_toolkit` documentation for more information."
278 "`prompt_toolkit` documentation for more information."
279 ),
279 ),
280 default_value='multicolumn').tag(config=True)
280 default_value='multicolumn').tag(config=True)
281
281
282 highlight_matching_brackets = Bool(True,
282 highlight_matching_brackets = Bool(True,
283 help="Highlight matching brackets.",
283 help="Highlight matching brackets.",
284 ).tag(config=True)
284 ).tag(config=True)
285
285
286 extra_open_editor_shortcuts = Bool(False,
286 extra_open_editor_shortcuts = Bool(False,
287 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
287 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
288 "This is in addition to the F2 binding, which is always enabled."
288 "This is in addition to the F2 binding, which is always enabled."
289 ).tag(config=True)
289 ).tag(config=True)
290
290
291 handle_return = Any(None,
291 handle_return = Any(None,
292 help="Provide an alternative handler to be called when the user presses "
292 help="Provide an alternative handler to be called when the user presses "
293 "Return. This is an advanced option intended for debugging, which "
293 "Return. This is an advanced option intended for debugging, which "
294 "may be changed or removed in later releases."
294 "may be changed or removed in later releases."
295 ).tag(config=True)
295 ).tag(config=True)
296
296
297 enable_history_search = Bool(True,
297 enable_history_search = Bool(True,
298 help="Allows to enable/disable the prompt toolkit history search"
298 help="Allows to enable/disable the prompt toolkit history search"
299 ).tag(config=True)
299 ).tag(config=True)
300
300
301 prompt_includes_vi_mode = Bool(True,
301 prompt_includes_vi_mode = Bool(True,
302 help="Display the current vi mode (when using vi editing mode)."
302 help="Display the current vi mode (when using vi editing mode)."
303 ).tag(config=True)
303 ).tag(config=True)
304
304
305 @observe('term_title')
305 @observe('term_title')
306 def init_term_title(self, change=None):
306 def init_term_title(self, change=None):
307 # Enable or disable the terminal title.
307 # Enable or disable the terminal title.
308 if self.term_title:
308 if self.term_title:
309 toggle_set_term_title(True)
309 toggle_set_term_title(True)
310 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
310 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
311 else:
311 else:
312 toggle_set_term_title(False)
312 toggle_set_term_title(False)
313
313
314 def restore_term_title(self):
314 def restore_term_title(self):
315 if self.term_title:
315 if self.term_title:
316 restore_term_title()
316 restore_term_title()
317
317
318 def init_display_formatter(self):
318 def init_display_formatter(self):
319 super(TerminalInteractiveShell, self).init_display_formatter()
319 super(TerminalInteractiveShell, self).init_display_formatter()
320 # terminal only supports plain text
320 # terminal only supports plain text
321 self.display_formatter.active_types = ['text/plain']
321 self.display_formatter.active_types = ['text/plain']
322 # disable `_ipython_display_`
322 # disable `_ipython_display_`
323 self.display_formatter.ipython_display_formatter.enabled = False
323 self.display_formatter.ipython_display_formatter.enabled = False
324
324
325 def init_prompt_toolkit_cli(self):
325 def init_prompt_toolkit_cli(self):
326 if self.simple_prompt:
326 if self.simple_prompt:
327 # Fall back to plain non-interactive output for tests.
327 # Fall back to plain non-interactive output for tests.
328 # This is very limited.
328 # This is very limited.
329 def prompt():
329 def prompt():
330 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
330 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
331 lines = [input(prompt_text)]
331 lines = [input(prompt_text)]
332 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
332 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
333 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
333 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
334 lines.append( input(prompt_continuation) )
334 lines.append( input(prompt_continuation) )
335 return '\n'.join(lines)
335 return '\n'.join(lines)
336 self.prompt_for_code = prompt
336 self.prompt_for_code = prompt
337 return
337 return
338
338
339 # Set up keyboard shortcuts
339 # Set up keyboard shortcuts
340 key_bindings = create_ipython_shortcuts(self)
340 key_bindings = create_ipython_shortcuts(self)
341
341
342 # Pre-populate history from IPython's history database
342 # Pre-populate history from IPython's history database
343 history = InMemoryHistory()
343 history = InMemoryHistory()
344 last_cell = u""
344 last_cell = u""
345 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
345 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
346 include_latest=True):
346 include_latest=True):
347 # Ignore blank lines and consecutive duplicates
347 # Ignore blank lines and consecutive duplicates
348 cell = cell.rstrip()
348 cell = cell.rstrip()
349 if cell and (cell != last_cell):
349 if cell and (cell != last_cell):
350 history.append_string(cell)
350 history.append_string(cell)
351 last_cell = cell
351 last_cell = cell
352
352
353 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
353 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
354 self.style = DynamicStyle(lambda: self._style)
354 self.style = DynamicStyle(lambda: self._style)
355
355
356 editing_mode = getattr(EditingMode, self.editing_mode.upper())
356 editing_mode = getattr(EditingMode, self.editing_mode.upper())
357
357
358 self.pt_loop = asyncio.new_event_loop()
358 self.pt_loop = asyncio.new_event_loop()
359 self.pt_app = PromptSession(
359 self.pt_app = PromptSession(
360 auto_suggest=AutoSuggestFromHistory(),
360 auto_suggest=AutoSuggestFromHistory(),
361 editing_mode=editing_mode,
361 editing_mode=editing_mode,
362 key_bindings=key_bindings,
362 key_bindings=key_bindings,
363 history=history,
363 history=history,
364 completer=IPythonPTCompleter(shell=self),
364 completer=IPythonPTCompleter(shell=self),
365 enable_history_search=self.enable_history_search,
365 enable_history_search=self.enable_history_search,
366 style=self.style,
366 style=self.style,
367 include_default_pygments_style=False,
367 include_default_pygments_style=False,
368 mouse_support=self.mouse_support,
368 mouse_support=self.mouse_support,
369 enable_open_in_editor=self.extra_open_editor_shortcuts,
369 enable_open_in_editor=self.extra_open_editor_shortcuts,
370 color_depth=self.color_depth,
370 color_depth=self.color_depth,
371 tempfile_suffix=".py",
371 tempfile_suffix=".py",
372 **self._extra_prompt_options()
372 **self._extra_prompt_options()
373 )
373 )
374
374
375 def _make_style_from_name_or_cls(self, name_or_cls):
375 def _make_style_from_name_or_cls(self, name_or_cls):
376 """
376 """
377 Small wrapper that make an IPython compatible style from a style name
377 Small wrapper that make an IPython compatible style from a style name
378
378
379 We need that to add style for prompt ... etc.
379 We need that to add style for prompt ... etc.
380 """
380 """
381 style_overrides = {}
381 style_overrides = {}
382 if name_or_cls == 'legacy':
382 if name_or_cls == 'legacy':
383 legacy = self.colors.lower()
383 legacy = self.colors.lower()
384 if legacy == 'linux':
384 if legacy == 'linux':
385 style_cls = get_style_by_name('monokai')
385 style_cls = get_style_by_name('monokai')
386 style_overrides = _style_overrides_linux
386 style_overrides = _style_overrides_linux
387 elif legacy == 'lightbg':
387 elif legacy == 'lightbg':
388 style_overrides = _style_overrides_light_bg
388 style_overrides = _style_overrides_light_bg
389 style_cls = get_style_by_name('pastie')
389 style_cls = get_style_by_name('pastie')
390 elif legacy == 'neutral':
390 elif legacy == 'neutral':
391 # The default theme needs to be visible on both a dark background
391 # The default theme needs to be visible on both a dark background
392 # and a light background, because we can't tell what the terminal
392 # and a light background, because we can't tell what the terminal
393 # looks like. These tweaks to the default theme help with that.
393 # looks like. These tweaks to the default theme help with that.
394 style_cls = get_style_by_name('default')
394 style_cls = get_style_by_name('default')
395 style_overrides.update({
395 style_overrides.update({
396 Token.Number: '#ansigreen',
396 Token.Number: '#ansigreen',
397 Token.Operator: 'noinherit',
397 Token.Operator: 'noinherit',
398 Token.String: '#ansiyellow',
398 Token.String: '#ansiyellow',
399 Token.Name.Function: '#ansiblue',
399 Token.Name.Function: '#ansiblue',
400 Token.Name.Class: 'bold #ansiblue',
400 Token.Name.Class: 'bold #ansiblue',
401 Token.Name.Namespace: 'bold #ansiblue',
401 Token.Name.Namespace: 'bold #ansiblue',
402 Token.Name.Variable.Magic: '#ansiblue',
402 Token.Name.Variable.Magic: '#ansiblue',
403 Token.Prompt: '#ansigreen',
403 Token.Prompt: '#ansigreen',
404 Token.PromptNum: '#ansibrightgreen bold',
404 Token.PromptNum: '#ansibrightgreen bold',
405 Token.OutPrompt: '#ansired',
405 Token.OutPrompt: '#ansired',
406 Token.OutPromptNum: '#ansibrightred bold',
406 Token.OutPromptNum: '#ansibrightred bold',
407 })
407 })
408
408
409 # Hack: Due to limited color support on the Windows console
409 # Hack: Due to limited color support on the Windows console
410 # the prompt colors will be wrong without this
410 # the prompt colors will be wrong without this
411 if os.name == 'nt':
411 if os.name == 'nt':
412 style_overrides.update({
412 style_overrides.update({
413 Token.Prompt: '#ansidarkgreen',
413 Token.Prompt: '#ansidarkgreen',
414 Token.PromptNum: '#ansigreen bold',
414 Token.PromptNum: '#ansigreen bold',
415 Token.OutPrompt: '#ansidarkred',
415 Token.OutPrompt: '#ansidarkred',
416 Token.OutPromptNum: '#ansired bold',
416 Token.OutPromptNum: '#ansired bold',
417 })
417 })
418 elif legacy =='nocolor':
418 elif legacy =='nocolor':
419 style_cls=_NoStyle
419 style_cls=_NoStyle
420 style_overrides = {}
420 style_overrides = {}
421 else :
421 else :
422 raise ValueError('Got unknown colors: ', legacy)
422 raise ValueError('Got unknown colors: ', legacy)
423 else :
423 else :
424 if isinstance(name_or_cls, str):
424 if isinstance(name_or_cls, str):
425 style_cls = get_style_by_name(name_or_cls)
425 style_cls = get_style_by_name(name_or_cls)
426 else:
426 else:
427 style_cls = name_or_cls
427 style_cls = name_or_cls
428 style_overrides = {
428 style_overrides = {
429 Token.Prompt: '#ansigreen',
429 Token.Prompt: '#ansigreen',
430 Token.PromptNum: '#ansibrightgreen bold',
430 Token.PromptNum: '#ansibrightgreen bold',
431 Token.OutPrompt: '#ansired',
431 Token.OutPrompt: '#ansired',
432 Token.OutPromptNum: '#ansibrightred bold',
432 Token.OutPromptNum: '#ansibrightred bold',
433 }
433 }
434 style_overrides.update(self.highlighting_style_overrides)
434 style_overrides.update(self.highlighting_style_overrides)
435 style = merge_styles([
435 style = merge_styles([
436 style_from_pygments_cls(style_cls),
436 style_from_pygments_cls(style_cls),
437 style_from_pygments_dict(style_overrides),
437 style_from_pygments_dict(style_overrides),
438 ])
438 ])
439
439
440 return style
440 return style
441
441
442 @property
442 @property
443 def pt_complete_style(self):
443 def pt_complete_style(self):
444 return {
444 return {
445 'multicolumn': CompleteStyle.MULTI_COLUMN,
445 'multicolumn': CompleteStyle.MULTI_COLUMN,
446 'column': CompleteStyle.COLUMN,
446 'column': CompleteStyle.COLUMN,
447 'readlinelike': CompleteStyle.READLINE_LIKE,
447 'readlinelike': CompleteStyle.READLINE_LIKE,
448 }[self.display_completions]
448 }[self.display_completions]
449
449
450 @property
450 @property
451 def color_depth(self):
451 def color_depth(self):
452 return (ColorDepth.TRUE_COLOR if self.true_color else None)
452 return (ColorDepth.TRUE_COLOR if self.true_color else None)
453
453
454 def _extra_prompt_options(self):
454 def _extra_prompt_options(self):
455 """
455 """
456 Return the current layout option for the current Terminal InteractiveShell
456 Return the current layout option for the current Terminal InteractiveShell
457 """
457 """
458 def get_message():
458 def get_message():
459 return PygmentsTokens(self.prompts.in_prompt_tokens())
459 return PygmentsTokens(self.prompts.in_prompt_tokens())
460
460
461 if self.editing_mode == 'emacs':
461 if self.editing_mode == 'emacs':
462 # with emacs mode the prompt is (usually) static, so we call only
462 # with emacs mode the prompt is (usually) static, so we call only
463 # the function once. With VI mode it can toggle between [ins] and
463 # the function once. With VI mode it can toggle between [ins] and
464 # [nor] so we can't precompute.
464 # [nor] so we can't precompute.
465 # here I'm going to favor the default keybinding which almost
465 # here I'm going to favor the default keybinding which almost
466 # everybody uses to decrease CPU usage.
466 # everybody uses to decrease CPU usage.
467 # if we have issues with users with custom Prompts we can see how to
467 # if we have issues with users with custom Prompts we can see how to
468 # work around this.
468 # work around this.
469 get_message = get_message()
469 get_message = get_message()
470
470
471 options = {
471 options = {
472 'complete_in_thread': False,
472 'complete_in_thread': False,
473 'lexer':IPythonPTLexer(),
473 'lexer':IPythonPTLexer(),
474 'reserve_space_for_menu':self.space_for_menu,
474 'reserve_space_for_menu':self.space_for_menu,
475 'message': get_message,
475 'message': get_message,
476 'prompt_continuation': (
476 'prompt_continuation': (
477 lambda width, lineno, is_soft_wrap:
477 lambda width, lineno, is_soft_wrap:
478 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
478 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
479 'multiline': True,
479 'multiline': True,
480 'complete_style': self.pt_complete_style,
480 'complete_style': self.pt_complete_style,
481
481
482 # Highlight matching brackets, but only when this setting is
482 # Highlight matching brackets, but only when this setting is
483 # enabled, and only when the DEFAULT_BUFFER has the focus.
483 # enabled, and only when the DEFAULT_BUFFER has the focus.
484 'input_processors': [ConditionalProcessor(
484 'input_processors': [ConditionalProcessor(
485 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
485 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
486 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
486 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
487 Condition(lambda: self.highlight_matching_brackets))],
487 Condition(lambda: self.highlight_matching_brackets))],
488 }
488 }
489 if not PTK3:
489 if not PTK3:
490 options['inputhook'] = self.inputhook
490 options['inputhook'] = self.inputhook
491
491
492 return options
492 return options
493
493
494 def prompt_for_code(self):
494 def prompt_for_code(self):
495 if self.rl_next_input:
495 if self.rl_next_input:
496 default = self.rl_next_input
496 default = self.rl_next_input
497 self.rl_next_input = None
497 self.rl_next_input = None
498 else:
498 else:
499 default = ''
499 default = ''
500
500
501 # In order to make sure that asyncio code written in the
501 # In order to make sure that asyncio code written in the
502 # interactive shell doesn't interfere with the prompt, we run the
502 # interactive shell doesn't interfere with the prompt, we run the
503 # prompt in a different event loop.
503 # prompt in a different event loop.
504 # If we don't do this, people could spawn coroutine with a
504 # If we don't do this, people could spawn coroutine with a
505 # while/true inside which will freeze the prompt.
505 # while/true inside which will freeze the prompt.
506
506
507 policy = asyncio.get_event_loop_policy()
507 try:
508 try:
508 old_loop = asyncio.get_running_loop()
509 old_loop = policy.get_event_loop()
509 except RuntimeError:
510 except RuntimeError:
510 # This happens when the user used `asyncio.run()`.
511 # This happens when the the event loop is closed,
512 # e.g. by calling `asyncio.run()`.
511 old_loop = None
513 old_loop = None
512
514
513 asyncio.set_event_loop(self.pt_loop)
515 policy.set_event_loop(self.pt_loop)
514 try:
516 try:
515 with patch_stdout(raw=True):
517 with patch_stdout(raw=True):
516 text = self.pt_app.prompt(
518 text = self.pt_app.prompt(
517 default=default,
519 default=default,
518 **self._extra_prompt_options())
520 **self._extra_prompt_options())
519 finally:
521 finally:
520 # Restore the original event loop.
522 # Restore the original event loop.
521 asyncio.set_event_loop(old_loop)
523 if old_loop is not None:
524 policy.set_event_loop(old_loop)
522
525
523 return text
526 return text
524
527
525 def enable_win_unicode_console(self):
528 def enable_win_unicode_console(self):
526 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
529 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
527 # console by default, so WUC shouldn't be needed.
530 # console by default, so WUC shouldn't be needed.
528 from warnings import warn
531 from warnings import warn
529 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
532 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
530 DeprecationWarning,
533 DeprecationWarning,
531 stacklevel=2)
534 stacklevel=2)
532
535
533 def init_io(self):
536 def init_io(self):
534 if sys.platform not in {'win32', 'cli'}:
537 if sys.platform not in {'win32', 'cli'}:
535 return
538 return
536
539
537 import colorama
540 import colorama
538 colorama.init()
541 colorama.init()
539
542
540 # For some reason we make these wrappers around stdout/stderr.
543 # For some reason we make these wrappers around stdout/stderr.
541 # For now, we need to reset them so all output gets coloured.
544 # For now, we need to reset them so all output gets coloured.
542 # https://github.com/ipython/ipython/issues/8669
545 # https://github.com/ipython/ipython/issues/8669
543 # io.std* are deprecated, but don't show our own deprecation warnings
546 # io.std* are deprecated, but don't show our own deprecation warnings
544 # during initialization of the deprecated API.
547 # during initialization of the deprecated API.
545 with warnings.catch_warnings():
548 with warnings.catch_warnings():
546 warnings.simplefilter('ignore', DeprecationWarning)
549 warnings.simplefilter('ignore', DeprecationWarning)
547 io.stdout = io.IOStream(sys.stdout)
550 io.stdout = io.IOStream(sys.stdout)
548 io.stderr = io.IOStream(sys.stderr)
551 io.stderr = io.IOStream(sys.stderr)
549
552
550 def init_magics(self):
553 def init_magics(self):
551 super(TerminalInteractiveShell, self).init_magics()
554 super(TerminalInteractiveShell, self).init_magics()
552 self.register_magics(TerminalMagics)
555 self.register_magics(TerminalMagics)
553
556
554 def init_alias(self):
557 def init_alias(self):
555 # The parent class defines aliases that can be safely used with any
558 # The parent class defines aliases that can be safely used with any
556 # frontend.
559 # frontend.
557 super(TerminalInteractiveShell, self).init_alias()
560 super(TerminalInteractiveShell, self).init_alias()
558
561
559 # Now define aliases that only make sense on the terminal, because they
562 # Now define aliases that only make sense on the terminal, because they
560 # need direct access to the console in a way that we can't emulate in
563 # need direct access to the console in a way that we can't emulate in
561 # GUI or web frontend
564 # GUI or web frontend
562 if os.name == 'posix':
565 if os.name == 'posix':
563 for cmd in ('clear', 'more', 'less', 'man'):
566 for cmd in ('clear', 'more', 'less', 'man'):
564 self.alias_manager.soft_define_alias(cmd, cmd)
567 self.alias_manager.soft_define_alias(cmd, cmd)
565
568
566
569
567 def __init__(self, *args, **kwargs):
570 def __init__(self, *args, **kwargs):
568 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
571 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
569 self.init_prompt_toolkit_cli()
572 self.init_prompt_toolkit_cli()
570 self.init_term_title()
573 self.init_term_title()
571 self.keep_running = True
574 self.keep_running = True
572
575
573
576
574 def ask_exit(self):
577 def ask_exit(self):
575 self.keep_running = False
578 self.keep_running = False
576
579
577 rl_next_input = None
580 rl_next_input = None
578
581
579 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
582 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
580
583
581 if display_banner is not DISPLAY_BANNER_DEPRECATED:
584 if display_banner is not DISPLAY_BANNER_DEPRECATED:
582 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
585 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
583
586
584 self.keep_running = True
587 self.keep_running = True
585 while self.keep_running:
588 while self.keep_running:
586 print(self.separate_in, end='')
589 print(self.separate_in, end='')
587
590
588 try:
591 try:
589 code = self.prompt_for_code()
592 code = self.prompt_for_code()
590 except EOFError:
593 except EOFError:
591 if (not self.confirm_exit) \
594 if (not self.confirm_exit) \
592 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
595 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
593 self.ask_exit()
596 self.ask_exit()
594
597
595 else:
598 else:
596 if code:
599 if code:
597 self.run_cell(code, store_history=True)
600 self.run_cell(code, store_history=True)
598
601
599 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
602 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
600 # An extra layer of protection in case someone mashing Ctrl-C breaks
603 # An extra layer of protection in case someone mashing Ctrl-C breaks
601 # out of our internal code.
604 # out of our internal code.
602 if display_banner is not DISPLAY_BANNER_DEPRECATED:
605 if display_banner is not DISPLAY_BANNER_DEPRECATED:
603 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
606 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
604 while True:
607 while True:
605 try:
608 try:
606 self.interact()
609 self.interact()
607 break
610 break
608 except KeyboardInterrupt as e:
611 except KeyboardInterrupt as e:
609 print("\n%s escaped interact()\n" % type(e).__name__)
612 print("\n%s escaped interact()\n" % type(e).__name__)
610 finally:
613 finally:
611 # An interrupt during the eventloop will mess up the
614 # An interrupt during the eventloop will mess up the
612 # internal state of the prompt_toolkit library.
615 # internal state of the prompt_toolkit library.
613 # Stopping the eventloop fixes this, see
616 # Stopping the eventloop fixes this, see
614 # https://github.com/ipython/ipython/pull/9867
617 # https://github.com/ipython/ipython/pull/9867
615 if hasattr(self, '_eventloop'):
618 if hasattr(self, '_eventloop'):
616 self._eventloop.stop()
619 self._eventloop.stop()
617
620
618 self.restore_term_title()
621 self.restore_term_title()
619
622
620 # try to call some at-exit operation optimistically as some things can't
623 # try to call some at-exit operation optimistically as some things can't
621 # be done during interpreter shutdown. this is technically inaccurate as
624 # be done during interpreter shutdown. this is technically inaccurate as
622 # this make mainlool not re-callable, but that should be a rare if not
625 # this make mainlool not re-callable, but that should be a rare if not
623 # in existent use case.
626 # in existent use case.
624
627
625 self._atexit_once()
628 self._atexit_once()
626
629
627
630
628 _inputhook = None
631 _inputhook = None
629 def inputhook(self, context):
632 def inputhook(self, context):
630 if self._inputhook is not None:
633 if self._inputhook is not None:
631 self._inputhook(context)
634 self._inputhook(context)
632
635
633 active_eventloop = None
636 active_eventloop = None
634 def enable_gui(self, gui=None):
637 def enable_gui(self, gui=None):
635 if gui and (gui != 'inline') :
638 if gui and (gui != 'inline') :
636 self.active_eventloop, self._inputhook =\
639 self.active_eventloop, self._inputhook =\
637 get_inputhook_name_and_func(gui)
640 get_inputhook_name_and_func(gui)
638 else:
641 else:
639 self.active_eventloop = self._inputhook = None
642 self.active_eventloop = self._inputhook = None
640
643
641 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
644 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
642 # this inputhook.
645 # this inputhook.
643 if PTK3:
646 if PTK3:
644 import asyncio
647 import asyncio
645 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
648 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
646
649
647 if gui == 'asyncio':
650 if gui == 'asyncio':
648 # When we integrate the asyncio event loop, run the UI in the
651 # When we integrate the asyncio event loop, run the UI in the
649 # same event loop as the rest of the code. don't use an actual
652 # same event loop as the rest of the code. don't use an actual
650 # input hook. (Asyncio is not made for nesting event loops.)
653 # input hook. (Asyncio is not made for nesting event loops.)
651 self.pt_loop = asyncio.get_event_loop_policy().get_event_loop()
654 self.pt_loop = asyncio.get_event_loop_policy().get_event_loop()
652
655
653 elif self._inputhook:
656 elif self._inputhook:
654 # If an inputhook was set, create a new asyncio event loop with
657 # If an inputhook was set, create a new asyncio event loop with
655 # this inputhook for the prompt.
658 # this inputhook for the prompt.
656 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
659 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
657 else:
660 else:
658 # When there's no inputhook, run the prompt in a separate
661 # When there's no inputhook, run the prompt in a separate
659 # asyncio event loop.
662 # asyncio event loop.
660 self.pt_loop = asyncio.new_event_loop()
663 self.pt_loop = asyncio.new_event_loop()
661
664
662 # Run !system commands directly, not through pipes, so terminal programs
665 # Run !system commands directly, not through pipes, so terminal programs
663 # work correctly.
666 # work correctly.
664 system = InteractiveShell.system_raw
667 system = InteractiveShell.system_raw
665
668
666 def auto_rewrite_input(self, cmd):
669 def auto_rewrite_input(self, cmd):
667 """Overridden from the parent class to use fancy rewriting prompt"""
670 """Overridden from the parent class to use fancy rewriting prompt"""
668 if not self.show_rewritten_input:
671 if not self.show_rewritten_input:
669 return
672 return
670
673
671 tokens = self.prompts.rewrite_prompt_tokens()
674 tokens = self.prompts.rewrite_prompt_tokens()
672 if self.pt_app:
675 if self.pt_app:
673 print_formatted_text(PygmentsTokens(tokens), end='',
676 print_formatted_text(PygmentsTokens(tokens), end='',
674 style=self.pt_app.app.style)
677 style=self.pt_app.app.style)
675 print(cmd)
678 print(cmd)
676 else:
679 else:
677 prompt = ''.join(s for t, s in tokens)
680 prompt = ''.join(s for t, s in tokens)
678 print(prompt, cmd, sep='')
681 print(prompt, cmd, sep='')
679
682
680 _prompts_before = None
683 _prompts_before = None
681 def switch_doctest_mode(self, mode):
684 def switch_doctest_mode(self, mode):
682 """Switch prompts to classic for %doctest_mode"""
685 """Switch prompts to classic for %doctest_mode"""
683 if mode:
686 if mode:
684 self._prompts_before = self.prompts
687 self._prompts_before = self.prompts
685 self.prompts = ClassicPrompts(self)
688 self.prompts = ClassicPrompts(self)
686 elif self._prompts_before:
689 elif self._prompts_before:
687 self.prompts = self._prompts_before
690 self.prompts = self._prompts_before
688 self._prompts_before = None
691 self._prompts_before = None
689 # self._update_layout()
692 # self._update_layout()
690
693
691
694
692 InteractiveShellABC.register(TerminalInteractiveShell)
695 InteractiveShellABC.register(TerminalInteractiveShell)
693
696
694 if __name__ == '__main__':
697 if __name__ == '__main__':
695 TerminalInteractiveShell.instance().interact()
698 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now