##// END OF EJS Templates
Switch out off deprecated `asyncio.get_event_loop` call
Nikita Kniazev -
Show More
@@ -1,172 +1,172 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 sys
15 import sys
16 import inspect
16 import inspect
17 from textwrap import dedent, indent
17 from textwrap import dedent, indent
18
18
19
19
20 class _AsyncIORunner:
20 class _AsyncIORunner:
21
21
22 def __call__(self, coro):
22 def __call__(self, coro):
23 """
23 """
24 Handler for asyncio autoawait
24 Handler for asyncio autoawait
25 """
25 """
26 import asyncio
26 import asyncio
27
27
28 return asyncio.get_event_loop().run_until_complete(coro)
28 return asyncio.get_event_loop_policy().get_event_loop().run_until_complete(coro)
29
29
30 def __str__(self):
30 def __str__(self):
31 return 'asyncio'
31 return 'asyncio'
32
32
33 _asyncio_runner = _AsyncIORunner()
33 _asyncio_runner = _AsyncIORunner()
34
34
35
35
36 def _curio_runner(coroutine):
36 def _curio_runner(coroutine):
37 """
37 """
38 handler for curio autoawait
38 handler for curio autoawait
39 """
39 """
40 import curio
40 import curio
41
41
42 return curio.run(coroutine)
42 return curio.run(coroutine)
43
43
44
44
45 def _trio_runner(async_fn):
45 def _trio_runner(async_fn):
46 import trio
46 import trio
47
47
48 async def loc(coro):
48 async def loc(coro):
49 """
49 """
50 We need the dummy no-op async def to protect from
50 We need the dummy no-op async def to protect from
51 trio's internal. See https://github.com/python-trio/trio/issues/89
51 trio's internal. See https://github.com/python-trio/trio/issues/89
52 """
52 """
53 return await coro
53 return await coro
54
54
55 return trio.run(loc, async_fn)
55 return trio.run(loc, async_fn)
56
56
57
57
58 def _pseudo_sync_runner(coro):
58 def _pseudo_sync_runner(coro):
59 """
59 """
60 A runner that does not really allow async execution, and just advance the coroutine.
60 A runner that does not really allow async execution, and just advance the coroutine.
61
61
62 See discussion in https://github.com/python-trio/trio/issues/608,
62 See discussion in https://github.com/python-trio/trio/issues/608,
63
63
64 Credit to Nathaniel Smith
64 Credit to Nathaniel Smith
65 """
65 """
66 try:
66 try:
67 coro.send(None)
67 coro.send(None)
68 except StopIteration as exc:
68 except StopIteration as exc:
69 return exc.value
69 return exc.value
70 else:
70 else:
71 # TODO: do not raise but return an execution result with the right info.
71 # TODO: do not raise but return an execution result with the right info.
72 raise RuntimeError(
72 raise RuntimeError(
73 "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__)
73 "{coro_name!r} needs a real async loop".format(coro_name=coro.__name__)
74 )
74 )
75
75
76
76
77 def _asyncify(code: str) -> str:
77 def _asyncify(code: str) -> str:
78 """wrap code in async def definition.
78 """wrap code in async def definition.
79
79
80 And setup a bit of context to run it later.
80 And setup a bit of context to run it later.
81 """
81 """
82 res = dedent(
82 res = dedent(
83 """
83 """
84 async def __wrapper__():
84 async def __wrapper__():
85 try:
85 try:
86 {usercode}
86 {usercode}
87 finally:
87 finally:
88 locals()
88 locals()
89 """
89 """
90 ).format(usercode=indent(code, " " * 8))
90 ).format(usercode=indent(code, " " * 8))
91 return res
91 return res
92
92
93
93
94 class _AsyncSyntaxErrorVisitor(ast.NodeVisitor):
94 class _AsyncSyntaxErrorVisitor(ast.NodeVisitor):
95 """
95 """
96 Find syntax errors that would be an error in an async repl, but because
96 Find syntax errors that would be an error in an async repl, but because
97 the implementation involves wrapping the repl in an async function, it
97 the implementation involves wrapping the repl in an async function, it
98 is erroneously allowed (e.g. yield or return at the top level)
98 is erroneously allowed (e.g. yield or return at the top level)
99 """
99 """
100 def __init__(self):
100 def __init__(self):
101 if sys.version_info >= (3,8):
101 if sys.version_info >= (3,8):
102 raise ValueError('DEPRECATED in Python 3.8+')
102 raise ValueError('DEPRECATED in Python 3.8+')
103 self.depth = 0
103 self.depth = 0
104 super().__init__()
104 super().__init__()
105
105
106 def generic_visit(self, node):
106 def generic_visit(self, node):
107 func_types = (ast.FunctionDef, ast.AsyncFunctionDef)
107 func_types = (ast.FunctionDef, ast.AsyncFunctionDef)
108 invalid_types_by_depth = {
108 invalid_types_by_depth = {
109 0: (ast.Return, ast.Yield, ast.YieldFrom),
109 0: (ast.Return, ast.Yield, ast.YieldFrom),
110 1: (ast.Nonlocal,)
110 1: (ast.Nonlocal,)
111 }
111 }
112
112
113 should_traverse = self.depth < max(invalid_types_by_depth.keys())
113 should_traverse = self.depth < max(invalid_types_by_depth.keys())
114 if isinstance(node, func_types) and should_traverse:
114 if isinstance(node, func_types) and should_traverse:
115 self.depth += 1
115 self.depth += 1
116 super().generic_visit(node)
116 super().generic_visit(node)
117 self.depth -= 1
117 self.depth -= 1
118 elif isinstance(node, invalid_types_by_depth[self.depth]):
118 elif isinstance(node, invalid_types_by_depth[self.depth]):
119 raise SyntaxError()
119 raise SyntaxError()
120 else:
120 else:
121 super().generic_visit(node)
121 super().generic_visit(node)
122
122
123
123
124 def _async_parse_cell(cell: str) -> ast.AST:
124 def _async_parse_cell(cell: str) -> ast.AST:
125 """
125 """
126 This is a compatibility shim for pre-3.7 when async outside of a function
126 This is a compatibility shim for pre-3.7 when async outside of a function
127 is a syntax error at the parse stage.
127 is a syntax error at the parse stage.
128
128
129 It will return an abstract syntax tree parsed as if async and await outside
129 It will return an abstract syntax tree parsed as if async and await outside
130 of a function were not a syntax error.
130 of a function were not a syntax error.
131 """
131 """
132 if sys.version_info < (3, 7):
132 if sys.version_info < (3, 7):
133 # Prior to 3.7 you need to asyncify before parse
133 # Prior to 3.7 you need to asyncify before parse
134 wrapped_parse_tree = ast.parse(_asyncify(cell))
134 wrapped_parse_tree = ast.parse(_asyncify(cell))
135 return wrapped_parse_tree.body[0].body[0]
135 return wrapped_parse_tree.body[0].body[0]
136 else:
136 else:
137 return ast.parse(cell)
137 return ast.parse(cell)
138
138
139
139
140 def _should_be_async(cell: str) -> bool:
140 def _should_be_async(cell: str) -> bool:
141 """Detect if a block of code need to be wrapped in an `async def`
141 """Detect if a block of code need to be wrapped in an `async def`
142
142
143 Attempt to parse the block of code, it it compile we're fine.
143 Attempt to parse the block of code, it it compile we're fine.
144 Otherwise we wrap if and try to compile.
144 Otherwise we wrap if and try to compile.
145
145
146 If it works, assume it should be async. Otherwise Return False.
146 If it works, assume it should be async. Otherwise Return False.
147
147
148 Not handled yet: If the block of code has a return statement as the top
148 Not handled yet: If the block of code has a return statement as the top
149 level, it will be seen as async. This is a know limitation.
149 level, it will be seen as async. This is a know limitation.
150 """
150 """
151 if sys.version_info > (3, 8):
151 if sys.version_info > (3, 8):
152 try:
152 try:
153 code = compile(cell, "<>", "exec", flags=getattr(ast,'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0))
153 code = compile(cell, "<>", "exec", flags=getattr(ast,'PyCF_ALLOW_TOP_LEVEL_AWAIT', 0x0))
154 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
154 return inspect.CO_COROUTINE & code.co_flags == inspect.CO_COROUTINE
155 except (SyntaxError, MemoryError):
155 except (SyntaxError, MemoryError):
156 return False
156 return False
157 try:
157 try:
158 # we can't limit ourself to ast.parse, as it __accepts__ to parse on
158 # we can't limit ourself to ast.parse, as it __accepts__ to parse on
159 # 3.7+, but just does not _compile_
159 # 3.7+, but just does not _compile_
160 code = compile(cell, "<>", "exec")
160 code = compile(cell, "<>", "exec")
161 except (SyntaxError, MemoryError):
161 except (SyntaxError, MemoryError):
162 try:
162 try:
163 parse_tree = _async_parse_cell(cell)
163 parse_tree = _async_parse_cell(cell)
164
164
165 # Raise a SyntaxError if there are top-level return or yields
165 # Raise a SyntaxError if there are top-level return or yields
166 v = _AsyncSyntaxErrorVisitor()
166 v = _AsyncSyntaxErrorVisitor()
167 v.visit(parse_tree)
167 v.visit(parse_tree)
168
168
169 except (SyntaxError, MemoryError):
169 except (SyntaxError, MemoryError):
170 return False
170 return False
171 return True
171 return True
172 return False
172 return False
@@ -1,355 +1,355 b''
1 """Magic functions for running cells in various scripts."""
1 """Magic functions for running cells in various scripts."""
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6 import asyncio
6 import asyncio
7 import atexit
7 import atexit
8 import errno
8 import errno
9 import functools
9 import functools
10 import os
10 import os
11 import signal
11 import signal
12 import sys
12 import sys
13 import time
13 import time
14 from contextlib import contextmanager
14 from contextlib import contextmanager
15 from subprocess import CalledProcessError
15 from subprocess import CalledProcessError
16
16
17 from traitlets import Dict, List, default
17 from traitlets import Dict, List, default
18
18
19 from IPython.core import magic_arguments
19 from IPython.core import magic_arguments
20 from IPython.core.magic import Magics, cell_magic, line_magic, magics_class
20 from IPython.core.magic import Magics, cell_magic, line_magic, magics_class
21 from IPython.lib.backgroundjobs import BackgroundJobManager
21 from IPython.lib.backgroundjobs import BackgroundJobManager
22 from IPython.utils.process import arg_split
22 from IPython.utils.process import arg_split
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Magic implementation classes
25 # Magic implementation classes
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 def script_args(f):
28 def script_args(f):
29 """single decorator for adding script args"""
29 """single decorator for adding script args"""
30 args = [
30 args = [
31 magic_arguments.argument(
31 magic_arguments.argument(
32 '--out', type=str,
32 '--out', type=str,
33 help="""The variable in which to store stdout from the script.
33 help="""The variable in which to store stdout from the script.
34 If the script is backgrounded, this will be the stdout *pipe*,
34 If the script is backgrounded, this will be the stdout *pipe*,
35 instead of the stderr text itself and will not be auto closed.
35 instead of the stderr text itself and will not be auto closed.
36 """
36 """
37 ),
37 ),
38 magic_arguments.argument(
38 magic_arguments.argument(
39 '--err', type=str,
39 '--err', type=str,
40 help="""The variable in which to store stderr from the script.
40 help="""The variable in which to store stderr from the script.
41 If the script is backgrounded, this will be the stderr *pipe*,
41 If the script is backgrounded, this will be the stderr *pipe*,
42 instead of the stderr text itself and will not be autoclosed.
42 instead of the stderr text itself and will not be autoclosed.
43 """
43 """
44 ),
44 ),
45 magic_arguments.argument(
45 magic_arguments.argument(
46 '--bg', action="store_true",
46 '--bg', action="store_true",
47 help="""Whether to run the script in the background.
47 help="""Whether to run the script in the background.
48 If given, the only way to see the output of the command is
48 If given, the only way to see the output of the command is
49 with --out/err.
49 with --out/err.
50 """
50 """
51 ),
51 ),
52 magic_arguments.argument(
52 magic_arguments.argument(
53 '--proc', type=str,
53 '--proc', type=str,
54 help="""The variable in which to store Popen instance.
54 help="""The variable in which to store Popen instance.
55 This is used only when --bg option is given.
55 This is used only when --bg option is given.
56 """
56 """
57 ),
57 ),
58 magic_arguments.argument(
58 magic_arguments.argument(
59 '--no-raise-error', action="store_false", dest='raise_error',
59 '--no-raise-error', action="store_false", dest='raise_error',
60 help="""Whether you should raise an error message in addition to
60 help="""Whether you should raise an error message in addition to
61 a stream on stderr if you get a nonzero exit code.
61 a stream on stderr if you get a nonzero exit code.
62 """
62 """
63 )
63 )
64 ]
64 ]
65 for arg in args:
65 for arg in args:
66 f = arg(f)
66 f = arg(f)
67 return f
67 return f
68
68
69
69
70 @contextmanager
70 @contextmanager
71 def safe_watcher():
71 def safe_watcher():
72 if sys.platform == "win32":
72 if sys.platform == "win32":
73 yield
73 yield
74 return
74 return
75
75
76 from asyncio import SafeChildWatcher
76 from asyncio import SafeChildWatcher
77
77
78 policy = asyncio.get_event_loop_policy()
78 policy = asyncio.get_event_loop_policy()
79 old_watcher = policy.get_child_watcher()
79 old_watcher = policy.get_child_watcher()
80 if isinstance(old_watcher, SafeChildWatcher):
80 if isinstance(old_watcher, SafeChildWatcher):
81 yield
81 yield
82 return
82 return
83
83
84 loop = asyncio.get_event_loop()
84 loop = policy.get_event_loop()
85 try:
85 try:
86 watcher = asyncio.SafeChildWatcher()
86 watcher = asyncio.SafeChildWatcher()
87 watcher.attach_loop(loop)
87 watcher.attach_loop(loop)
88 policy.set_child_watcher(watcher)
88 policy.set_child_watcher(watcher)
89 yield
89 yield
90 finally:
90 finally:
91 watcher.close()
91 watcher.close()
92 policy.set_child_watcher(old_watcher)
92 policy.set_child_watcher(old_watcher)
93
93
94
94
95 def dec_safe_watcher(fun):
95 def dec_safe_watcher(fun):
96 @functools.wraps(fun)
96 @functools.wraps(fun)
97 def _inner(*args, **kwargs):
97 def _inner(*args, **kwargs):
98 with safe_watcher():
98 with safe_watcher():
99 return fun(*args, **kwargs)
99 return fun(*args, **kwargs)
100
100
101 return _inner
101 return _inner
102
102
103
103
104 @magics_class
104 @magics_class
105 class ScriptMagics(Magics):
105 class ScriptMagics(Magics):
106 """Magics for talking to scripts
106 """Magics for talking to scripts
107
107
108 This defines a base `%%script` cell magic for running a cell
108 This defines a base `%%script` cell magic for running a cell
109 with a program in a subprocess, and registers a few top-level
109 with a program in a subprocess, and registers a few top-level
110 magics that call %%script with common interpreters.
110 magics that call %%script with common interpreters.
111 """
111 """
112 script_magics = List(
112 script_magics = List(
113 help="""Extra script cell magics to define
113 help="""Extra script cell magics to define
114
114
115 This generates simple wrappers of `%%script foo` as `%%foo`.
115 This generates simple wrappers of `%%script foo` as `%%foo`.
116
116
117 If you want to add script magics that aren't on your path,
117 If you want to add script magics that aren't on your path,
118 specify them in script_paths
118 specify them in script_paths
119 """,
119 """,
120 ).tag(config=True)
120 ).tag(config=True)
121 @default('script_magics')
121 @default('script_magics')
122 def _script_magics_default(self):
122 def _script_magics_default(self):
123 """default to a common list of programs"""
123 """default to a common list of programs"""
124
124
125 defaults = [
125 defaults = [
126 'sh',
126 'sh',
127 'bash',
127 'bash',
128 'perl',
128 'perl',
129 'ruby',
129 'ruby',
130 'python',
130 'python',
131 'python2',
131 'python2',
132 'python3',
132 'python3',
133 'pypy',
133 'pypy',
134 ]
134 ]
135 if os.name == 'nt':
135 if os.name == 'nt':
136 defaults.extend([
136 defaults.extend([
137 'cmd',
137 'cmd',
138 ])
138 ])
139
139
140 return defaults
140 return defaults
141
141
142 script_paths = Dict(
142 script_paths = Dict(
143 help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
143 help="""Dict mapping short 'ruby' names to full paths, such as '/opt/secret/bin/ruby'
144
144
145 Only necessary for items in script_magics where the default path will not
145 Only necessary for items in script_magics where the default path will not
146 find the right interpreter.
146 find the right interpreter.
147 """
147 """
148 ).tag(config=True)
148 ).tag(config=True)
149
149
150 def __init__(self, shell=None):
150 def __init__(self, shell=None):
151 super(ScriptMagics, self).__init__(shell=shell)
151 super(ScriptMagics, self).__init__(shell=shell)
152 self._generate_script_magics()
152 self._generate_script_magics()
153 self.job_manager = BackgroundJobManager()
153 self.job_manager = BackgroundJobManager()
154 self.bg_processes = []
154 self.bg_processes = []
155 atexit.register(self.kill_bg_processes)
155 atexit.register(self.kill_bg_processes)
156
156
157 def __del__(self):
157 def __del__(self):
158 self.kill_bg_processes()
158 self.kill_bg_processes()
159
159
160 def _generate_script_magics(self):
160 def _generate_script_magics(self):
161 cell_magics = self.magics['cell']
161 cell_magics = self.magics['cell']
162 for name in self.script_magics:
162 for name in self.script_magics:
163 cell_magics[name] = self._make_script_magic(name)
163 cell_magics[name] = self._make_script_magic(name)
164
164
165 def _make_script_magic(self, name):
165 def _make_script_magic(self, name):
166 """make a named magic, that calls %%script with a particular program"""
166 """make a named magic, that calls %%script with a particular program"""
167 # expand to explicit path if necessary:
167 # expand to explicit path if necessary:
168 script = self.script_paths.get(name, name)
168 script = self.script_paths.get(name, name)
169
169
170 @magic_arguments.magic_arguments()
170 @magic_arguments.magic_arguments()
171 @script_args
171 @script_args
172 def named_script_magic(line, cell):
172 def named_script_magic(line, cell):
173 # if line, add it as cl-flags
173 # if line, add it as cl-flags
174 if line:
174 if line:
175 line = "%s %s" % (script, line)
175 line = "%s %s" % (script, line)
176 else:
176 else:
177 line = script
177 line = script
178 return self.shebang(line, cell)
178 return self.shebang(line, cell)
179
179
180 # write a basic docstring:
180 # write a basic docstring:
181 named_script_magic.__doc__ = \
181 named_script_magic.__doc__ = \
182 """%%{name} script magic
182 """%%{name} script magic
183
183
184 Run cells with {script} in a subprocess.
184 Run cells with {script} in a subprocess.
185
185
186 This is a shortcut for `%%script {script}`
186 This is a shortcut for `%%script {script}`
187 """.format(**locals())
187 """.format(**locals())
188
188
189 return named_script_magic
189 return named_script_magic
190
190
191 @magic_arguments.magic_arguments()
191 @magic_arguments.magic_arguments()
192 @script_args
192 @script_args
193 @cell_magic("script")
193 @cell_magic("script")
194 @dec_safe_watcher
194 @dec_safe_watcher
195 def shebang(self, line, cell):
195 def shebang(self, line, cell):
196 """Run a cell via a shell command
196 """Run a cell via a shell command
197
197
198 The `%%script` line is like the #! line of script,
198 The `%%script` line is like the #! line of script,
199 specifying a program (bash, perl, ruby, etc.) with which to run.
199 specifying a program (bash, perl, ruby, etc.) with which to run.
200
200
201 The rest of the cell is run by that program.
201 The rest of the cell is run by that program.
202
202
203 Examples
203 Examples
204 --------
204 --------
205 ::
205 ::
206
206
207 In [1]: %%script bash
207 In [1]: %%script bash
208 ...: for i in 1 2 3; do
208 ...: for i in 1 2 3; do
209 ...: echo $i
209 ...: echo $i
210 ...: done
210 ...: done
211 1
211 1
212 2
212 2
213 3
213 3
214 """
214 """
215
215
216 async def _handle_stream(stream, stream_arg, file_object):
216 async def _handle_stream(stream, stream_arg, file_object):
217 while True:
217 while True:
218 line = (await stream.readline()).decode("utf8")
218 line = (await stream.readline()).decode("utf8")
219 if not line:
219 if not line:
220 break
220 break
221 if stream_arg:
221 if stream_arg:
222 self.shell.user_ns[stream_arg] = line
222 self.shell.user_ns[stream_arg] = line
223 else:
223 else:
224 file_object.write(line)
224 file_object.write(line)
225 file_object.flush()
225 file_object.flush()
226
226
227 async def _stream_communicate(process, cell):
227 async def _stream_communicate(process, cell):
228 process.stdin.write(cell)
228 process.stdin.write(cell)
229 process.stdin.close()
229 process.stdin.close()
230 stdout_task = asyncio.create_task(
230 stdout_task = asyncio.create_task(
231 _handle_stream(process.stdout, args.out, sys.stdout)
231 _handle_stream(process.stdout, args.out, sys.stdout)
232 )
232 )
233 stderr_task = asyncio.create_task(
233 stderr_task = asyncio.create_task(
234 _handle_stream(process.stderr, args.err, sys.stderr)
234 _handle_stream(process.stderr, args.err, sys.stderr)
235 )
235 )
236 await asyncio.wait([stdout_task, stderr_task])
236 await asyncio.wait([stdout_task, stderr_task])
237 await process.wait()
237 await process.wait()
238
238
239 if sys.platform.startswith("win"):
239 if sys.platform.startswith("win"):
240 asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
240 asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
241 loop = asyncio.get_event_loop()
241 loop = asyncio.get_event_loop_policy().get_event_loop()
242 argv = arg_split(line, posix=not sys.platform.startswith("win"))
242 argv = arg_split(line, posix=not sys.platform.startswith("win"))
243 args, cmd = self.shebang.parser.parse_known_args(argv)
243 args, cmd = self.shebang.parser.parse_known_args(argv)
244 try:
244 try:
245 p = loop.run_until_complete(
245 p = loop.run_until_complete(
246 asyncio.create_subprocess_exec(
246 asyncio.create_subprocess_exec(
247 *cmd,
247 *cmd,
248 stdout=asyncio.subprocess.PIPE,
248 stdout=asyncio.subprocess.PIPE,
249 stderr=asyncio.subprocess.PIPE,
249 stderr=asyncio.subprocess.PIPE,
250 stdin=asyncio.subprocess.PIPE,
250 stdin=asyncio.subprocess.PIPE,
251 )
251 )
252 )
252 )
253 except OSError as e:
253 except OSError as e:
254 if e.errno == errno.ENOENT:
254 if e.errno == errno.ENOENT:
255 print("Couldn't find program: %r" % cmd[0])
255 print("Couldn't find program: %r" % cmd[0])
256 return
256 return
257 else:
257 else:
258 raise
258 raise
259
259
260 if not cell.endswith('\n'):
260 if not cell.endswith('\n'):
261 cell += '\n'
261 cell += '\n'
262 cell = cell.encode('utf8', 'replace')
262 cell = cell.encode('utf8', 'replace')
263 if args.bg:
263 if args.bg:
264 self.bg_processes.append(p)
264 self.bg_processes.append(p)
265 self._gc_bg_processes()
265 self._gc_bg_processes()
266 to_close = []
266 to_close = []
267 if args.out:
267 if args.out:
268 self.shell.user_ns[args.out] = p.stdout
268 self.shell.user_ns[args.out] = p.stdout
269 else:
269 else:
270 to_close.append(p.stdout)
270 to_close.append(p.stdout)
271 if args.err:
271 if args.err:
272 self.shell.user_ns[args.err] = p.stderr
272 self.shell.user_ns[args.err] = p.stderr
273 else:
273 else:
274 to_close.append(p.stderr)
274 to_close.append(p.stderr)
275 self.job_manager.new(self._run_script, p, cell, to_close, daemon=True)
275 self.job_manager.new(self._run_script, p, cell, to_close, daemon=True)
276 if args.proc:
276 if args.proc:
277 self.shell.user_ns[args.proc] = p
277 self.shell.user_ns[args.proc] = p
278 return
278 return
279
279
280 try:
280 try:
281 loop.run_until_complete(_stream_communicate(p, cell))
281 loop.run_until_complete(_stream_communicate(p, cell))
282 except KeyboardInterrupt:
282 except KeyboardInterrupt:
283 try:
283 try:
284 p.send_signal(signal.SIGINT)
284 p.send_signal(signal.SIGINT)
285 time.sleep(0.1)
285 time.sleep(0.1)
286 if p.returncode is not None:
286 if p.returncode is not None:
287 print("Process is interrupted.")
287 print("Process is interrupted.")
288 return
288 return
289 p.terminate()
289 p.terminate()
290 time.sleep(0.1)
290 time.sleep(0.1)
291 if p.returncode is not None:
291 if p.returncode is not None:
292 print("Process is terminated.")
292 print("Process is terminated.")
293 return
293 return
294 p.kill()
294 p.kill()
295 print("Process is killed.")
295 print("Process is killed.")
296 except OSError:
296 except OSError:
297 pass
297 pass
298 except Exception as e:
298 except Exception as e:
299 print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
299 print("Error while terminating subprocess (pid=%i): %s" % (p.pid, e))
300 return
300 return
301 if args.raise_error and p.returncode!=0:
301 if args.raise_error and p.returncode!=0:
302 # If we get here and p.returncode is still None, we must have
302 # If we get here and p.returncode is still None, we must have
303 # killed it but not yet seen its return code. We don't wait for it,
303 # killed it but not yet seen its return code. We don't wait for it,
304 # in case it's stuck in uninterruptible sleep. -9 = SIGKILL
304 # in case it's stuck in uninterruptible sleep. -9 = SIGKILL
305 rc = p.returncode or -9
305 rc = p.returncode or -9
306 raise CalledProcessError(rc, cell)
306 raise CalledProcessError(rc, cell)
307
307
308 def _run_script(self, p, cell, to_close):
308 def _run_script(self, p, cell, to_close):
309 """callback for running the script in the background"""
309 """callback for running the script in the background"""
310 p.stdin.write(cell)
310 p.stdin.write(cell)
311 p.stdin.close()
311 p.stdin.close()
312 for s in to_close:
312 for s in to_close:
313 s.close()
313 s.close()
314 p.wait()
314 p.wait()
315
315
316 @line_magic("killbgscripts")
316 @line_magic("killbgscripts")
317 def killbgscripts(self, _nouse_=''):
317 def killbgscripts(self, _nouse_=''):
318 """Kill all BG processes started by %%script and its family."""
318 """Kill all BG processes started by %%script and its family."""
319 self.kill_bg_processes()
319 self.kill_bg_processes()
320 print("All background processes were killed.")
320 print("All background processes were killed.")
321
321
322 def kill_bg_processes(self):
322 def kill_bg_processes(self):
323 """Kill all BG processes which are still running."""
323 """Kill all BG processes which are still running."""
324 if not self.bg_processes:
324 if not self.bg_processes:
325 return
325 return
326 for p in self.bg_processes:
326 for p in self.bg_processes:
327 if p.returncode is None:
327 if p.returncode is None:
328 try:
328 try:
329 p.send_signal(signal.SIGINT)
329 p.send_signal(signal.SIGINT)
330 except:
330 except:
331 pass
331 pass
332 time.sleep(0.1)
332 time.sleep(0.1)
333 self._gc_bg_processes()
333 self._gc_bg_processes()
334 if not self.bg_processes:
334 if not self.bg_processes:
335 return
335 return
336 for p in self.bg_processes:
336 for p in self.bg_processes:
337 if p.returncode is None:
337 if p.returncode is None:
338 try:
338 try:
339 p.terminate()
339 p.terminate()
340 except:
340 except:
341 pass
341 pass
342 time.sleep(0.1)
342 time.sleep(0.1)
343 self._gc_bg_processes()
343 self._gc_bg_processes()
344 if not self.bg_processes:
344 if not self.bg_processes:
345 return
345 return
346 for p in self.bg_processes:
346 for p in self.bg_processes:
347 if p.returncode is None:
347 if p.returncode is None:
348 try:
348 try:
349 p.kill()
349 p.kill()
350 except:
350 except:
351 pass
351 pass
352 self._gc_bg_processes()
352 self._gc_bg_processes()
353
353
354 def _gc_bg_processes(self):
354 def _gc_bg_processes(self):
355 self.bg_processes = [p for p in self.bg_processes if p.returncode is None]
355 self.bg_processes = [p for p in self.bg_processes if p.returncode is None]
@@ -1,1072 +1,1072 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(found=True, isalias=False, ismagic=True,
350 info = dict(found=True, isalias=False, ismagic=True,
351 namespace = 'IPython internal', obj= lmagic.__wrapped__,
351 namespace = 'IPython internal', obj= lmagic.__wrapped__,
352 parent = None)
352 parent = None)
353 self.assertEqual(lfind, info)
353 self.assertEqual(lfind, info)
354
354
355 def test_ofind_cell_magic(self):
355 def test_ofind_cell_magic(self):
356 from IPython.core.magic import register_cell_magic
356 from IPython.core.magic import register_cell_magic
357
357
358 @register_cell_magic
358 @register_cell_magic
359 def cmagic(line, cell):
359 def cmagic(line, cell):
360 "A cell magic"
360 "A cell magic"
361
361
362 # Get info on cell magic
362 # Get info on cell magic
363 find = ip._ofind('cmagic')
363 find = ip._ofind('cmagic')
364 info = dict(found=True, isalias=False, ismagic=True,
364 info = dict(found=True, isalias=False, ismagic=True,
365 namespace = 'IPython internal', obj= cmagic.__wrapped__,
365 namespace = 'IPython internal', obj= cmagic.__wrapped__,
366 parent = None)
366 parent = None)
367 self.assertEqual(find, info)
367 self.assertEqual(find, info)
368
368
369 def test_ofind_property_with_error(self):
369 def test_ofind_property_with_error(self):
370 class A(object):
370 class A(object):
371 @property
371 @property
372 def foo(self):
372 def foo(self):
373 raise NotImplementedError()
373 raise NotImplementedError()
374 a = A()
374 a = A()
375
375
376 found = ip._ofind('a.foo', [('locals', locals())])
376 found = ip._ofind('a.foo', [('locals', locals())])
377 info = dict(found=True, isalias=False, ismagic=False,
377 info = dict(found=True, isalias=False, ismagic=False,
378 namespace='locals', obj=A.foo, parent=a)
378 namespace='locals', obj=A.foo, parent=a)
379 self.assertEqual(found, info)
379 self.assertEqual(found, info)
380
380
381 def test_ofind_multiple_attribute_lookups(self):
381 def test_ofind_multiple_attribute_lookups(self):
382 class A(object):
382 class A(object):
383 @property
383 @property
384 def foo(self):
384 def foo(self):
385 raise NotImplementedError()
385 raise NotImplementedError()
386
386
387 a = A()
387 a = A()
388 a.a = A()
388 a.a = A()
389 a.a.a = A()
389 a.a.a = A()
390
390
391 found = ip._ofind('a.a.a.foo', [('locals', locals())])
391 found = ip._ofind('a.a.a.foo', [('locals', locals())])
392 info = dict(found=True, isalias=False, ismagic=False,
392 info = dict(found=True, isalias=False, ismagic=False,
393 namespace='locals', obj=A.foo, parent=a.a.a)
393 namespace='locals', obj=A.foo, parent=a.a.a)
394 self.assertEqual(found, info)
394 self.assertEqual(found, info)
395
395
396 def test_ofind_slotted_attributes(self):
396 def test_ofind_slotted_attributes(self):
397 class A(object):
397 class A(object):
398 __slots__ = ['foo']
398 __slots__ = ['foo']
399 def __init__(self):
399 def __init__(self):
400 self.foo = 'bar'
400 self.foo = 'bar'
401
401
402 a = A()
402 a = A()
403 found = ip._ofind('a.foo', [('locals', locals())])
403 found = ip._ofind('a.foo', [('locals', locals())])
404 info = dict(found=True, isalias=False, ismagic=False,
404 info = dict(found=True, isalias=False, ismagic=False,
405 namespace='locals', obj=a.foo, parent=a)
405 namespace='locals', obj=a.foo, parent=a)
406 self.assertEqual(found, info)
406 self.assertEqual(found, info)
407
407
408 found = ip._ofind('a.bar', [('locals', locals())])
408 found = ip._ofind('a.bar', [('locals', locals())])
409 info = dict(found=False, isalias=False, ismagic=False,
409 info = dict(found=False, isalias=False, ismagic=False,
410 namespace=None, obj=None, parent=a)
410 namespace=None, obj=None, parent=a)
411 self.assertEqual(found, info)
411 self.assertEqual(found, info)
412
412
413 def test_ofind_prefers_property_to_instance_level_attribute(self):
413 def test_ofind_prefers_property_to_instance_level_attribute(self):
414 class A(object):
414 class A(object):
415 @property
415 @property
416 def foo(self):
416 def foo(self):
417 return 'bar'
417 return 'bar'
418 a = A()
418 a = A()
419 a.__dict__["foo"] = "baz"
419 a.__dict__["foo"] = "baz"
420 self.assertEqual(a.foo, "bar")
420 self.assertEqual(a.foo, "bar")
421 found = ip._ofind("a.foo", [("locals", locals())])
421 found = ip._ofind("a.foo", [("locals", locals())])
422 self.assertIs(found["obj"], A.foo)
422 self.assertIs(found["obj"], A.foo)
423
423
424 def test_custom_syntaxerror_exception(self):
424 def test_custom_syntaxerror_exception(self):
425 called = []
425 called = []
426 def my_handler(shell, etype, value, tb, tb_offset=None):
426 def my_handler(shell, etype, value, tb, tb_offset=None):
427 called.append(etype)
427 called.append(etype)
428 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
428 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
429
429
430 ip.set_custom_exc((SyntaxError,), my_handler)
430 ip.set_custom_exc((SyntaxError,), my_handler)
431 try:
431 try:
432 ip.run_cell("1f")
432 ip.run_cell("1f")
433 # Check that this was called, and only once.
433 # Check that this was called, and only once.
434 self.assertEqual(called, [SyntaxError])
434 self.assertEqual(called, [SyntaxError])
435 finally:
435 finally:
436 # Reset the custom exception hook
436 # Reset the custom exception hook
437 ip.set_custom_exc((), None)
437 ip.set_custom_exc((), None)
438
438
439 def test_custom_exception(self):
439 def test_custom_exception(self):
440 called = []
440 called = []
441 def my_handler(shell, etype, value, tb, tb_offset=None):
441 def my_handler(shell, etype, value, tb, tb_offset=None):
442 called.append(etype)
442 called.append(etype)
443 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
443 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
444
444
445 ip.set_custom_exc((ValueError,), my_handler)
445 ip.set_custom_exc((ValueError,), my_handler)
446 try:
446 try:
447 res = ip.run_cell("raise ValueError('test')")
447 res = ip.run_cell("raise ValueError('test')")
448 # Check that this was called, and only once.
448 # Check that this was called, and only once.
449 self.assertEqual(called, [ValueError])
449 self.assertEqual(called, [ValueError])
450 # Check that the error is on the result object
450 # Check that the error is on the result object
451 self.assertIsInstance(res.error_in_exec, ValueError)
451 self.assertIsInstance(res.error_in_exec, ValueError)
452 finally:
452 finally:
453 # Reset the custom exception hook
453 # Reset the custom exception hook
454 ip.set_custom_exc((), None)
454 ip.set_custom_exc((), None)
455
455
456 @mock.patch("builtins.print")
456 @mock.patch("builtins.print")
457 def test_showtraceback_with_surrogates(self, mocked_print):
457 def test_showtraceback_with_surrogates(self, mocked_print):
458 values = []
458 values = []
459
459
460 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
460 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
461 values.append(value)
461 values.append(value)
462 if value == chr(0xD8FF):
462 if value == chr(0xD8FF):
463 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
463 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
464
464
465 # mock builtins.print
465 # mock builtins.print
466 mocked_print.side_effect = mock_print_func
466 mocked_print.side_effect = mock_print_func
467
467
468 # ip._showtraceback() is replaced in globalipapp.py.
468 # ip._showtraceback() is replaced in globalipapp.py.
469 # Call original method to test.
469 # Call original method to test.
470 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
470 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
471
471
472 self.assertEqual(mocked_print.call_count, 2)
472 self.assertEqual(mocked_print.call_count, 2)
473 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
473 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
474
474
475 def test_mktempfile(self):
475 def test_mktempfile(self):
476 filename = ip.mktempfile()
476 filename = ip.mktempfile()
477 # Check that we can open the file again on Windows
477 # Check that we can open the file again on Windows
478 with open(filename, 'w') as f:
478 with open(filename, 'w') as f:
479 f.write('abc')
479 f.write('abc')
480
480
481 filename = ip.mktempfile(data='blah')
481 filename = ip.mktempfile(data='blah')
482 with open(filename, 'r') as f:
482 with open(filename, 'r') as f:
483 self.assertEqual(f.read(), 'blah')
483 self.assertEqual(f.read(), 'blah')
484
484
485 def test_new_main_mod(self):
485 def test_new_main_mod(self):
486 # Smoketest to check that this accepts a unicode module name
486 # Smoketest to check that this accepts a unicode module name
487 name = u'jiefmw'
487 name = u'jiefmw'
488 mod = ip.new_main_mod(u'%s.py' % name, name)
488 mod = ip.new_main_mod(u'%s.py' % name, name)
489 self.assertEqual(mod.__name__, name)
489 self.assertEqual(mod.__name__, name)
490
490
491 def test_get_exception_only(self):
491 def test_get_exception_only(self):
492 try:
492 try:
493 raise KeyboardInterrupt
493 raise KeyboardInterrupt
494 except KeyboardInterrupt:
494 except KeyboardInterrupt:
495 msg = ip.get_exception_only()
495 msg = ip.get_exception_only()
496 self.assertEqual(msg, 'KeyboardInterrupt\n')
496 self.assertEqual(msg, 'KeyboardInterrupt\n')
497
497
498 try:
498 try:
499 raise DerivedInterrupt("foo")
499 raise DerivedInterrupt("foo")
500 except KeyboardInterrupt:
500 except KeyboardInterrupt:
501 msg = ip.get_exception_only()
501 msg = ip.get_exception_only()
502 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
502 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
503
503
504 def test_inspect_text(self):
504 def test_inspect_text(self):
505 ip.run_cell('a = 5')
505 ip.run_cell('a = 5')
506 text = ip.object_inspect_text('a')
506 text = ip.object_inspect_text('a')
507 self.assertIsInstance(text, str)
507 self.assertIsInstance(text, str)
508
508
509 def test_last_execution_result(self):
509 def test_last_execution_result(self):
510 """ Check that last execution result gets set correctly (GH-10702) """
510 """ Check that last execution result gets set correctly (GH-10702) """
511 result = ip.run_cell('a = 5; a')
511 result = ip.run_cell('a = 5; a')
512 self.assertTrue(ip.last_execution_succeeded)
512 self.assertTrue(ip.last_execution_succeeded)
513 self.assertEqual(ip.last_execution_result.result, 5)
513 self.assertEqual(ip.last_execution_result.result, 5)
514
514
515 result = ip.run_cell('a = x_invalid_id_x')
515 result = ip.run_cell('a = x_invalid_id_x')
516 self.assertFalse(ip.last_execution_succeeded)
516 self.assertFalse(ip.last_execution_succeeded)
517 self.assertFalse(ip.last_execution_result.success)
517 self.assertFalse(ip.last_execution_result.success)
518 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
518 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
519
519
520 def test_reset_aliasing(self):
520 def test_reset_aliasing(self):
521 """ Check that standard posix aliases work after %reset. """
521 """ Check that standard posix aliases work after %reset. """
522 if os.name != 'posix':
522 if os.name != 'posix':
523 return
523 return
524
524
525 ip.reset()
525 ip.reset()
526 for cmd in ('clear', 'more', 'less', 'man'):
526 for cmd in ('clear', 'more', 'less', 'man'):
527 res = ip.run_cell('%' + cmd)
527 res = ip.run_cell('%' + cmd)
528 self.assertEqual(res.success, True)
528 self.assertEqual(res.success, True)
529
529
530
530
531 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
531 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
532
532
533 @onlyif_unicode_paths
533 @onlyif_unicode_paths
534 def setUp(self):
534 def setUp(self):
535 self.BASETESTDIR = tempfile.mkdtemp()
535 self.BASETESTDIR = tempfile.mkdtemp()
536 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
536 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
537 os.mkdir(self.TESTDIR)
537 os.mkdir(self.TESTDIR)
538 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
538 with open(join(self.TESTDIR, u"Γ₯Àâtestscript.py"), "w") as sfile:
539 sfile.write("pass\n")
539 sfile.write("pass\n")
540 self.oldpath = os.getcwd()
540 self.oldpath = os.getcwd()
541 os.chdir(self.TESTDIR)
541 os.chdir(self.TESTDIR)
542 self.fname = u"Γ₯Àâtestscript.py"
542 self.fname = u"Γ₯Àâtestscript.py"
543
543
544 def tearDown(self):
544 def tearDown(self):
545 os.chdir(self.oldpath)
545 os.chdir(self.oldpath)
546 shutil.rmtree(self.BASETESTDIR)
546 shutil.rmtree(self.BASETESTDIR)
547
547
548 @onlyif_unicode_paths
548 @onlyif_unicode_paths
549 def test_1(self):
549 def test_1(self):
550 """Test safe_execfile with non-ascii path
550 """Test safe_execfile with non-ascii path
551 """
551 """
552 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
552 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
553
553
554 class ExitCodeChecks(tt.TempFileMixin):
554 class ExitCodeChecks(tt.TempFileMixin):
555
555
556 def setUp(self):
556 def setUp(self):
557 self.system = ip.system_raw
557 self.system = ip.system_raw
558
558
559 def test_exit_code_ok(self):
559 def test_exit_code_ok(self):
560 self.system('exit 0')
560 self.system('exit 0')
561 self.assertEqual(ip.user_ns['_exit_code'], 0)
561 self.assertEqual(ip.user_ns['_exit_code'], 0)
562
562
563 def test_exit_code_error(self):
563 def test_exit_code_error(self):
564 self.system('exit 1')
564 self.system('exit 1')
565 self.assertEqual(ip.user_ns['_exit_code'], 1)
565 self.assertEqual(ip.user_ns['_exit_code'], 1)
566
566
567 @skipif(not hasattr(signal, 'SIGALRM'))
567 @skipif(not hasattr(signal, 'SIGALRM'))
568 def test_exit_code_signal(self):
568 def test_exit_code_signal(self):
569 self.mktmp("import signal, time\n"
569 self.mktmp("import signal, time\n"
570 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
570 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
571 "time.sleep(1)\n")
571 "time.sleep(1)\n")
572 self.system("%s %s" % (sys.executable, self.fname))
572 self.system("%s %s" % (sys.executable, self.fname))
573 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
573 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
574
574
575 @onlyif_cmds_exist("csh")
575 @onlyif_cmds_exist("csh")
576 def test_exit_code_signal_csh(self):
576 def test_exit_code_signal_csh(self):
577 SHELL = os.environ.get('SHELL', None)
577 SHELL = os.environ.get('SHELL', None)
578 os.environ['SHELL'] = find_cmd("csh")
578 os.environ['SHELL'] = find_cmd("csh")
579 try:
579 try:
580 self.test_exit_code_signal()
580 self.test_exit_code_signal()
581 finally:
581 finally:
582 if SHELL is not None:
582 if SHELL is not None:
583 os.environ['SHELL'] = SHELL
583 os.environ['SHELL'] = SHELL
584 else:
584 else:
585 del os.environ['SHELL']
585 del os.environ['SHELL']
586
586
587
587
588 class TestSystemRaw(ExitCodeChecks):
588 class TestSystemRaw(ExitCodeChecks):
589
589
590 def setUp(self):
590 def setUp(self):
591 super().setUp()
591 super().setUp()
592 self.system = ip.system_raw
592 self.system = ip.system_raw
593
593
594 @onlyif_unicode_paths
594 @onlyif_unicode_paths
595 def test_1(self):
595 def test_1(self):
596 """Test system_raw with non-ascii cmd
596 """Test system_raw with non-ascii cmd
597 """
597 """
598 cmd = u'''python -c "'Γ₯Àâ'" '''
598 cmd = u'''python -c "'Γ₯Àâ'" '''
599 ip.system_raw(cmd)
599 ip.system_raw(cmd)
600
600
601 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
601 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
602 @mock.patch('os.system', side_effect=KeyboardInterrupt)
602 @mock.patch('os.system', side_effect=KeyboardInterrupt)
603 def test_control_c(self, *mocks):
603 def test_control_c(self, *mocks):
604 try:
604 try:
605 self.system("sleep 1 # wont happen")
605 self.system("sleep 1 # wont happen")
606 except KeyboardInterrupt:
606 except KeyboardInterrupt:
607 self.fail(
607 self.fail(
608 "system call should intercept "
608 "system call should intercept "
609 "keyboard interrupt from subprocess.call"
609 "keyboard interrupt from subprocess.call"
610 )
610 )
611 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
611 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
612
612
613 def test_magic_warnings(self):
613 def test_magic_warnings(self):
614 for magic_cmd in ("ls", "pip", "conda", "cd"):
614 for magic_cmd in ("ls", "pip", "conda", "cd"):
615 with self.assertWarnsRegex(Warning, "You executed the system command"):
615 with self.assertWarnsRegex(Warning, "You executed the system command"):
616 ip.system_raw(magic_cmd)
616 ip.system_raw(magic_cmd)
617
617
618 # TODO: Exit codes are currently ignored on Windows.
618 # TODO: Exit codes are currently ignored on Windows.
619 class TestSystemPipedExitCode(ExitCodeChecks):
619 class TestSystemPipedExitCode(ExitCodeChecks):
620
620
621 def setUp(self):
621 def setUp(self):
622 super().setUp()
622 super().setUp()
623 self.system = ip.system_piped
623 self.system = ip.system_piped
624
624
625 @skip_win32
625 @skip_win32
626 def test_exit_code_ok(self):
626 def test_exit_code_ok(self):
627 ExitCodeChecks.test_exit_code_ok(self)
627 ExitCodeChecks.test_exit_code_ok(self)
628
628
629 @skip_win32
629 @skip_win32
630 def test_exit_code_error(self):
630 def test_exit_code_error(self):
631 ExitCodeChecks.test_exit_code_error(self)
631 ExitCodeChecks.test_exit_code_error(self)
632
632
633 @skip_win32
633 @skip_win32
634 def test_exit_code_signal(self):
634 def test_exit_code_signal(self):
635 ExitCodeChecks.test_exit_code_signal(self)
635 ExitCodeChecks.test_exit_code_signal(self)
636
636
637 class TestModules(tt.TempFileMixin):
637 class TestModules(tt.TempFileMixin):
638 def test_extraneous_loads(self):
638 def test_extraneous_loads(self):
639 """Test we're not loading modules on startup that we shouldn't.
639 """Test we're not loading modules on startup that we shouldn't.
640 """
640 """
641 self.mktmp("import sys\n"
641 self.mktmp("import sys\n"
642 "print('numpy' in sys.modules)\n"
642 "print('numpy' in sys.modules)\n"
643 "print('ipyparallel' in sys.modules)\n"
643 "print('ipyparallel' in sys.modules)\n"
644 "print('ipykernel' in sys.modules)\n"
644 "print('ipykernel' in sys.modules)\n"
645 )
645 )
646 out = "False\nFalse\nFalse\n"
646 out = "False\nFalse\nFalse\n"
647 tt.ipexec_validate(self.fname, out)
647 tt.ipexec_validate(self.fname, out)
648
648
649 class Negator(ast.NodeTransformer):
649 class Negator(ast.NodeTransformer):
650 """Negates all number literals in an AST."""
650 """Negates all number literals in an AST."""
651
651
652 # for python 3.7 and earlier
652 # for python 3.7 and earlier
653 def visit_Num(self, node):
653 def visit_Num(self, node):
654 node.n = -node.n
654 node.n = -node.n
655 return node
655 return node
656
656
657 # for python 3.8+
657 # for python 3.8+
658 def visit_Constant(self, node):
658 def visit_Constant(self, node):
659 if isinstance(node.value, int):
659 if isinstance(node.value, int):
660 return self.visit_Num(node)
660 return self.visit_Num(node)
661 return node
661 return node
662
662
663 class TestAstTransform(unittest.TestCase):
663 class TestAstTransform(unittest.TestCase):
664 def setUp(self):
664 def setUp(self):
665 self.negator = Negator()
665 self.negator = Negator()
666 ip.ast_transformers.append(self.negator)
666 ip.ast_transformers.append(self.negator)
667
667
668 def tearDown(self):
668 def tearDown(self):
669 ip.ast_transformers.remove(self.negator)
669 ip.ast_transformers.remove(self.negator)
670
670
671 def test_run_cell(self):
671 def test_run_cell(self):
672 with tt.AssertPrints('-34'):
672 with tt.AssertPrints('-34'):
673 ip.run_cell('print (12 + 22)')
673 ip.run_cell('print (12 + 22)')
674
674
675 # A named reference to a number shouldn't be transformed.
675 # A named reference to a number shouldn't be transformed.
676 ip.user_ns['n'] = 55
676 ip.user_ns['n'] = 55
677 with tt.AssertNotPrints('-55'):
677 with tt.AssertNotPrints('-55'):
678 ip.run_cell('print (n)')
678 ip.run_cell('print (n)')
679
679
680 def test_timeit(self):
680 def test_timeit(self):
681 called = set()
681 called = set()
682 def f(x):
682 def f(x):
683 called.add(x)
683 called.add(x)
684 ip.push({'f':f})
684 ip.push({'f':f})
685
685
686 with tt.AssertPrints("std. dev. of"):
686 with tt.AssertPrints("std. dev. of"):
687 ip.run_line_magic("timeit", "-n1 f(1)")
687 ip.run_line_magic("timeit", "-n1 f(1)")
688 self.assertEqual(called, {-1})
688 self.assertEqual(called, {-1})
689 called.clear()
689 called.clear()
690
690
691 with tt.AssertPrints("std. dev. of"):
691 with tt.AssertPrints("std. dev. of"):
692 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
692 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
693 self.assertEqual(called, {-2, -3})
693 self.assertEqual(called, {-2, -3})
694
694
695 def test_time(self):
695 def test_time(self):
696 called = []
696 called = []
697 def f(x):
697 def f(x):
698 called.append(x)
698 called.append(x)
699 ip.push({'f':f})
699 ip.push({'f':f})
700
700
701 # Test with an expression
701 # Test with an expression
702 with tt.AssertPrints("Wall time: "):
702 with tt.AssertPrints("Wall time: "):
703 ip.run_line_magic("time", "f(5+9)")
703 ip.run_line_magic("time", "f(5+9)")
704 self.assertEqual(called, [-14])
704 self.assertEqual(called, [-14])
705 called[:] = []
705 called[:] = []
706
706
707 # Test with a statement (different code path)
707 # Test with a statement (different code path)
708 with tt.AssertPrints("Wall time: "):
708 with tt.AssertPrints("Wall time: "):
709 ip.run_line_magic("time", "a = f(-3 + -2)")
709 ip.run_line_magic("time", "a = f(-3 + -2)")
710 self.assertEqual(called, [5])
710 self.assertEqual(called, [5])
711
711
712 def test_macro(self):
712 def test_macro(self):
713 ip.push({'a':10})
713 ip.push({'a':10})
714 # The AST transformation makes this do a+=-1
714 # The AST transformation makes this do a+=-1
715 ip.define_macro("amacro", "a+=1\nprint(a)")
715 ip.define_macro("amacro", "a+=1\nprint(a)")
716
716
717 with tt.AssertPrints("9"):
717 with tt.AssertPrints("9"):
718 ip.run_cell("amacro")
718 ip.run_cell("amacro")
719 with tt.AssertPrints("8"):
719 with tt.AssertPrints("8"):
720 ip.run_cell("amacro")
720 ip.run_cell("amacro")
721
721
722 class TestMiscTransform(unittest.TestCase):
722 class TestMiscTransform(unittest.TestCase):
723
723
724
724
725 def test_transform_only_once(self):
725 def test_transform_only_once(self):
726 cleanup = 0
726 cleanup = 0
727 line_t = 0
727 line_t = 0
728 def count_cleanup(lines):
728 def count_cleanup(lines):
729 nonlocal cleanup
729 nonlocal cleanup
730 cleanup += 1
730 cleanup += 1
731 return lines
731 return lines
732
732
733 def count_line_t(lines):
733 def count_line_t(lines):
734 nonlocal line_t
734 nonlocal line_t
735 line_t += 1
735 line_t += 1
736 return lines
736 return lines
737
737
738 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
738 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
739 ip.input_transformer_manager.line_transforms.append(count_line_t)
739 ip.input_transformer_manager.line_transforms.append(count_line_t)
740
740
741 ip.run_cell('1')
741 ip.run_cell('1')
742
742
743 assert cleanup == 1
743 assert cleanup == 1
744 assert line_t == 1
744 assert line_t == 1
745
745
746 class IntegerWrapper(ast.NodeTransformer):
746 class IntegerWrapper(ast.NodeTransformer):
747 """Wraps all integers in a call to Integer()"""
747 """Wraps all integers in a call to Integer()"""
748
748
749 # for Python 3.7 and earlier
749 # for Python 3.7 and earlier
750
750
751 # for Python 3.7 and earlier
751 # for Python 3.7 and earlier
752 def visit_Num(self, node):
752 def visit_Num(self, node):
753 if isinstance(node.n, int):
753 if isinstance(node.n, int):
754 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
754 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
755 args=[node], keywords=[])
755 args=[node], keywords=[])
756 return node
756 return node
757
757
758 # For Python 3.8+
758 # For Python 3.8+
759 def visit_Constant(self, node):
759 def visit_Constant(self, node):
760 if isinstance(node.value, int):
760 if isinstance(node.value, int):
761 return self.visit_Num(node)
761 return self.visit_Num(node)
762 return node
762 return node
763
763
764
764
765 class TestAstTransform2(unittest.TestCase):
765 class TestAstTransform2(unittest.TestCase):
766 def setUp(self):
766 def setUp(self):
767 self.intwrapper = IntegerWrapper()
767 self.intwrapper = IntegerWrapper()
768 ip.ast_transformers.append(self.intwrapper)
768 ip.ast_transformers.append(self.intwrapper)
769
769
770 self.calls = []
770 self.calls = []
771 def Integer(*args):
771 def Integer(*args):
772 self.calls.append(args)
772 self.calls.append(args)
773 return args
773 return args
774 ip.push({"Integer": Integer})
774 ip.push({"Integer": Integer})
775
775
776 def tearDown(self):
776 def tearDown(self):
777 ip.ast_transformers.remove(self.intwrapper)
777 ip.ast_transformers.remove(self.intwrapper)
778 del ip.user_ns['Integer']
778 del ip.user_ns['Integer']
779
779
780 def test_run_cell(self):
780 def test_run_cell(self):
781 ip.run_cell("n = 2")
781 ip.run_cell("n = 2")
782 self.assertEqual(self.calls, [(2,)])
782 self.assertEqual(self.calls, [(2,)])
783
783
784 # This shouldn't throw an error
784 # This shouldn't throw an error
785 ip.run_cell("o = 2.0")
785 ip.run_cell("o = 2.0")
786 self.assertEqual(ip.user_ns['o'], 2.0)
786 self.assertEqual(ip.user_ns['o'], 2.0)
787
787
788 def test_timeit(self):
788 def test_timeit(self):
789 called = set()
789 called = set()
790 def f(x):
790 def f(x):
791 called.add(x)
791 called.add(x)
792 ip.push({'f':f})
792 ip.push({'f':f})
793
793
794 with tt.AssertPrints("std. dev. of"):
794 with tt.AssertPrints("std. dev. of"):
795 ip.run_line_magic("timeit", "-n1 f(1)")
795 ip.run_line_magic("timeit", "-n1 f(1)")
796 self.assertEqual(called, {(1,)})
796 self.assertEqual(called, {(1,)})
797 called.clear()
797 called.clear()
798
798
799 with tt.AssertPrints("std. dev. of"):
799 with tt.AssertPrints("std. dev. of"):
800 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
800 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
801 self.assertEqual(called, {(2,), (3,)})
801 self.assertEqual(called, {(2,), (3,)})
802
802
803 class ErrorTransformer(ast.NodeTransformer):
803 class ErrorTransformer(ast.NodeTransformer):
804 """Throws an error when it sees a number."""
804 """Throws an error when it sees a number."""
805
805
806 # for Python 3.7 and earlier
806 # for Python 3.7 and earlier
807 def visit_Num(self, node):
807 def visit_Num(self, node):
808 raise ValueError("test")
808 raise ValueError("test")
809
809
810 # for Python 3.8+
810 # for Python 3.8+
811 def visit_Constant(self, node):
811 def visit_Constant(self, node):
812 if isinstance(node.value, int):
812 if isinstance(node.value, int):
813 return self.visit_Num(node)
813 return self.visit_Num(node)
814 return node
814 return node
815
815
816
816
817 class TestAstTransformError(unittest.TestCase):
817 class TestAstTransformError(unittest.TestCase):
818 def test_unregistering(self):
818 def test_unregistering(self):
819 err_transformer = ErrorTransformer()
819 err_transformer = ErrorTransformer()
820 ip.ast_transformers.append(err_transformer)
820 ip.ast_transformers.append(err_transformer)
821
821
822 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
822 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
823 ip.run_cell("1 + 2")
823 ip.run_cell("1 + 2")
824
824
825 # This should have been removed.
825 # This should have been removed.
826 self.assertNotIn(err_transformer, ip.ast_transformers)
826 self.assertNotIn(err_transformer, ip.ast_transformers)
827
827
828
828
829 class StringRejector(ast.NodeTransformer):
829 class StringRejector(ast.NodeTransformer):
830 """Throws an InputRejected when it sees a string literal.
830 """Throws an InputRejected when it sees a string literal.
831
831
832 Used to verify that NodeTransformers can signal that a piece of code should
832 Used to verify that NodeTransformers can signal that a piece of code should
833 not be executed by throwing an InputRejected.
833 not be executed by throwing an InputRejected.
834 """
834 """
835
835
836 #for python 3.7 and earlier
836 #for python 3.7 and earlier
837 def visit_Str(self, node):
837 def visit_Str(self, node):
838 raise InputRejected("test")
838 raise InputRejected("test")
839
839
840 # 3.8 only
840 # 3.8 only
841 def visit_Constant(self, node):
841 def visit_Constant(self, node):
842 if isinstance(node.value, str):
842 if isinstance(node.value, str):
843 raise InputRejected("test")
843 raise InputRejected("test")
844 return node
844 return node
845
845
846
846
847 class TestAstTransformInputRejection(unittest.TestCase):
847 class TestAstTransformInputRejection(unittest.TestCase):
848
848
849 def setUp(self):
849 def setUp(self):
850 self.transformer = StringRejector()
850 self.transformer = StringRejector()
851 ip.ast_transformers.append(self.transformer)
851 ip.ast_transformers.append(self.transformer)
852
852
853 def tearDown(self):
853 def tearDown(self):
854 ip.ast_transformers.remove(self.transformer)
854 ip.ast_transformers.remove(self.transformer)
855
855
856 def test_input_rejection(self):
856 def test_input_rejection(self):
857 """Check that NodeTransformers can reject input."""
857 """Check that NodeTransformers can reject input."""
858
858
859 expect_exception_tb = tt.AssertPrints("InputRejected: test")
859 expect_exception_tb = tt.AssertPrints("InputRejected: test")
860 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
860 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
861
861
862 # Run the same check twice to verify that the transformer is not
862 # Run the same check twice to verify that the transformer is not
863 # disabled after raising.
863 # disabled after raising.
864 with expect_exception_tb, expect_no_cell_output:
864 with expect_exception_tb, expect_no_cell_output:
865 ip.run_cell("'unsafe'")
865 ip.run_cell("'unsafe'")
866
866
867 with expect_exception_tb, expect_no_cell_output:
867 with expect_exception_tb, expect_no_cell_output:
868 res = ip.run_cell("'unsafe'")
868 res = ip.run_cell("'unsafe'")
869
869
870 self.assertIsInstance(res.error_before_exec, InputRejected)
870 self.assertIsInstance(res.error_before_exec, InputRejected)
871
871
872 def test__IPYTHON__():
872 def test__IPYTHON__():
873 # This shouldn't raise a NameError, that's all
873 # This shouldn't raise a NameError, that's all
874 __IPYTHON__
874 __IPYTHON__
875
875
876
876
877 class DummyRepr(object):
877 class DummyRepr(object):
878 def __repr__(self):
878 def __repr__(self):
879 return "DummyRepr"
879 return "DummyRepr"
880
880
881 def _repr_html_(self):
881 def _repr_html_(self):
882 return "<b>dummy</b>"
882 return "<b>dummy</b>"
883
883
884 def _repr_javascript_(self):
884 def _repr_javascript_(self):
885 return "console.log('hi');", {'key': 'value'}
885 return "console.log('hi');", {'key': 'value'}
886
886
887
887
888 def test_user_variables():
888 def test_user_variables():
889 # enable all formatters
889 # enable all formatters
890 ip.display_formatter.active_types = ip.display_formatter.format_types
890 ip.display_formatter.active_types = ip.display_formatter.format_types
891
891
892 ip.user_ns['dummy'] = d = DummyRepr()
892 ip.user_ns['dummy'] = d = DummyRepr()
893 keys = {'dummy', 'doesnotexist'}
893 keys = {'dummy', 'doesnotexist'}
894 r = ip.user_expressions({ key:key for key in keys})
894 r = ip.user_expressions({ key:key for key in keys})
895
895
896 assert keys == set(r.keys())
896 assert keys == set(r.keys())
897 dummy = r["dummy"]
897 dummy = r["dummy"]
898 assert {"status", "data", "metadata"} == set(dummy.keys())
898 assert {"status", "data", "metadata"} == set(dummy.keys())
899 assert dummy["status"] == "ok"
899 assert dummy["status"] == "ok"
900 data = dummy["data"]
900 data = dummy["data"]
901 metadata = dummy["metadata"]
901 metadata = dummy["metadata"]
902 assert data.get("text/html") == d._repr_html_()
902 assert data.get("text/html") == d._repr_html_()
903 js, jsmd = d._repr_javascript_()
903 js, jsmd = d._repr_javascript_()
904 assert data.get("application/javascript") == js
904 assert data.get("application/javascript") == js
905 assert metadata.get("application/javascript") == jsmd
905 assert metadata.get("application/javascript") == jsmd
906
906
907 dne = r["doesnotexist"]
907 dne = r["doesnotexist"]
908 assert dne["status"] == "error"
908 assert dne["status"] == "error"
909 assert dne["ename"] == "NameError"
909 assert dne["ename"] == "NameError"
910
910
911 # back to text only
911 # back to text only
912 ip.display_formatter.active_types = ['text/plain']
912 ip.display_formatter.active_types = ['text/plain']
913
913
914 def test_user_expression():
914 def test_user_expression():
915 # enable all formatters
915 # enable all formatters
916 ip.display_formatter.active_types = ip.display_formatter.format_types
916 ip.display_formatter.active_types = ip.display_formatter.format_types
917 query = {
917 query = {
918 'a' : '1 + 2',
918 'a' : '1 + 2',
919 'b' : '1/0',
919 'b' : '1/0',
920 }
920 }
921 r = ip.user_expressions(query)
921 r = ip.user_expressions(query)
922 import pprint
922 import pprint
923 pprint.pprint(r)
923 pprint.pprint(r)
924 assert set(r.keys()) == set(query.keys())
924 assert set(r.keys()) == set(query.keys())
925 a = r["a"]
925 a = r["a"]
926 assert {"status", "data", "metadata"} == set(a.keys())
926 assert {"status", "data", "metadata"} == set(a.keys())
927 assert a["status"] == "ok"
927 assert a["status"] == "ok"
928 data = a["data"]
928 data = a["data"]
929 metadata = a["metadata"]
929 metadata = a["metadata"]
930 assert data.get("text/plain") == "3"
930 assert data.get("text/plain") == "3"
931
931
932 b = r["b"]
932 b = r["b"]
933 assert b["status"] == "error"
933 assert b["status"] == "error"
934 assert b["ename"] == "ZeroDivisionError"
934 assert b["ename"] == "ZeroDivisionError"
935
935
936 # back to text only
936 # back to text only
937 ip.display_formatter.active_types = ['text/plain']
937 ip.display_formatter.active_types = ['text/plain']
938
938
939
939
940 class TestSyntaxErrorTransformer(unittest.TestCase):
940 class TestSyntaxErrorTransformer(unittest.TestCase):
941 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
941 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
942
942
943 @staticmethod
943 @staticmethod
944 def transformer(lines):
944 def transformer(lines):
945 for line in lines:
945 for line in lines:
946 pos = line.find('syntaxerror')
946 pos = line.find('syntaxerror')
947 if pos >= 0:
947 if pos >= 0:
948 e = SyntaxError('input contains "syntaxerror"')
948 e = SyntaxError('input contains "syntaxerror"')
949 e.text = line
949 e.text = line
950 e.offset = pos + 1
950 e.offset = pos + 1
951 raise e
951 raise e
952 return lines
952 return lines
953
953
954 def setUp(self):
954 def setUp(self):
955 ip.input_transformers_post.append(self.transformer)
955 ip.input_transformers_post.append(self.transformer)
956
956
957 def tearDown(self):
957 def tearDown(self):
958 ip.input_transformers_post.remove(self.transformer)
958 ip.input_transformers_post.remove(self.transformer)
959
959
960 def test_syntaxerror_input_transformer(self):
960 def test_syntaxerror_input_transformer(self):
961 with tt.AssertPrints('1234'):
961 with tt.AssertPrints('1234'):
962 ip.run_cell('1234')
962 ip.run_cell('1234')
963 with tt.AssertPrints('SyntaxError: invalid syntax'):
963 with tt.AssertPrints('SyntaxError: invalid syntax'):
964 ip.run_cell('1 2 3') # plain python syntax error
964 ip.run_cell('1 2 3') # plain python syntax error
965 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
965 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
966 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
966 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
967 with tt.AssertPrints('3456'):
967 with tt.AssertPrints('3456'):
968 ip.run_cell('3456')
968 ip.run_cell('3456')
969
969
970
970
971 class TestWarningSuppression(unittest.TestCase):
971 class TestWarningSuppression(unittest.TestCase):
972 def test_warning_suppression(self):
972 def test_warning_suppression(self):
973 ip.run_cell("import warnings")
973 ip.run_cell("import warnings")
974 try:
974 try:
975 with self.assertWarnsRegex(UserWarning, "asdf"):
975 with self.assertWarnsRegex(UserWarning, "asdf"):
976 ip.run_cell("warnings.warn('asdf')")
976 ip.run_cell("warnings.warn('asdf')")
977 # Here's the real test -- if we run that again, we should get the
977 # Here's the real test -- if we run that again, we should get the
978 # warning again. Traditionally, each warning was only issued once per
978 # warning again. Traditionally, each warning was only issued once per
979 # IPython session (approximately), even if the user typed in new and
979 # IPython session (approximately), even if the user typed in new and
980 # different code that should have also triggered the warning, leading
980 # different code that should have also triggered the warning, leading
981 # to much confusion.
981 # to much confusion.
982 with self.assertWarnsRegex(UserWarning, "asdf"):
982 with self.assertWarnsRegex(UserWarning, "asdf"):
983 ip.run_cell("warnings.warn('asdf')")
983 ip.run_cell("warnings.warn('asdf')")
984 finally:
984 finally:
985 ip.run_cell("del warnings")
985 ip.run_cell("del warnings")
986
986
987
987
988 def test_deprecation_warning(self):
988 def test_deprecation_warning(self):
989 ip.run_cell("""
989 ip.run_cell("""
990 import warnings
990 import warnings
991 def wrn():
991 def wrn():
992 warnings.warn(
992 warnings.warn(
993 "I AM A WARNING",
993 "I AM A WARNING",
994 DeprecationWarning
994 DeprecationWarning
995 )
995 )
996 """)
996 """)
997 try:
997 try:
998 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
998 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
999 ip.run_cell("wrn()")
999 ip.run_cell("wrn()")
1000 finally:
1000 finally:
1001 ip.run_cell("del warnings")
1001 ip.run_cell("del warnings")
1002 ip.run_cell("del wrn")
1002 ip.run_cell("del wrn")
1003
1003
1004
1004
1005 class TestImportNoDeprecate(tt.TempFileMixin):
1005 class TestImportNoDeprecate(tt.TempFileMixin):
1006
1006
1007 def setUp(self):
1007 def setUp(self):
1008 """Make a valid python temp file."""
1008 """Make a valid python temp file."""
1009 self.mktmp("""
1009 self.mktmp("""
1010 import warnings
1010 import warnings
1011 def wrn():
1011 def wrn():
1012 warnings.warn(
1012 warnings.warn(
1013 "I AM A WARNING",
1013 "I AM A WARNING",
1014 DeprecationWarning
1014 DeprecationWarning
1015 )
1015 )
1016 """)
1016 """)
1017 super().setUp()
1017 super().setUp()
1018
1018
1019 def test_no_dep(self):
1019 def test_no_dep(self):
1020 """
1020 """
1021 No deprecation warning should be raised from imported functions
1021 No deprecation warning should be raised from imported functions
1022 """
1022 """
1023 ip.run_cell("from {} import wrn".format(self.fname))
1023 ip.run_cell("from {} import wrn".format(self.fname))
1024
1024
1025 with tt.AssertNotPrints("I AM A WARNING"):
1025 with tt.AssertNotPrints("I AM A WARNING"):
1026 ip.run_cell("wrn()")
1026 ip.run_cell("wrn()")
1027 ip.run_cell("del wrn")
1027 ip.run_cell("del wrn")
1028
1028
1029
1029
1030 def test_custom_exc_count():
1030 def test_custom_exc_count():
1031 hook = mock.Mock(return_value=None)
1031 hook = mock.Mock(return_value=None)
1032 ip.set_custom_exc((SyntaxError,), hook)
1032 ip.set_custom_exc((SyntaxError,), hook)
1033 before = ip.execution_count
1033 before = ip.execution_count
1034 ip.run_cell("def foo()", store_history=True)
1034 ip.run_cell("def foo()", store_history=True)
1035 # restore default excepthook
1035 # restore default excepthook
1036 ip.set_custom_exc((), None)
1036 ip.set_custom_exc((), None)
1037 assert hook.call_count == 1
1037 assert hook.call_count == 1
1038 assert ip.execution_count == before + 1
1038 assert ip.execution_count == before + 1
1039
1039
1040
1040
1041 def test_run_cell_async():
1041 def test_run_cell_async():
1042 loop = asyncio.get_event_loop()
1042 loop = asyncio.get_event_loop_policy().get_event_loop()
1043 ip.run_cell("import asyncio")
1043 ip.run_cell("import asyncio")
1044 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1044 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1045 assert asyncio.iscoroutine(coro)
1045 assert asyncio.iscoroutine(coro)
1046 result = loop.run_until_complete(coro)
1046 result = loop.run_until_complete(coro)
1047 assert isinstance(result, interactiveshell.ExecutionResult)
1047 assert isinstance(result, interactiveshell.ExecutionResult)
1048 assert result.result == 5
1048 assert result.result == 5
1049
1049
1050
1050
1051 def test_should_run_async():
1051 def test_should_run_async():
1052 assert not ip.should_run_async("a = 5")
1052 assert not ip.should_run_async("a = 5")
1053 assert ip.should_run_async("await x")
1053 assert ip.should_run_async("await x")
1054 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1054 assert ip.should_run_async("import asyncio; await asyncio.sleep(1)")
1055
1055
1056
1056
1057 def test_set_custom_completer():
1057 def test_set_custom_completer():
1058 num_completers = len(ip.Completer.matchers)
1058 num_completers = len(ip.Completer.matchers)
1059
1059
1060 def foo(*args, **kwargs):
1060 def foo(*args, **kwargs):
1061 return "I'm a completer!"
1061 return "I'm a completer!"
1062
1062
1063 ip.set_custom_completer(foo, 0)
1063 ip.set_custom_completer(foo, 0)
1064
1064
1065 # check that we've really added a new completer
1065 # check that we've really added a new completer
1066 assert len(ip.Completer.matchers) == num_completers + 1
1066 assert len(ip.Completer.matchers) == num_completers + 1
1067
1067
1068 # check that the first completer is the function we defined
1068 # check that the first completer is the function we defined
1069 assert ip.Completer.matchers[0]() == "I'm a completer!"
1069 assert ip.Completer.matchers[0]() == "I'm a completer!"
1070
1070
1071 # clean up
1071 # clean up
1072 ip.Completer.custom_matchers.pop()
1072 ip.Completer.custom_matchers.pop()
@@ -1,1363 +1,1368 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 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
135 if not re.match("TerminalInteractiveShell.* options", stdout.splitlines()[0]):
136 print(stdout)
136 print(stdout)
137 raise AssertionError("1st line of stdout not like "
137 raise AssertionError("1st line of stdout not like "
138 "'TerminalInteractiveShell.* options'")
138 "'TerminalInteractiveShell.* options'")
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 _ip = get_ipython()
745 _ip = get_ipython()
746 _ip.history_manager.reset() # Clear any existing history.
746 _ip.history_manager.reset() # Clear any existing history.
747 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
747 cmds = ["a=1", "def b():\n return a**2", "print('noΓ«l, Γ©tΓ©', b())"]
748 for i, cmd in enumerate(cmds, start=1):
748 for i, cmd in enumerate(cmds, start=1):
749 _ip.history_manager.store_inputs(i, cmd)
749 _ip.history_manager.store_inputs(i, cmd)
750 with TemporaryDirectory() as td:
750 with TemporaryDirectory() as td:
751 outfile = os.path.join(td, "nb.ipynb")
751 outfile = os.path.join(td, "nb.ipynb")
752 _ip.magic("notebook -e %s" % outfile)
752 _ip.magic("notebook -e %s" % outfile)
753
753
754
754
755 class TestEnv(TestCase):
755 class TestEnv(TestCase):
756
756
757 def test_env(self):
757 def test_env(self):
758 env = _ip.magic("env")
758 env = _ip.magic("env")
759 self.assertTrue(isinstance(env, dict))
759 self.assertTrue(isinstance(env, dict))
760
760
761 def test_env_secret(self):
761 def test_env_secret(self):
762 env = _ip.magic("env")
762 env = _ip.magic("env")
763 hidden = "<hidden>"
763 hidden = "<hidden>"
764 with mock.patch.dict(
764 with mock.patch.dict(
765 os.environ,
765 os.environ,
766 {
766 {
767 "API_KEY": "abc123",
767 "API_KEY": "abc123",
768 "SECRET_THING": "ssshhh",
768 "SECRET_THING": "ssshhh",
769 "JUPYTER_TOKEN": "",
769 "JUPYTER_TOKEN": "",
770 "VAR": "abc"
770 "VAR": "abc"
771 }
771 }
772 ):
772 ):
773 env = _ip.magic("env")
773 env = _ip.magic("env")
774 assert env["API_KEY"] == hidden
774 assert env["API_KEY"] == hidden
775 assert env["SECRET_THING"] == hidden
775 assert env["SECRET_THING"] == hidden
776 assert env["JUPYTER_TOKEN"] == hidden
776 assert env["JUPYTER_TOKEN"] == hidden
777 assert env["VAR"] == "abc"
777 assert env["VAR"] == "abc"
778
778
779 def test_env_get_set_simple(self):
779 def test_env_get_set_simple(self):
780 env = _ip.magic("env var val1")
780 env = _ip.magic("env var val1")
781 self.assertEqual(env, None)
781 self.assertEqual(env, None)
782 self.assertEqual(os.environ['var'], 'val1')
782 self.assertEqual(os.environ['var'], 'val1')
783 self.assertEqual(_ip.magic("env var"), 'val1')
783 self.assertEqual(_ip.magic("env var"), 'val1')
784 env = _ip.magic("env var=val2")
784 env = _ip.magic("env var=val2")
785 self.assertEqual(env, None)
785 self.assertEqual(env, None)
786 self.assertEqual(os.environ['var'], 'val2')
786 self.assertEqual(os.environ['var'], 'val2')
787
787
788 def test_env_get_set_complex(self):
788 def test_env_get_set_complex(self):
789 env = _ip.magic("env var 'val1 '' 'val2")
789 env = _ip.magic("env var 'val1 '' 'val2")
790 self.assertEqual(env, None)
790 self.assertEqual(env, None)
791 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
791 self.assertEqual(os.environ['var'], "'val1 '' 'val2")
792 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
792 self.assertEqual(_ip.magic("env var"), "'val1 '' 'val2")
793 env = _ip.magic('env var=val2 val3="val4')
793 env = _ip.magic('env var=val2 val3="val4')
794 self.assertEqual(env, None)
794 self.assertEqual(env, None)
795 self.assertEqual(os.environ['var'], 'val2 val3="val4')
795 self.assertEqual(os.environ['var'], 'val2 val3="val4')
796
796
797 def test_env_set_bad_input(self):
797 def test_env_set_bad_input(self):
798 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
798 self.assertRaises(UsageError, lambda: _ip.magic("set_env var"))
799
799
800 def test_env_set_whitespace(self):
800 def test_env_set_whitespace(self):
801 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
801 self.assertRaises(UsageError, lambda: _ip.magic("env var A=B"))
802
802
803
803
804 class CellMagicTestCase(TestCase):
804 class CellMagicTestCase(TestCase):
805
805
806 def check_ident(self, magic):
806 def check_ident(self, magic):
807 # Manually called, we get the result
807 # Manually called, we get the result
808 out = _ip.run_cell_magic(magic, "a", "b")
808 out = _ip.run_cell_magic(magic, "a", "b")
809 assert out == ("a", "b")
809 assert out == ("a", "b")
810 # Via run_cell, it goes into the user's namespace via displayhook
810 # Via run_cell, it goes into the user's namespace via displayhook
811 _ip.run_cell("%%" + magic + " c\nd\n")
811 _ip.run_cell("%%" + magic + " c\nd\n")
812 assert _ip.user_ns["_"] == ("c", "d\n")
812 assert _ip.user_ns["_"] == ("c", "d\n")
813
813
814 def test_cell_magic_func_deco(self):
814 def test_cell_magic_func_deco(self):
815 "Cell magic using simple decorator"
815 "Cell magic using simple decorator"
816 @register_cell_magic
816 @register_cell_magic
817 def cellm(line, cell):
817 def cellm(line, cell):
818 return line, cell
818 return line, cell
819
819
820 self.check_ident('cellm')
820 self.check_ident('cellm')
821
821
822 def test_cell_magic_reg(self):
822 def test_cell_magic_reg(self):
823 "Cell magic manually registered"
823 "Cell magic manually registered"
824 def cellm(line, cell):
824 def cellm(line, cell):
825 return line, cell
825 return line, cell
826
826
827 _ip.register_magic_function(cellm, 'cell', 'cellm2')
827 _ip.register_magic_function(cellm, 'cell', 'cellm2')
828 self.check_ident('cellm2')
828 self.check_ident('cellm2')
829
829
830 def test_cell_magic_class(self):
830 def test_cell_magic_class(self):
831 "Cell magics declared via a class"
831 "Cell magics declared via a class"
832 @magics_class
832 @magics_class
833 class MyMagics(Magics):
833 class MyMagics(Magics):
834
834
835 @cell_magic
835 @cell_magic
836 def cellm3(self, line, cell):
836 def cellm3(self, line, cell):
837 return line, cell
837 return line, cell
838
838
839 _ip.register_magics(MyMagics)
839 _ip.register_magics(MyMagics)
840 self.check_ident('cellm3')
840 self.check_ident('cellm3')
841
841
842 def test_cell_magic_class2(self):
842 def test_cell_magic_class2(self):
843 "Cell magics declared via a class, #2"
843 "Cell magics declared via a class, #2"
844 @magics_class
844 @magics_class
845 class MyMagics2(Magics):
845 class MyMagics2(Magics):
846
846
847 @cell_magic('cellm4')
847 @cell_magic('cellm4')
848 def cellm33(self, line, cell):
848 def cellm33(self, line, cell):
849 return line, cell
849 return line, cell
850
850
851 _ip.register_magics(MyMagics2)
851 _ip.register_magics(MyMagics2)
852 self.check_ident('cellm4')
852 self.check_ident('cellm4')
853 # Check that nothing is registered as 'cellm33'
853 # Check that nothing is registered as 'cellm33'
854 c33 = _ip.find_cell_magic('cellm33')
854 c33 = _ip.find_cell_magic('cellm33')
855 assert c33 == None
855 assert c33 == None
856
856
857 def test_file():
857 def test_file():
858 """Basic %%writefile"""
858 """Basic %%writefile"""
859 ip = get_ipython()
859 ip = get_ipython()
860 with TemporaryDirectory() as td:
860 with TemporaryDirectory() as td:
861 fname = os.path.join(td, 'file1')
861 fname = os.path.join(td, 'file1')
862 ip.run_cell_magic("writefile", fname, u'\n'.join([
862 ip.run_cell_magic("writefile", fname, u'\n'.join([
863 'line1',
863 'line1',
864 'line2',
864 'line2',
865 ]))
865 ]))
866 s = Path(fname).read_text()
866 s = Path(fname).read_text()
867 assert "line1\n" in s
867 assert "line1\n" in s
868 assert "line2" in s
868 assert "line2" in s
869
869
870
870
871 @dec.skip_win32
871 @dec.skip_win32
872 def test_file_single_quote():
872 def test_file_single_quote():
873 """Basic %%writefile with embedded single quotes"""
873 """Basic %%writefile with embedded single quotes"""
874 ip = get_ipython()
874 ip = get_ipython()
875 with TemporaryDirectory() as td:
875 with TemporaryDirectory() as td:
876 fname = os.path.join(td, '\'file1\'')
876 fname = os.path.join(td, '\'file1\'')
877 ip.run_cell_magic("writefile", fname, u'\n'.join([
877 ip.run_cell_magic("writefile", fname, u'\n'.join([
878 'line1',
878 'line1',
879 'line2',
879 'line2',
880 ]))
880 ]))
881 s = Path(fname).read_text()
881 s = Path(fname).read_text()
882 assert "line1\n" in s
882 assert "line1\n" in s
883 assert "line2" in s
883 assert "line2" in s
884
884
885
885
886 @dec.skip_win32
886 @dec.skip_win32
887 def test_file_double_quote():
887 def test_file_double_quote():
888 """Basic %%writefile with embedded double quotes"""
888 """Basic %%writefile with embedded double quotes"""
889 ip = get_ipython()
889 ip = get_ipython()
890 with TemporaryDirectory() as td:
890 with TemporaryDirectory() as td:
891 fname = os.path.join(td, '"file1"')
891 fname = os.path.join(td, '"file1"')
892 ip.run_cell_magic("writefile", fname, u'\n'.join([
892 ip.run_cell_magic("writefile", fname, u'\n'.join([
893 'line1',
893 'line1',
894 'line2',
894 'line2',
895 ]))
895 ]))
896 s = Path(fname).read_text()
896 s = Path(fname).read_text()
897 assert "line1\n" in s
897 assert "line1\n" in s
898 assert "line2" in s
898 assert "line2" in s
899
899
900
900
901 def test_file_var_expand():
901 def test_file_var_expand():
902 """%%writefile $filename"""
902 """%%writefile $filename"""
903 ip = get_ipython()
903 ip = get_ipython()
904 with TemporaryDirectory() as td:
904 with TemporaryDirectory() as td:
905 fname = os.path.join(td, 'file1')
905 fname = os.path.join(td, 'file1')
906 ip.user_ns['filename'] = fname
906 ip.user_ns['filename'] = fname
907 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
907 ip.run_cell_magic("writefile", '$filename', u'\n'.join([
908 'line1',
908 'line1',
909 'line2',
909 'line2',
910 ]))
910 ]))
911 s = Path(fname).read_text()
911 s = Path(fname).read_text()
912 assert "line1\n" in s
912 assert "line1\n" in s
913 assert "line2" in s
913 assert "line2" in s
914
914
915
915
916 def test_file_unicode():
916 def test_file_unicode():
917 """%%writefile with unicode cell"""
917 """%%writefile with unicode cell"""
918 ip = get_ipython()
918 ip = get_ipython()
919 with TemporaryDirectory() as td:
919 with TemporaryDirectory() as td:
920 fname = os.path.join(td, 'file1')
920 fname = os.path.join(td, 'file1')
921 ip.run_cell_magic("writefile", fname, u'\n'.join([
921 ip.run_cell_magic("writefile", fname, u'\n'.join([
922 u'linΓ©1',
922 u'linΓ©1',
923 u'linΓ©2',
923 u'linΓ©2',
924 ]))
924 ]))
925 with io.open(fname, encoding='utf-8') as f:
925 with io.open(fname, encoding='utf-8') as f:
926 s = f.read()
926 s = f.read()
927 assert "linΓ©1\n" in s
927 assert "linΓ©1\n" in s
928 assert "linΓ©2" in s
928 assert "linΓ©2" in s
929
929
930
930
931 def test_file_amend():
931 def test_file_amend():
932 """%%writefile -a amends files"""
932 """%%writefile -a amends files"""
933 ip = get_ipython()
933 ip = get_ipython()
934 with TemporaryDirectory() as td:
934 with TemporaryDirectory() as td:
935 fname = os.path.join(td, 'file2')
935 fname = os.path.join(td, 'file2')
936 ip.run_cell_magic("writefile", fname, u'\n'.join([
936 ip.run_cell_magic("writefile", fname, u'\n'.join([
937 'line1',
937 'line1',
938 'line2',
938 'line2',
939 ]))
939 ]))
940 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
940 ip.run_cell_magic("writefile", "-a %s" % fname, u'\n'.join([
941 'line3',
941 'line3',
942 'line4',
942 'line4',
943 ]))
943 ]))
944 s = Path(fname).read_text()
944 s = Path(fname).read_text()
945 assert "line1\n" in s
945 assert "line1\n" in s
946 assert "line3\n" in s
946 assert "line3\n" in s
947
947
948
948
949 def test_file_spaces():
949 def test_file_spaces():
950 """%%file with spaces in filename"""
950 """%%file with spaces in filename"""
951 ip = get_ipython()
951 ip = get_ipython()
952 with TemporaryWorkingDirectory() as td:
952 with TemporaryWorkingDirectory() as td:
953 fname = "file name"
953 fname = "file name"
954 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
954 ip.run_cell_magic("file", '"%s"'%fname, u'\n'.join([
955 'line1',
955 'line1',
956 'line2',
956 'line2',
957 ]))
957 ]))
958 s = Path(fname).read_text()
958 s = Path(fname).read_text()
959 assert "line1\n" in s
959 assert "line1\n" in s
960 assert "line2" in s
960 assert "line2" in s
961
961
962
962
963 def test_script_config():
963 def test_script_config():
964 ip = get_ipython()
964 ip = get_ipython()
965 ip.config.ScriptMagics.script_magics = ['whoda']
965 ip.config.ScriptMagics.script_magics = ['whoda']
966 sm = script.ScriptMagics(shell=ip)
966 sm = script.ScriptMagics(shell=ip)
967 assert "whoda" in sm.magics["cell"]
967 assert "whoda" in sm.magics["cell"]
968
968
969
969
970 @pytest.fixture
971 def event_loop():
972 yield asyncio.get_event_loop_policy().get_event_loop()
973
974
970 @dec.skip_iptest_but_not_pytest
975 @dec.skip_iptest_but_not_pytest
971 @dec.skip_win32
976 @dec.skip_win32
972 @pytest.mark.skipif(
977 @pytest.mark.skipif(
973 sys.platform == "win32", reason="This test does not run under Windows"
978 sys.platform == "win32", reason="This test does not run under Windows"
974 )
979 )
975 def test_script_out():
980 def test_script_out(event_loop):
976 assert asyncio.get_event_loop().is_running() is False
981 assert event_loop.is_running() is False
977
982
978 ip = get_ipython()
983 ip = get_ipython()
979 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
984 ip.run_cell_magic("script", "--out output sh", "echo 'hi'")
980 assert asyncio.get_event_loop().is_running() is False
985 assert event_loop.is_running() is False
981 assert ip.user_ns["output"] == "hi\n"
986 assert ip.user_ns["output"] == "hi\n"
982
987
983
988
984 @dec.skip_iptest_but_not_pytest
989 @dec.skip_iptest_but_not_pytest
985 @dec.skip_win32
990 @dec.skip_win32
986 @pytest.mark.skipif(
991 @pytest.mark.skipif(
987 sys.platform == "win32", reason="This test does not run under Windows"
992 sys.platform == "win32", reason="This test does not run under Windows"
988 )
993 )
989 def test_script_err():
994 def test_script_err(event_loop):
990 ip = get_ipython()
995 ip = get_ipython()
991 assert asyncio.get_event_loop().is_running() is False
996 assert event_loop.is_running() is False
992 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
997 ip.run_cell_magic("script", "--err error sh", "echo 'hello' >&2")
993 assert asyncio.get_event_loop().is_running() is False
998 assert event_loop.is_running() is False
994 assert ip.user_ns["error"] == "hello\n"
999 assert ip.user_ns["error"] == "hello\n"
995
1000
996
1001
997 @dec.skip_iptest_but_not_pytest
1002 @dec.skip_iptest_but_not_pytest
998 @dec.skip_win32
1003 @dec.skip_win32
999 @pytest.mark.skipif(
1004 @pytest.mark.skipif(
1000 sys.platform == "win32", reason="This test does not run under Windows"
1005 sys.platform == "win32", reason="This test does not run under Windows"
1001 )
1006 )
1002 def test_script_out_err():
1007 def test_script_out_err():
1003
1008
1004 ip = get_ipython()
1009 ip = get_ipython()
1005 ip.run_cell_magic(
1010 ip.run_cell_magic(
1006 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1011 "script", "--out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1007 )
1012 )
1008 assert ip.user_ns["output"] == "hi\n"
1013 assert ip.user_ns["output"] == "hi\n"
1009 assert ip.user_ns["error"] == "hello\n"
1014 assert ip.user_ns["error"] == "hello\n"
1010
1015
1011
1016
1012 @dec.skip_iptest_but_not_pytest
1017 @dec.skip_iptest_but_not_pytest
1013 @dec.skip_win32
1018 @dec.skip_win32
1014 @pytest.mark.skipif(
1019 @pytest.mark.skipif(
1015 sys.platform == "win32", reason="This test does not run under Windows"
1020 sys.platform == "win32", reason="This test does not run under Windows"
1016 )
1021 )
1017 async def test_script_bg_out():
1022 async def test_script_bg_out(event_loop):
1018 ip = get_ipython()
1023 ip = get_ipython()
1019 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1024 ip.run_cell_magic("script", "--bg --out output sh", "echo 'hi'")
1020 assert (await ip.user_ns["output"].read()) == b"hi\n"
1025 assert (await ip.user_ns["output"].read()) == b"hi\n"
1021 ip.user_ns["output"].close()
1026 ip.user_ns["output"].close()
1022 asyncio.get_event_loop().stop()
1027 event_loop.stop()
1023
1028
1024
1029
1025 @dec.skip_iptest_but_not_pytest
1030 @dec.skip_iptest_but_not_pytest
1026 @dec.skip_win32
1031 @dec.skip_win32
1027 @pytest.mark.skipif(
1032 @pytest.mark.skipif(
1028 sys.platform == "win32", reason="This test does not run under Windows"
1033 sys.platform == "win32", reason="This test does not run under Windows"
1029 )
1034 )
1030 async def test_script_bg_err():
1035 async def test_script_bg_err():
1031 ip = get_ipython()
1036 ip = get_ipython()
1032 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")
1033 assert (await ip.user_ns["error"].read()) == b"hello\n"
1038 assert (await ip.user_ns["error"].read()) == b"hello\n"
1034 ip.user_ns["error"].close()
1039 ip.user_ns["error"].close()
1035
1040
1036
1041
1037 @dec.skip_iptest_but_not_pytest
1042 @dec.skip_iptest_but_not_pytest
1038 @dec.skip_win32
1043 @dec.skip_win32
1039 @pytest.mark.skipif(
1044 @pytest.mark.skipif(
1040 sys.platform == "win32", reason="This test does not run under Windows"
1045 sys.platform == "win32", reason="This test does not run under Windows"
1041 )
1046 )
1042 async def test_script_bg_out_err():
1047 async def test_script_bg_out_err():
1043 ip = get_ipython()
1048 ip = get_ipython()
1044 ip.run_cell_magic(
1049 ip.run_cell_magic(
1045 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1050 "script", "--bg --out output --err error sh", "echo 'hi'\necho 'hello' >&2"
1046 )
1051 )
1047 assert (await ip.user_ns["output"].read()) == b"hi\n"
1052 assert (await ip.user_ns["output"].read()) == b"hi\n"
1048 assert (await ip.user_ns["error"].read()) == b"hello\n"
1053 assert (await ip.user_ns["error"].read()) == b"hello\n"
1049 ip.user_ns["output"].close()
1054 ip.user_ns["output"].close()
1050 ip.user_ns["error"].close()
1055 ip.user_ns["error"].close()
1051
1056
1052
1057
1053 def test_script_defaults():
1058 def test_script_defaults():
1054 ip = get_ipython()
1059 ip = get_ipython()
1055 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1060 for cmd in ['sh', 'bash', 'perl', 'ruby']:
1056 try:
1061 try:
1057 find_cmd(cmd)
1062 find_cmd(cmd)
1058 except Exception:
1063 except Exception:
1059 pass
1064 pass
1060 else:
1065 else:
1061 assert cmd in ip.magics_manager.magics["cell"]
1066 assert cmd in ip.magics_manager.magics["cell"]
1062
1067
1063
1068
1064 @magics_class
1069 @magics_class
1065 class FooFoo(Magics):
1070 class FooFoo(Magics):
1066 """class with both %foo and %%foo magics"""
1071 """class with both %foo and %%foo magics"""
1067 @line_magic('foo')
1072 @line_magic('foo')
1068 def line_foo(self, line):
1073 def line_foo(self, line):
1069 "I am line foo"
1074 "I am line foo"
1070 pass
1075 pass
1071
1076
1072 @cell_magic("foo")
1077 @cell_magic("foo")
1073 def cell_foo(self, line, cell):
1078 def cell_foo(self, line, cell):
1074 "I am cell foo, not line foo"
1079 "I am cell foo, not line foo"
1075 pass
1080 pass
1076
1081
1077 def test_line_cell_info():
1082 def test_line_cell_info():
1078 """%%foo and %foo magics are distinguishable to inspect"""
1083 """%%foo and %foo magics are distinguishable to inspect"""
1079 ip = get_ipython()
1084 ip = get_ipython()
1080 ip.magics_manager.register(FooFoo)
1085 ip.magics_manager.register(FooFoo)
1081 oinfo = ip.object_inspect("foo")
1086 oinfo = ip.object_inspect("foo")
1082 assert oinfo["found"] is True
1087 assert oinfo["found"] is True
1083 assert oinfo["ismagic"] is True
1088 assert oinfo["ismagic"] is True
1084
1089
1085 oinfo = ip.object_inspect("%%foo")
1090 oinfo = ip.object_inspect("%%foo")
1086 assert oinfo["found"] is True
1091 assert oinfo["found"] is True
1087 assert oinfo["ismagic"] is True
1092 assert oinfo["ismagic"] is True
1088 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1093 assert oinfo["docstring"] == FooFoo.cell_foo.__doc__
1089
1094
1090 oinfo = ip.object_inspect("%foo")
1095 oinfo = ip.object_inspect("%foo")
1091 assert oinfo["found"] is True
1096 assert oinfo["found"] is True
1092 assert oinfo["ismagic"] is True
1097 assert oinfo["ismagic"] is True
1093 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1098 assert oinfo["docstring"] == FooFoo.line_foo.__doc__
1094
1099
1095
1100
1096 def test_multiple_magics():
1101 def test_multiple_magics():
1097 ip = get_ipython()
1102 ip = get_ipython()
1098 foo1 = FooFoo(ip)
1103 foo1 = FooFoo(ip)
1099 foo2 = FooFoo(ip)
1104 foo2 = FooFoo(ip)
1100 mm = ip.magics_manager
1105 mm = ip.magics_manager
1101 mm.register(foo1)
1106 mm.register(foo1)
1102 assert mm.magics["line"]["foo"].__self__ is foo1
1107 assert mm.magics["line"]["foo"].__self__ is foo1
1103 mm.register(foo2)
1108 mm.register(foo2)
1104 assert mm.magics["line"]["foo"].__self__ is foo2
1109 assert mm.magics["line"]["foo"].__self__ is foo2
1105
1110
1106
1111
1107 def test_alias_magic():
1112 def test_alias_magic():
1108 """Test %alias_magic."""
1113 """Test %alias_magic."""
1109 ip = get_ipython()
1114 ip = get_ipython()
1110 mm = ip.magics_manager
1115 mm = ip.magics_manager
1111
1116
1112 # Basic operation: both cell and line magics are created, if possible.
1117 # Basic operation: both cell and line magics are created, if possible.
1113 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1118 ip.run_line_magic("alias_magic", "timeit_alias timeit")
1114 assert "timeit_alias" in mm.magics["line"]
1119 assert "timeit_alias" in mm.magics["line"]
1115 assert "timeit_alias" in mm.magics["cell"]
1120 assert "timeit_alias" in mm.magics["cell"]
1116
1121
1117 # --cell is specified, line magic not created.
1122 # --cell is specified, line magic not created.
1118 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1123 ip.run_line_magic("alias_magic", "--cell timeit_cell_alias timeit")
1119 assert "timeit_cell_alias" not in mm.magics["line"]
1124 assert "timeit_cell_alias" not in mm.magics["line"]
1120 assert "timeit_cell_alias" in mm.magics["cell"]
1125 assert "timeit_cell_alias" in mm.magics["cell"]
1121
1126
1122 # Test that line alias is created successfully.
1127 # Test that line alias is created successfully.
1123 ip.run_line_magic("alias_magic", "--line env_alias env")
1128 ip.run_line_magic("alias_magic", "--line env_alias env")
1124 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1129 assert ip.run_line_magic("env", "") == ip.run_line_magic("env_alias", "")
1125
1130
1126 # Test that line alias with parameters passed in is created successfully.
1131 # Test that line alias with parameters passed in is created successfully.
1127 ip.run_line_magic(
1132 ip.run_line_magic(
1128 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1133 "alias_magic", "--line history_alias history --params " + shlex.quote("3")
1129 )
1134 )
1130 assert "history_alias" in mm.magics["line"]
1135 assert "history_alias" in mm.magics["line"]
1131
1136
1132
1137
1133 def test_save():
1138 def test_save():
1134 """Test %save."""
1139 """Test %save."""
1135 ip = get_ipython()
1140 ip = get_ipython()
1136 ip.history_manager.reset() # Clear any existing history.
1141 ip.history_manager.reset() # Clear any existing history.
1137 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1142 cmds = ["a=1", "def b():\n return a**2", "print(a, b())"]
1138 for i, cmd in enumerate(cmds, start=1):
1143 for i, cmd in enumerate(cmds, start=1):
1139 ip.history_manager.store_inputs(i, cmd)
1144 ip.history_manager.store_inputs(i, cmd)
1140 with TemporaryDirectory() as tmpdir:
1145 with TemporaryDirectory() as tmpdir:
1141 file = os.path.join(tmpdir, "testsave.py")
1146 file = os.path.join(tmpdir, "testsave.py")
1142 ip.run_line_magic("save", "%s 1-10" % file)
1147 ip.run_line_magic("save", "%s 1-10" % file)
1143 content = Path(file).read_text()
1148 content = Path(file).read_text()
1144 assert content.count(cmds[0]) == 1
1149 assert content.count(cmds[0]) == 1
1145 assert "coding: utf-8" in content
1150 assert "coding: utf-8" in content
1146 ip.run_line_magic("save", "-a %s 1-10" % file)
1151 ip.run_line_magic("save", "-a %s 1-10" % file)
1147 content = Path(file).read_text()
1152 content = Path(file).read_text()
1148 assert content.count(cmds[0]) == 2
1153 assert content.count(cmds[0]) == 2
1149 assert "coding: utf-8" in content
1154 assert "coding: utf-8" in content
1150
1155
1151
1156
1152 def test_save_with_no_args():
1157 def test_save_with_no_args():
1153 ip = get_ipython()
1158 ip = get_ipython()
1154 ip.history_manager.reset() # Clear any existing history.
1159 ip.history_manager.reset() # Clear any existing history.
1155 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1160 cmds = ["a=1", "def b():\n return a**2", "print(a, b())", "%save"]
1156 for i, cmd in enumerate(cmds, start=1):
1161 for i, cmd in enumerate(cmds, start=1):
1157 ip.history_manager.store_inputs(i, cmd)
1162 ip.history_manager.store_inputs(i, cmd)
1158
1163
1159 with TemporaryDirectory() as tmpdir:
1164 with TemporaryDirectory() as tmpdir:
1160 path = os.path.join(tmpdir, "testsave.py")
1165 path = os.path.join(tmpdir, "testsave.py")
1161 ip.run_line_magic("save", path)
1166 ip.run_line_magic("save", path)
1162 content = Path(path).read_text()
1167 content = Path(path).read_text()
1163 expected_content = dedent(
1168 expected_content = dedent(
1164 """\
1169 """\
1165 # coding: utf-8
1170 # coding: utf-8
1166 a=1
1171 a=1
1167 def b():
1172 def b():
1168 return a**2
1173 return a**2
1169 print(a, b())
1174 print(a, b())
1170 """
1175 """
1171 )
1176 )
1172 assert content == expected_content
1177 assert content == expected_content
1173
1178
1174
1179
1175 def test_store():
1180 def test_store():
1176 """Test %store."""
1181 """Test %store."""
1177 ip = get_ipython()
1182 ip = get_ipython()
1178 ip.run_line_magic('load_ext', 'storemagic')
1183 ip.run_line_magic('load_ext', 'storemagic')
1179
1184
1180 # make sure the storage is empty
1185 # make sure the storage is empty
1181 ip.run_line_magic("store", "-z")
1186 ip.run_line_magic("store", "-z")
1182 ip.user_ns["var"] = 42
1187 ip.user_ns["var"] = 42
1183 ip.run_line_magic("store", "var")
1188 ip.run_line_magic("store", "var")
1184 ip.user_ns["var"] = 39
1189 ip.user_ns["var"] = 39
1185 ip.run_line_magic("store", "-r")
1190 ip.run_line_magic("store", "-r")
1186 assert ip.user_ns["var"] == 42
1191 assert ip.user_ns["var"] == 42
1187
1192
1188 ip.run_line_magic("store", "-d var")
1193 ip.run_line_magic("store", "-d var")
1189 ip.user_ns["var"] = 39
1194 ip.user_ns["var"] = 39
1190 ip.run_line_magic("store", "-r")
1195 ip.run_line_magic("store", "-r")
1191 assert ip.user_ns["var"] == 39
1196 assert ip.user_ns["var"] == 39
1192
1197
1193
1198
1194 def _run_edit_test(arg_s, exp_filename=None,
1199 def _run_edit_test(arg_s, exp_filename=None,
1195 exp_lineno=-1,
1200 exp_lineno=-1,
1196 exp_contents=None,
1201 exp_contents=None,
1197 exp_is_temp=None):
1202 exp_is_temp=None):
1198 ip = get_ipython()
1203 ip = get_ipython()
1199 M = code.CodeMagics(ip)
1204 M = code.CodeMagics(ip)
1200 last_call = ['','']
1205 last_call = ['','']
1201 opts,args = M.parse_options(arg_s,'prxn:')
1206 opts,args = M.parse_options(arg_s,'prxn:')
1202 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1207 filename, lineno, is_temp = M._find_edit_target(ip, args, opts, last_call)
1203
1208
1204 if exp_filename is not None:
1209 if exp_filename is not None:
1205 assert exp_filename == filename
1210 assert exp_filename == filename
1206 if exp_contents is not None:
1211 if exp_contents is not None:
1207 with io.open(filename, 'r', encoding='utf-8') as f:
1212 with io.open(filename, 'r', encoding='utf-8') as f:
1208 contents = f.read()
1213 contents = f.read()
1209 assert exp_contents == contents
1214 assert exp_contents == contents
1210 if exp_lineno != -1:
1215 if exp_lineno != -1:
1211 assert exp_lineno == lineno
1216 assert exp_lineno == lineno
1212 if exp_is_temp is not None:
1217 if exp_is_temp is not None:
1213 assert exp_is_temp == is_temp
1218 assert exp_is_temp == is_temp
1214
1219
1215
1220
1216 def test_edit_interactive():
1221 def test_edit_interactive():
1217 """%edit on interactively defined objects"""
1222 """%edit on interactively defined objects"""
1218 ip = get_ipython()
1223 ip = get_ipython()
1219 n = ip.execution_count
1224 n = ip.execution_count
1220 ip.run_cell("def foo(): return 1", store_history=True)
1225 ip.run_cell("def foo(): return 1", store_history=True)
1221
1226
1222 try:
1227 try:
1223 _run_edit_test("foo")
1228 _run_edit_test("foo")
1224 except code.InteractivelyDefined as e:
1229 except code.InteractivelyDefined as e:
1225 assert e.index == n
1230 assert e.index == n
1226 else:
1231 else:
1227 raise AssertionError("Should have raised InteractivelyDefined")
1232 raise AssertionError("Should have raised InteractivelyDefined")
1228
1233
1229
1234
1230 def test_edit_cell():
1235 def test_edit_cell():
1231 """%edit [cell id]"""
1236 """%edit [cell id]"""
1232 ip = get_ipython()
1237 ip = get_ipython()
1233
1238
1234 ip.run_cell("def foo(): return 1", store_history=True)
1239 ip.run_cell("def foo(): return 1", store_history=True)
1235
1240
1236 # test
1241 # test
1237 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1242 _run_edit_test("1", exp_contents=ip.user_ns['In'][1], exp_is_temp=True)
1238
1243
1239 def test_edit_fname():
1244 def test_edit_fname():
1240 """%edit file"""
1245 """%edit file"""
1241 # test
1246 # test
1242 _run_edit_test("test file.py", exp_filename="test file.py")
1247 _run_edit_test("test file.py", exp_filename="test file.py")
1243
1248
1244 def test_bookmark():
1249 def test_bookmark():
1245 ip = get_ipython()
1250 ip = get_ipython()
1246 ip.run_line_magic('bookmark', 'bmname')
1251 ip.run_line_magic('bookmark', 'bmname')
1247 with tt.AssertPrints('bmname'):
1252 with tt.AssertPrints('bmname'):
1248 ip.run_line_magic('bookmark', '-l')
1253 ip.run_line_magic('bookmark', '-l')
1249 ip.run_line_magic('bookmark', '-d bmname')
1254 ip.run_line_magic('bookmark', '-d bmname')
1250
1255
1251 def test_ls_magic():
1256 def test_ls_magic():
1252 ip = get_ipython()
1257 ip = get_ipython()
1253 json_formatter = ip.display_formatter.formatters['application/json']
1258 json_formatter = ip.display_formatter.formatters['application/json']
1254 json_formatter.enabled = True
1259 json_formatter.enabled = True
1255 lsmagic = ip.magic('lsmagic')
1260 lsmagic = ip.magic('lsmagic')
1256 with warnings.catch_warnings(record=True) as w:
1261 with warnings.catch_warnings(record=True) as w:
1257 j = json_formatter(lsmagic)
1262 j = json_formatter(lsmagic)
1258 assert sorted(j) == ["cell", "line"]
1263 assert sorted(j) == ["cell", "line"]
1259 assert w == [] # no warnings
1264 assert w == [] # no warnings
1260
1265
1261
1266
1262 def test_strip_initial_indent():
1267 def test_strip_initial_indent():
1263 def sii(s):
1268 def sii(s):
1264 lines = s.splitlines()
1269 lines = s.splitlines()
1265 return '\n'.join(code.strip_initial_indent(lines))
1270 return '\n'.join(code.strip_initial_indent(lines))
1266
1271
1267 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1272 assert sii(" a = 1\nb = 2") == "a = 1\nb = 2"
1268 assert sii(" a\n b\nc") == "a\n b\nc"
1273 assert sii(" a\n b\nc") == "a\n b\nc"
1269 assert sii("a\n b") == "a\n b"
1274 assert sii("a\n b") == "a\n b"
1270
1275
1271 def test_logging_magic_quiet_from_arg():
1276 def test_logging_magic_quiet_from_arg():
1272 _ip.config.LoggingMagics.quiet = False
1277 _ip.config.LoggingMagics.quiet = False
1273 lm = logging.LoggingMagics(shell=_ip)
1278 lm = logging.LoggingMagics(shell=_ip)
1274 with TemporaryDirectory() as td:
1279 with TemporaryDirectory() as td:
1275 try:
1280 try:
1276 with tt.AssertNotPrints(re.compile("Activating.*")):
1281 with tt.AssertNotPrints(re.compile("Activating.*")):
1277 lm.logstart('-q {}'.format(
1282 lm.logstart('-q {}'.format(
1278 os.path.join(td, "quiet_from_arg.log")))
1283 os.path.join(td, "quiet_from_arg.log")))
1279 finally:
1284 finally:
1280 _ip.logger.logstop()
1285 _ip.logger.logstop()
1281
1286
1282 def test_logging_magic_quiet_from_config():
1287 def test_logging_magic_quiet_from_config():
1283 _ip.config.LoggingMagics.quiet = True
1288 _ip.config.LoggingMagics.quiet = True
1284 lm = logging.LoggingMagics(shell=_ip)
1289 lm = logging.LoggingMagics(shell=_ip)
1285 with TemporaryDirectory() as td:
1290 with TemporaryDirectory() as td:
1286 try:
1291 try:
1287 with tt.AssertNotPrints(re.compile("Activating.*")):
1292 with tt.AssertNotPrints(re.compile("Activating.*")):
1288 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1293 lm.logstart(os.path.join(td, "quiet_from_config.log"))
1289 finally:
1294 finally:
1290 _ip.logger.logstop()
1295 _ip.logger.logstop()
1291
1296
1292
1297
1293 def test_logging_magic_not_quiet():
1298 def test_logging_magic_not_quiet():
1294 _ip.config.LoggingMagics.quiet = False
1299 _ip.config.LoggingMagics.quiet = False
1295 lm = logging.LoggingMagics(shell=_ip)
1300 lm = logging.LoggingMagics(shell=_ip)
1296 with TemporaryDirectory() as td:
1301 with TemporaryDirectory() as td:
1297 try:
1302 try:
1298 with tt.AssertPrints(re.compile("Activating.*")):
1303 with tt.AssertPrints(re.compile("Activating.*")):
1299 lm.logstart(os.path.join(td, "not_quiet.log"))
1304 lm.logstart(os.path.join(td, "not_quiet.log"))
1300 finally:
1305 finally:
1301 _ip.logger.logstop()
1306 _ip.logger.logstop()
1302
1307
1303
1308
1304 def test_time_no_var_expand():
1309 def test_time_no_var_expand():
1305 _ip.user_ns['a'] = 5
1310 _ip.user_ns['a'] = 5
1306 _ip.user_ns['b'] = []
1311 _ip.user_ns['b'] = []
1307 _ip.magic('time b.append("{a}")')
1312 _ip.magic('time b.append("{a}")')
1308 assert _ip.user_ns['b'] == ['{a}']
1313 assert _ip.user_ns['b'] == ['{a}']
1309
1314
1310
1315
1311 # this is slow, put at the end for local testing.
1316 # this is slow, put at the end for local testing.
1312 def test_timeit_arguments():
1317 def test_timeit_arguments():
1313 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1318 "Test valid timeit arguments, should not cause SyntaxError (GH #1269)"
1314 if sys.version_info < (3,7):
1319 if sys.version_info < (3,7):
1315 _ip.magic("timeit -n1 -r1 ('#')")
1320 _ip.magic("timeit -n1 -r1 ('#')")
1316 else:
1321 else:
1317 # 3.7 optimize no-op statement like above out, and complain there is
1322 # 3.7 optimize no-op statement like above out, and complain there is
1318 # nothing in the for loop.
1323 # nothing in the for loop.
1319 _ip.magic("timeit -n1 -r1 a=('#')")
1324 _ip.magic("timeit -n1 -r1 a=('#')")
1320
1325
1321
1326
1322 TEST_MODULE = """
1327 TEST_MODULE = """
1323 print('Loaded my_tmp')
1328 print('Loaded my_tmp')
1324 if __name__ == "__main__":
1329 if __name__ == "__main__":
1325 print('I just ran a script')
1330 print('I just ran a script')
1326 """
1331 """
1327
1332
1328
1333
1329 def test_run_module_from_import_hook():
1334 def test_run_module_from_import_hook():
1330 "Test that a module can be loaded via an import hook"
1335 "Test that a module can be loaded via an import hook"
1331 with TemporaryDirectory() as tmpdir:
1336 with TemporaryDirectory() as tmpdir:
1332 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1337 fullpath = os.path.join(tmpdir, 'my_tmp.py')
1333 Path(fullpath).write_text(TEST_MODULE)
1338 Path(fullpath).write_text(TEST_MODULE)
1334
1339
1335 class MyTempImporter(object):
1340 class MyTempImporter(object):
1336 def __init__(self):
1341 def __init__(self):
1337 pass
1342 pass
1338
1343
1339 def find_module(self, fullname, path=None):
1344 def find_module(self, fullname, path=None):
1340 if 'my_tmp' in fullname:
1345 if 'my_tmp' in fullname:
1341 return self
1346 return self
1342 return None
1347 return None
1343
1348
1344 def load_module(self, name):
1349 def load_module(self, name):
1345 import imp
1350 import imp
1346 return imp.load_source('my_tmp', fullpath)
1351 return imp.load_source('my_tmp', fullpath)
1347
1352
1348 def get_code(self, fullname):
1353 def get_code(self, fullname):
1349 return compile(Path(fullpath).read_text(), "foo", "exec")
1354 return compile(Path(fullpath).read_text(), "foo", "exec")
1350
1355
1351 def is_package(self, __):
1356 def is_package(self, __):
1352 return False
1357 return False
1353
1358
1354 sys.meta_path.insert(0, MyTempImporter())
1359 sys.meta_path.insert(0, MyTempImporter())
1355
1360
1356 with capture_output() as captured:
1361 with capture_output() as captured:
1357 _ip.magic("run -m my_tmp")
1362 _ip.magic("run -m my_tmp")
1358 _ip.run_cell("import my_tmp")
1363 _ip.run_cell("import my_tmp")
1359
1364
1360 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1365 output = "Loaded my_tmp\nI just ran a script\nLoaded my_tmp\n"
1361 assert output == captured.stdout
1366 assert output == captured.stdout
1362
1367
1363 sys.meta_path.pop(0)
1368 sys.meta_path.pop(0)
@@ -1,695 +1,695 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 try:
507 try:
508 old_loop = asyncio.get_running_loop()
508 old_loop = asyncio.get_running_loop()
509 except RuntimeError:
509 except RuntimeError:
510 # This happens when the user used `asyncio.run()`.
510 # This happens when the user used `asyncio.run()`.
511 old_loop = None
511 old_loop = None
512
512
513 asyncio.set_event_loop(self.pt_loop)
513 asyncio.set_event_loop(self.pt_loop)
514 try:
514 try:
515 with patch_stdout(raw=True):
515 with patch_stdout(raw=True):
516 text = self.pt_app.prompt(
516 text = self.pt_app.prompt(
517 default=default,
517 default=default,
518 **self._extra_prompt_options())
518 **self._extra_prompt_options())
519 finally:
519 finally:
520 # Restore the original event loop.
520 # Restore the original event loop.
521 asyncio.set_event_loop(old_loop)
521 asyncio.set_event_loop(old_loop)
522
522
523 return text
523 return text
524
524
525 def enable_win_unicode_console(self):
525 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
526 # 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.
527 # console by default, so WUC shouldn't be needed.
528 from warnings import warn
528 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",
529 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
530 DeprecationWarning,
530 DeprecationWarning,
531 stacklevel=2)
531 stacklevel=2)
532
532
533 def init_io(self):
533 def init_io(self):
534 if sys.platform not in {'win32', 'cli'}:
534 if sys.platform not in {'win32', 'cli'}:
535 return
535 return
536
536
537 import colorama
537 import colorama
538 colorama.init()
538 colorama.init()
539
539
540 # For some reason we make these wrappers around stdout/stderr.
540 # For some reason we make these wrappers around stdout/stderr.
541 # For now, we need to reset them so all output gets coloured.
541 # For now, we need to reset them so all output gets coloured.
542 # https://github.com/ipython/ipython/issues/8669
542 # https://github.com/ipython/ipython/issues/8669
543 # io.std* are deprecated, but don't show our own deprecation warnings
543 # io.std* are deprecated, but don't show our own deprecation warnings
544 # during initialization of the deprecated API.
544 # during initialization of the deprecated API.
545 with warnings.catch_warnings():
545 with warnings.catch_warnings():
546 warnings.simplefilter('ignore', DeprecationWarning)
546 warnings.simplefilter('ignore', DeprecationWarning)
547 io.stdout = io.IOStream(sys.stdout)
547 io.stdout = io.IOStream(sys.stdout)
548 io.stderr = io.IOStream(sys.stderr)
548 io.stderr = io.IOStream(sys.stderr)
549
549
550 def init_magics(self):
550 def init_magics(self):
551 super(TerminalInteractiveShell, self).init_magics()
551 super(TerminalInteractiveShell, self).init_magics()
552 self.register_magics(TerminalMagics)
552 self.register_magics(TerminalMagics)
553
553
554 def init_alias(self):
554 def init_alias(self):
555 # The parent class defines aliases that can be safely used with any
555 # The parent class defines aliases that can be safely used with any
556 # frontend.
556 # frontend.
557 super(TerminalInteractiveShell, self).init_alias()
557 super(TerminalInteractiveShell, self).init_alias()
558
558
559 # Now define aliases that only make sense on the terminal, because they
559 # 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
560 # need direct access to the console in a way that we can't emulate in
561 # GUI or web frontend
561 # GUI or web frontend
562 if os.name == 'posix':
562 if os.name == 'posix':
563 for cmd in ('clear', 'more', 'less', 'man'):
563 for cmd in ('clear', 'more', 'less', 'man'):
564 self.alias_manager.soft_define_alias(cmd, cmd)
564 self.alias_manager.soft_define_alias(cmd, cmd)
565
565
566
566
567 def __init__(self, *args, **kwargs):
567 def __init__(self, *args, **kwargs):
568 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
568 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
569 self.init_prompt_toolkit_cli()
569 self.init_prompt_toolkit_cli()
570 self.init_term_title()
570 self.init_term_title()
571 self.keep_running = True
571 self.keep_running = True
572
572
573
573
574 def ask_exit(self):
574 def ask_exit(self):
575 self.keep_running = False
575 self.keep_running = False
576
576
577 rl_next_input = None
577 rl_next_input = None
578
578
579 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
579 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
580
580
581 if display_banner is not DISPLAY_BANNER_DEPRECATED:
581 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)
582 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
583
583
584 self.keep_running = True
584 self.keep_running = True
585 while self.keep_running:
585 while self.keep_running:
586 print(self.separate_in, end='')
586 print(self.separate_in, end='')
587
587
588 try:
588 try:
589 code = self.prompt_for_code()
589 code = self.prompt_for_code()
590 except EOFError:
590 except EOFError:
591 if (not self.confirm_exit) \
591 if (not self.confirm_exit) \
592 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
592 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
593 self.ask_exit()
593 self.ask_exit()
594
594
595 else:
595 else:
596 if code:
596 if code:
597 self.run_cell(code, store_history=True)
597 self.run_cell(code, store_history=True)
598
598
599 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
599 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
600 # An extra layer of protection in case someone mashing Ctrl-C breaks
600 # An extra layer of protection in case someone mashing Ctrl-C breaks
601 # out of our internal code.
601 # out of our internal code.
602 if display_banner is not DISPLAY_BANNER_DEPRECATED:
602 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)
603 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
604 while True:
604 while True:
605 try:
605 try:
606 self.interact()
606 self.interact()
607 break
607 break
608 except KeyboardInterrupt as e:
608 except KeyboardInterrupt as e:
609 print("\n%s escaped interact()\n" % type(e).__name__)
609 print("\n%s escaped interact()\n" % type(e).__name__)
610 finally:
610 finally:
611 # An interrupt during the eventloop will mess up the
611 # An interrupt during the eventloop will mess up the
612 # internal state of the prompt_toolkit library.
612 # internal state of the prompt_toolkit library.
613 # Stopping the eventloop fixes this, see
613 # Stopping the eventloop fixes this, see
614 # https://github.com/ipython/ipython/pull/9867
614 # https://github.com/ipython/ipython/pull/9867
615 if hasattr(self, '_eventloop'):
615 if hasattr(self, '_eventloop'):
616 self._eventloop.stop()
616 self._eventloop.stop()
617
617
618 self.restore_term_title()
618 self.restore_term_title()
619
619
620 # try to call some at-exit operation optimistically as some things can't
620 # try to call some at-exit operation optimistically as some things can't
621 # be done during interpreter shutdown. this is technically inaccurate as
621 # 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
622 # this make mainlool not re-callable, but that should be a rare if not
623 # in existent use case.
623 # in existent use case.
624
624
625 self._atexit_once()
625 self._atexit_once()
626
626
627
627
628 _inputhook = None
628 _inputhook = None
629 def inputhook(self, context):
629 def inputhook(self, context):
630 if self._inputhook is not None:
630 if self._inputhook is not None:
631 self._inputhook(context)
631 self._inputhook(context)
632
632
633 active_eventloop = None
633 active_eventloop = None
634 def enable_gui(self, gui=None):
634 def enable_gui(self, gui=None):
635 if gui and (gui != 'inline') :
635 if gui and (gui != 'inline') :
636 self.active_eventloop, self._inputhook =\
636 self.active_eventloop, self._inputhook =\
637 get_inputhook_name_and_func(gui)
637 get_inputhook_name_and_func(gui)
638 else:
638 else:
639 self.active_eventloop = self._inputhook = None
639 self.active_eventloop = self._inputhook = None
640
640
641 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
641 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
642 # this inputhook.
642 # this inputhook.
643 if PTK3:
643 if PTK3:
644 import asyncio
644 import asyncio
645 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
645 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
646
646
647 if gui == 'asyncio':
647 if gui == 'asyncio':
648 # When we integrate the asyncio event loop, run the UI in the
648 # 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
649 # 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.)
650 # input hook. (Asyncio is not made for nesting event loops.)
651 self.pt_loop = asyncio.get_event_loop()
651 self.pt_loop = asyncio.get_event_loop_policy().get_event_loop()
652
652
653 elif self._inputhook:
653 elif self._inputhook:
654 # If an inputhook was set, create a new asyncio event loop with
654 # If an inputhook was set, create a new asyncio event loop with
655 # this inputhook for the prompt.
655 # this inputhook for the prompt.
656 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
656 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
657 else:
657 else:
658 # When there's no inputhook, run the prompt in a separate
658 # When there's no inputhook, run the prompt in a separate
659 # asyncio event loop.
659 # asyncio event loop.
660 self.pt_loop = asyncio.new_event_loop()
660 self.pt_loop = asyncio.new_event_loop()
661
661
662 # Run !system commands directly, not through pipes, so terminal programs
662 # Run !system commands directly, not through pipes, so terminal programs
663 # work correctly.
663 # work correctly.
664 system = InteractiveShell.system_raw
664 system = InteractiveShell.system_raw
665
665
666 def auto_rewrite_input(self, cmd):
666 def auto_rewrite_input(self, cmd):
667 """Overridden from the parent class to use fancy rewriting prompt"""
667 """Overridden from the parent class to use fancy rewriting prompt"""
668 if not self.show_rewritten_input:
668 if not self.show_rewritten_input:
669 return
669 return
670
670
671 tokens = self.prompts.rewrite_prompt_tokens()
671 tokens = self.prompts.rewrite_prompt_tokens()
672 if self.pt_app:
672 if self.pt_app:
673 print_formatted_text(PygmentsTokens(tokens), end='',
673 print_formatted_text(PygmentsTokens(tokens), end='',
674 style=self.pt_app.app.style)
674 style=self.pt_app.app.style)
675 print(cmd)
675 print(cmd)
676 else:
676 else:
677 prompt = ''.join(s for t, s in tokens)
677 prompt = ''.join(s for t, s in tokens)
678 print(prompt, cmd, sep='')
678 print(prompt, cmd, sep='')
679
679
680 _prompts_before = None
680 _prompts_before = None
681 def switch_doctest_mode(self, mode):
681 def switch_doctest_mode(self, mode):
682 """Switch prompts to classic for %doctest_mode"""
682 """Switch prompts to classic for %doctest_mode"""
683 if mode:
683 if mode:
684 self._prompts_before = self.prompts
684 self._prompts_before = self.prompts
685 self.prompts = ClassicPrompts(self)
685 self.prompts = ClassicPrompts(self)
686 elif self._prompts_before:
686 elif self._prompts_before:
687 self.prompts = self._prompts_before
687 self.prompts = self._prompts_before
688 self._prompts_before = None
688 self._prompts_before = None
689 # self._update_layout()
689 # self._update_layout()
690
690
691
691
692 InteractiveShellABC.register(TerminalInteractiveShell)
692 InteractiveShellABC.register(TerminalInteractiveShell)
693
693
694 if __name__ == '__main__':
694 if __name__ == '__main__':
695 TerminalInteractiveShell.instance().interact()
695 TerminalInteractiveShell.instance().interact()
@@ -1,64 +1,64 b''
1 """
1 """
2 Inputhook for running the original asyncio event loop while we're waiting for
2 Inputhook for running the original asyncio event loop while we're waiting for
3 input.
3 input.
4
4
5 By default, in IPython, we run the prompt with a different asyncio event loop,
5 By default, in IPython, we run the prompt with a different asyncio event loop,
6 because otherwise we risk that people are freezing the prompt by scheduling bad
6 because otherwise we risk that people are freezing the prompt by scheduling bad
7 coroutines. E.g., a coroutine that does a while/true and never yield back
7 coroutines. E.g., a coroutine that does a while/true and never yield back
8 control to the loop. We can't cancel that.
8 control to the loop. We can't cancel that.
9
9
10 However, sometimes we want the asyncio loop to keep running while waiting for
10 However, sometimes we want the asyncio loop to keep running while waiting for
11 a prompt.
11 a prompt.
12
12
13 The following example will print the numbers from 1 to 10 above the prompt,
13 The following example will print the numbers from 1 to 10 above the prompt,
14 while we are waiting for input. (This works also because we use
14 while we are waiting for input. (This works also because we use
15 prompt_toolkit`s `patch_stdout`)::
15 prompt_toolkit`s `patch_stdout`)::
16
16
17 In [1]: import asyncio
17 In [1]: import asyncio
18
18
19 In [2]: %gui asyncio
19 In [2]: %gui asyncio
20
20
21 In [3]: async def f():
21 In [3]: async def f():
22 ...: for i in range(10):
22 ...: for i in range(10):
23 ...: await asyncio.sleep(1)
23 ...: await asyncio.sleep(1)
24 ...: print(i)
24 ...: print(i)
25
25
26
26
27 In [4]: asyncio.ensure_future(f())
27 In [4]: asyncio.ensure_future(f())
28
28
29 """
29 """
30 import asyncio
30 import asyncio
31 from prompt_toolkit import __version__ as ptk_version
31 from prompt_toolkit import __version__ as ptk_version
32
32
33 PTK3 = ptk_version.startswith('3.')
33 PTK3 = ptk_version.startswith('3.')
34
34
35
35
36 # Keep reference to the original asyncio loop, because getting the event loop
36 # Keep reference to the original asyncio loop, because getting the event loop
37 # within the input hook would return the other loop.
37 # within the input hook would return the other loop.
38 loop = asyncio.get_event_loop()
38 loop = asyncio.get_event_loop_policy().get_event_loop()
39
39
40
40
41 def inputhook(context):
41 def inputhook(context):
42 """
42 """
43 Inputhook for asyncio event loop integration.
43 Inputhook for asyncio event loop integration.
44 """
44 """
45 # For prompt_toolkit 3.0, this input hook literally doesn't do anything.
45 # For prompt_toolkit 3.0, this input hook literally doesn't do anything.
46 # The event loop integration here is implemented in `interactiveshell.py`
46 # The event loop integration here is implemented in `interactiveshell.py`
47 # by running the prompt itself in the current asyncio loop. The main reason
47 # by running the prompt itself in the current asyncio loop. The main reason
48 # for this is that nesting asyncio event loops is unreliable.
48 # for this is that nesting asyncio event loops is unreliable.
49 if PTK3:
49 if PTK3:
50 return
50 return
51
51
52 # For prompt_toolkit 2.0, we can run the current asyncio event loop,
52 # For prompt_toolkit 2.0, we can run the current asyncio event loop,
53 # because prompt_toolkit 2.0 uses a different event loop internally.
53 # because prompt_toolkit 2.0 uses a different event loop internally.
54
54
55 def stop():
55 def stop():
56 loop.stop()
56 loop.stop()
57
57
58 fileno = context.fileno()
58 fileno = context.fileno()
59 loop.add_reader(fileno, stop)
59 loop.add_reader(fileno, stop)
60 try:
60 try:
61 loop.run_forever()
61 loop.run_forever()
62 finally:
62 finally:
63 loop.remove_reader(fileno)
63 loop.remove_reader(fileno)
64
64
General Comments 0
You need to be logged in to leave comments. Login now