##// END OF EJS Templates
Merge pull request #13397 from Carreau/enable-black-terminal...
Matthias Bussonnier -
r27326:e9a96bed merge
parent child Browse files
Show More
@@ -1,563 +1,567 b''
1 """Tests for debugging machinery.
1 """Tests for debugging machinery.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import bdb
7 import bdb
8 import builtins
8 import builtins
9 import os
9 import os
10 import sys
10 import sys
11 import platform
11
12
12 from tempfile import NamedTemporaryFile
13 from tempfile import NamedTemporaryFile
13 from textwrap import dedent
14 from textwrap import dedent
14 from unittest.mock import patch
15 from unittest.mock import patch
15
16
16 from IPython.core import debugger
17 from IPython.core import debugger
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
18 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
18 from IPython.testing.decorators import skip_win32
19 from IPython.testing.decorators import skip_win32
20 import pytest
19
21
20 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
21 # Helper classes, from CPython's Pdb test suite
23 # Helper classes, from CPython's Pdb test suite
22 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
23
25
24 class _FakeInput(object):
26 class _FakeInput(object):
25 """
27 """
26 A fake input stream for pdb's interactive debugger. Whenever a
28 A fake input stream for pdb's interactive debugger. Whenever a
27 line is read, print it (to simulate the user typing it), and then
29 line is read, print it (to simulate the user typing it), and then
28 return it. The set of lines to return is specified in the
30 return it. The set of lines to return is specified in the
29 constructor; they should not have trailing newlines.
31 constructor; they should not have trailing newlines.
30 """
32 """
31 def __init__(self, lines):
33 def __init__(self, lines):
32 self.lines = iter(lines)
34 self.lines = iter(lines)
33
35
34 def readline(self):
36 def readline(self):
35 line = next(self.lines)
37 line = next(self.lines)
36 print(line)
38 print(line)
37 return line+'\n'
39 return line+'\n'
38
40
39 class PdbTestInput(object):
41 class PdbTestInput(object):
40 """Context manager that makes testing Pdb in doctests easier."""
42 """Context manager that makes testing Pdb in doctests easier."""
41
43
42 def __init__(self, input):
44 def __init__(self, input):
43 self.input = input
45 self.input = input
44
46
45 def __enter__(self):
47 def __enter__(self):
46 self.real_stdin = sys.stdin
48 self.real_stdin = sys.stdin
47 sys.stdin = _FakeInput(self.input)
49 sys.stdin = _FakeInput(self.input)
48
50
49 def __exit__(self, *exc):
51 def __exit__(self, *exc):
50 sys.stdin = self.real_stdin
52 sys.stdin = self.real_stdin
51
53
52 #-----------------------------------------------------------------------------
54 #-----------------------------------------------------------------------------
53 # Tests
55 # Tests
54 #-----------------------------------------------------------------------------
56 #-----------------------------------------------------------------------------
55
57
56 def test_ipdb_magics():
58 def test_ipdb_magics():
57 '''Test calling some IPython magics from ipdb.
59 '''Test calling some IPython magics from ipdb.
58
60
59 First, set up some test functions and classes which we can inspect.
61 First, set up some test functions and classes which we can inspect.
60
62
61 >>> class ExampleClass(object):
63 >>> class ExampleClass(object):
62 ... """Docstring for ExampleClass."""
64 ... """Docstring for ExampleClass."""
63 ... def __init__(self):
65 ... def __init__(self):
64 ... """Docstring for ExampleClass.__init__"""
66 ... """Docstring for ExampleClass.__init__"""
65 ... pass
67 ... pass
66 ... def __str__(self):
68 ... def __str__(self):
67 ... return "ExampleClass()"
69 ... return "ExampleClass()"
68
70
69 >>> def example_function(x, y, z="hello"):
71 >>> def example_function(x, y, z="hello"):
70 ... """Docstring for example_function."""
72 ... """Docstring for example_function."""
71 ... pass
73 ... pass
72
74
73 >>> old_trace = sys.gettrace()
75 >>> old_trace = sys.gettrace()
74
76
75 Create a function which triggers ipdb.
77 Create a function which triggers ipdb.
76
78
77 >>> def trigger_ipdb():
79 >>> def trigger_ipdb():
78 ... a = ExampleClass()
80 ... a = ExampleClass()
79 ... debugger.Pdb().set_trace()
81 ... debugger.Pdb().set_trace()
80
82
81 >>> with PdbTestInput([
83 >>> with PdbTestInput([
82 ... 'pdef example_function',
84 ... 'pdef example_function',
83 ... 'pdoc ExampleClass',
85 ... 'pdoc ExampleClass',
84 ... 'up',
86 ... 'up',
85 ... 'down',
87 ... 'down',
86 ... 'list',
88 ... 'list',
87 ... 'pinfo a',
89 ... 'pinfo a',
88 ... 'll',
90 ... 'll',
89 ... 'continue',
91 ... 'continue',
90 ... ]):
92 ... ]):
91 ... trigger_ipdb()
93 ... trigger_ipdb()
92 --Return--
94 --Return--
93 None
95 None
94 > <doctest ...>(3)trigger_ipdb()
96 > <doctest ...>(3)trigger_ipdb()
95 1 def trigger_ipdb():
97 1 def trigger_ipdb():
96 2 a = ExampleClass()
98 2 a = ExampleClass()
97 ----> 3 debugger.Pdb().set_trace()
99 ----> 3 debugger.Pdb().set_trace()
98 <BLANKLINE>
100 <BLANKLINE>
99 ipdb> pdef example_function
101 ipdb> pdef example_function
100 example_function(x, y, z='hello')
102 example_function(x, y, z='hello')
101 ipdb> pdoc ExampleClass
103 ipdb> pdoc ExampleClass
102 Class docstring:
104 Class docstring:
103 Docstring for ExampleClass.
105 Docstring for ExampleClass.
104 Init docstring:
106 Init docstring:
105 Docstring for ExampleClass.__init__
107 Docstring for ExampleClass.__init__
106 ipdb> up
108 ipdb> up
107 > <doctest ...>(11)<module>()
109 > <doctest ...>(11)<module>()
108 7 'pinfo a',
110 7 'pinfo a',
109 8 'll',
111 8 'll',
110 9 'continue',
112 9 'continue',
111 10 ]):
113 10 ]):
112 ---> 11 trigger_ipdb()
114 ---> 11 trigger_ipdb()
113 <BLANKLINE>
115 <BLANKLINE>
114 ipdb> down
116 ipdb> down
115 None
117 None
116 > <doctest ...>(3)trigger_ipdb()
118 > <doctest ...>(3)trigger_ipdb()
117 1 def trigger_ipdb():
119 1 def trigger_ipdb():
118 2 a = ExampleClass()
120 2 a = ExampleClass()
119 ----> 3 debugger.Pdb().set_trace()
121 ----> 3 debugger.Pdb().set_trace()
120 <BLANKLINE>
122 <BLANKLINE>
121 ipdb> list
123 ipdb> list
122 1 def trigger_ipdb():
124 1 def trigger_ipdb():
123 2 a = ExampleClass()
125 2 a = ExampleClass()
124 ----> 3 debugger.Pdb().set_trace()
126 ----> 3 debugger.Pdb().set_trace()
125 <BLANKLINE>
127 <BLANKLINE>
126 ipdb> pinfo a
128 ipdb> pinfo a
127 Type: ExampleClass
129 Type: ExampleClass
128 String form: ExampleClass()
130 String form: ExampleClass()
129 Namespace: Local...
131 Namespace: Local...
130 Docstring: Docstring for ExampleClass.
132 Docstring: Docstring for ExampleClass.
131 Init docstring: Docstring for ExampleClass.__init__
133 Init docstring: Docstring for ExampleClass.__init__
132 ipdb> ll
134 ipdb> ll
133 1 def trigger_ipdb():
135 1 def trigger_ipdb():
134 2 a = ExampleClass()
136 2 a = ExampleClass()
135 ----> 3 debugger.Pdb().set_trace()
137 ----> 3 debugger.Pdb().set_trace()
136 <BLANKLINE>
138 <BLANKLINE>
137 ipdb> continue
139 ipdb> continue
138
140
139 Restore previous trace function, e.g. for coverage.py
141 Restore previous trace function, e.g. for coverage.py
140
142
141 >>> sys.settrace(old_trace)
143 >>> sys.settrace(old_trace)
142 '''
144 '''
143
145
144 def test_ipdb_magics2():
146 def test_ipdb_magics2():
145 '''Test ipdb with a very short function.
147 '''Test ipdb with a very short function.
146
148
147 >>> old_trace = sys.gettrace()
149 >>> old_trace = sys.gettrace()
148
150
149 >>> def bar():
151 >>> def bar():
150 ... pass
152 ... pass
151
153
152 Run ipdb.
154 Run ipdb.
153
155
154 >>> with PdbTestInput([
156 >>> with PdbTestInput([
155 ... 'continue',
157 ... 'continue',
156 ... ]):
158 ... ]):
157 ... debugger.Pdb().runcall(bar)
159 ... debugger.Pdb().runcall(bar)
158 > <doctest ...>(2)bar()
160 > <doctest ...>(2)bar()
159 1 def bar():
161 1 def bar():
160 ----> 2 pass
162 ----> 2 pass
161 <BLANKLINE>
163 <BLANKLINE>
162 ipdb> continue
164 ipdb> continue
163
165
164 Restore previous trace function, e.g. for coverage.py
166 Restore previous trace function, e.g. for coverage.py
165
167
166 >>> sys.settrace(old_trace)
168 >>> sys.settrace(old_trace)
167 '''
169 '''
168
170
169 def can_quit():
171 def can_quit():
170 '''Test that quit work in ipydb
172 '''Test that quit work in ipydb
171
173
172 >>> old_trace = sys.gettrace()
174 >>> old_trace = sys.gettrace()
173
175
174 >>> def bar():
176 >>> def bar():
175 ... pass
177 ... pass
176
178
177 >>> with PdbTestInput([
179 >>> with PdbTestInput([
178 ... 'quit',
180 ... 'quit',
179 ... ]):
181 ... ]):
180 ... debugger.Pdb().runcall(bar)
182 ... debugger.Pdb().runcall(bar)
181 > <doctest ...>(2)bar()
183 > <doctest ...>(2)bar()
182 1 def bar():
184 1 def bar():
183 ----> 2 pass
185 ----> 2 pass
184 <BLANKLINE>
186 <BLANKLINE>
185 ipdb> quit
187 ipdb> quit
186
188
187 Restore previous trace function, e.g. for coverage.py
189 Restore previous trace function, e.g. for coverage.py
188
190
189 >>> sys.settrace(old_trace)
191 >>> sys.settrace(old_trace)
190 '''
192 '''
191
193
192
194
193 def can_exit():
195 def can_exit():
194 '''Test that quit work in ipydb
196 '''Test that quit work in ipydb
195
197
196 >>> old_trace = sys.gettrace()
198 >>> old_trace = sys.gettrace()
197
199
198 >>> def bar():
200 >>> def bar():
199 ... pass
201 ... pass
200
202
201 >>> with PdbTestInput([
203 >>> with PdbTestInput([
202 ... 'exit',
204 ... 'exit',
203 ... ]):
205 ... ]):
204 ... debugger.Pdb().runcall(bar)
206 ... debugger.Pdb().runcall(bar)
205 > <doctest ...>(2)bar()
207 > <doctest ...>(2)bar()
206 1 def bar():
208 1 def bar():
207 ----> 2 pass
209 ----> 2 pass
208 <BLANKLINE>
210 <BLANKLINE>
209 ipdb> exit
211 ipdb> exit
210
212
211 Restore previous trace function, e.g. for coverage.py
213 Restore previous trace function, e.g. for coverage.py
212
214
213 >>> sys.settrace(old_trace)
215 >>> sys.settrace(old_trace)
214 '''
216 '''
215
217
216
218
217 def test_interruptible_core_debugger():
219 def test_interruptible_core_debugger():
218 """The debugger can be interrupted.
220 """The debugger can be interrupted.
219
221
220 The presumption is there is some mechanism that causes a KeyboardInterrupt
222 The presumption is there is some mechanism that causes a KeyboardInterrupt
221 (this is implemented in ipykernel). We want to ensure the
223 (this is implemented in ipykernel). We want to ensure the
222 KeyboardInterrupt cause debugging to cease.
224 KeyboardInterrupt cause debugging to cease.
223 """
225 """
224 def raising_input(msg="", called=[0]):
226 def raising_input(msg="", called=[0]):
225 called[0] += 1
227 called[0] += 1
226 assert called[0] == 1, "input() should only be called once!"
228 assert called[0] == 1, "input() should only be called once!"
227 raise KeyboardInterrupt()
229 raise KeyboardInterrupt()
228
230
229 tracer_orig = sys.gettrace()
231 tracer_orig = sys.gettrace()
230 try:
232 try:
231 with patch.object(builtins, "input", raising_input):
233 with patch.object(builtins, "input", raising_input):
232 debugger.InterruptiblePdb().set_trace()
234 debugger.InterruptiblePdb().set_trace()
233 # The way this test will fail is by set_trace() never exiting,
235 # The way this test will fail is by set_trace() never exiting,
234 # resulting in a timeout by the test runner. The alternative
236 # resulting in a timeout by the test runner. The alternative
235 # implementation would involve a subprocess, but that adds issues
237 # implementation would involve a subprocess, but that adds issues
236 # with interrupting subprocesses that are rather complex, so it's
238 # with interrupting subprocesses that are rather complex, so it's
237 # simpler just to do it this way.
239 # simpler just to do it this way.
238 finally:
240 finally:
239 # restore the original trace function
241 # restore the original trace function
240 sys.settrace(tracer_orig)
242 sys.settrace(tracer_orig)
241
243
242
244
243 @skip_win32
245 @skip_win32
244 def test_xmode_skip():
246 def test_xmode_skip():
245 """that xmode skip frames
247 """that xmode skip frames
246
248
247 Not as a doctest as pytest does not run doctests.
249 Not as a doctest as pytest does not run doctests.
248 """
250 """
249 import pexpect
251 import pexpect
250 env = os.environ.copy()
252 env = os.environ.copy()
251 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
253 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
252
254
253 child = pexpect.spawn(
255 child = pexpect.spawn(
254 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
256 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
255 )
257 )
256 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
258 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
257
259
258 child.expect("IPython")
260 child.expect("IPython")
259 child.expect("\n")
261 child.expect("\n")
260 child.expect_exact("In [1]")
262 child.expect_exact("In [1]")
261
263
262 block = dedent(
264 block = dedent(
263 """
265 """
264 def f():
266 def f():
265 __tracebackhide__ = True
267 __tracebackhide__ = True
266 g()
268 g()
267
269
268 def g():
270 def g():
269 raise ValueError
271 raise ValueError
270
272
271 f()
273 f()
272 """
274 """
273 )
275 )
274
276
275 for line in block.splitlines():
277 for line in block.splitlines():
276 child.sendline(line)
278 child.sendline(line)
277 child.expect_exact(line)
279 child.expect_exact(line)
278 child.expect_exact("skipping")
280 child.expect_exact("skipping")
279
281
280 block = dedent(
282 block = dedent(
281 """
283 """
282 def f():
284 def f():
283 __tracebackhide__ = True
285 __tracebackhide__ = True
284 g()
286 g()
285
287
286 def g():
288 def g():
287 from IPython.core.debugger import set_trace
289 from IPython.core.debugger import set_trace
288 set_trace()
290 set_trace()
289
291
290 f()
292 f()
291 """
293 """
292 )
294 )
293
295
294 for line in block.splitlines():
296 for line in block.splitlines():
295 child.sendline(line)
297 child.sendline(line)
296 child.expect_exact(line)
298 child.expect_exact(line)
297
299
298 child.expect("ipdb>")
300 child.expect("ipdb>")
299 child.sendline("w")
301 child.sendline("w")
300 child.expect("hidden")
302 child.expect("hidden")
301 child.expect("ipdb>")
303 child.expect("ipdb>")
302 child.sendline("skip_hidden false")
304 child.sendline("skip_hidden false")
303 child.sendline("w")
305 child.sendline("w")
304 child.expect("__traceba")
306 child.expect("__traceba")
305 child.expect("ipdb>")
307 child.expect("ipdb>")
306
308
307 child.close()
309 child.close()
308
310
309
311
310 skip_decorators_blocks = (
312 skip_decorators_blocks = (
311 """
313 """
312 def helpers_helper():
314 def helpers_helper():
313 pass # should not stop here except breakpoint
315 pass # should not stop here except breakpoint
314 """,
316 """,
315 """
317 """
316 def helper_1():
318 def helper_1():
317 helpers_helper() # should not stop here
319 helpers_helper() # should not stop here
318 """,
320 """,
319 """
321 """
320 def helper_2():
322 def helper_2():
321 pass # should not stop here
323 pass # should not stop here
322 """,
324 """,
323 """
325 """
324 def pdb_skipped_decorator2(function):
326 def pdb_skipped_decorator2(function):
325 def wrapped_fn(*args, **kwargs):
327 def wrapped_fn(*args, **kwargs):
326 __debuggerskip__ = True
328 __debuggerskip__ = True
327 helper_2()
329 helper_2()
328 __debuggerskip__ = False
330 __debuggerskip__ = False
329 result = function(*args, **kwargs)
331 result = function(*args, **kwargs)
330 __debuggerskip__ = True
332 __debuggerskip__ = True
331 helper_2()
333 helper_2()
332 return result
334 return result
333 return wrapped_fn
335 return wrapped_fn
334 """,
336 """,
335 """
337 """
336 def pdb_skipped_decorator(function):
338 def pdb_skipped_decorator(function):
337 def wrapped_fn(*args, **kwargs):
339 def wrapped_fn(*args, **kwargs):
338 __debuggerskip__ = True
340 __debuggerskip__ = True
339 helper_1()
341 helper_1()
340 __debuggerskip__ = False
342 __debuggerskip__ = False
341 result = function(*args, **kwargs)
343 result = function(*args, **kwargs)
342 __debuggerskip__ = True
344 __debuggerskip__ = True
343 helper_2()
345 helper_2()
344 return result
346 return result
345 return wrapped_fn
347 return wrapped_fn
346 """,
348 """,
347 """
349 """
348 @pdb_skipped_decorator
350 @pdb_skipped_decorator
349 @pdb_skipped_decorator2
351 @pdb_skipped_decorator2
350 def bar(x, y):
352 def bar(x, y):
351 return x * y
353 return x * y
352 """,
354 """,
353 """import IPython.terminal.debugger as ipdb""",
355 """import IPython.terminal.debugger as ipdb""",
354 """
356 """
355 def f():
357 def f():
356 ipdb.set_trace()
358 ipdb.set_trace()
357 bar(3, 4)
359 bar(3, 4)
358 """,
360 """,
359 """
361 """
360 f()
362 f()
361 """,
363 """,
362 )
364 )
363
365
364
366
365 def _decorator_skip_setup():
367 def _decorator_skip_setup():
366 import pexpect
368 import pexpect
367
369
368 env = os.environ.copy()
370 env = os.environ.copy()
369 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
371 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
370
372
371 child = pexpect.spawn(
373 child = pexpect.spawn(
372 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
374 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
373 )
375 )
374 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
376 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
375
377
376 child.expect("IPython")
378 child.expect("IPython")
377 child.expect("\n")
379 child.expect("\n")
378
380
379 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
381 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
380
382
381 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
383 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
382 in_prompt_number = 1
384 in_prompt_number = 1
383 for cblock in dedented_blocks:
385 for cblock in dedented_blocks:
384 child.expect_exact(f"In [{in_prompt_number}]:")
386 child.expect_exact(f"In [{in_prompt_number}]:")
385 in_prompt_number += 1
387 in_prompt_number += 1
386 for line in cblock.splitlines():
388 for line in cblock.splitlines():
387 child.sendline(line)
389 child.sendline(line)
388 child.expect_exact(line)
390 child.expect_exact(line)
389 child.sendline("")
391 child.sendline("")
390 return child
392 return child
391
393
392
394
393 @skip_win32
395 @skip_win32
394 def test_decorator_skip():
396 def test_decorator_skip():
395 """test that decorator frames can be skipped."""
397 """test that decorator frames can be skipped."""
396
398
397 child = _decorator_skip_setup()
399 child = _decorator_skip_setup()
398
400
399 child.expect_exact("3 bar(3, 4)")
401 child.expect_exact("3 bar(3, 4)")
400 child.expect("ipdb>")
402 child.expect("ipdb>")
401
403
402 child.expect("ipdb>")
404 child.expect("ipdb>")
403 child.sendline("step")
405 child.sendline("step")
404 child.expect_exact("step")
406 child.expect_exact("step")
405
407
406 child.expect_exact("1 @pdb_skipped_decorator")
408 child.expect_exact("1 @pdb_skipped_decorator")
407
409
408 child.sendline("s")
410 child.sendline("s")
409 child.expect_exact("return x * y")
411 child.expect_exact("return x * y")
410
412
411 child.close()
413 child.close()
412
414
413
415
416 @pytest.mark.skip(platform.python_implementation() == "PyPy", reason="issues on PyPy")
414 @skip_win32
417 @skip_win32
415 def test_decorator_skip_disabled():
418 def test_decorator_skip_disabled():
416 """test that decorator frame skipping can be disabled"""
419 """test that decorator frame skipping can be disabled"""
417
420
418 child = _decorator_skip_setup()
421 child = _decorator_skip_setup()
419
422
420 child.expect_exact("3 bar(3, 4)")
423 child.expect_exact("3 bar(3, 4)")
421
424
422 for input_, expected in [
425 for input_, expected in [
423 ("skip_predicates debuggerskip False", ""),
426 ("skip_predicates debuggerskip False", ""),
424 ("skip_predicates", "debuggerskip : False"),
427 ("skip_predicates", "debuggerskip : False"),
425 ("step", "---> 2 def wrapped_fn"),
428 ("step", "---> 2 def wrapped_fn"),
426 ("step", "----> 3 __debuggerskip__"),
429 ("step", "----> 3 __debuggerskip__"),
427 ("step", "----> 4 helper_1()"),
430 ("step", "----> 4 helper_1()"),
428 ("step", "---> 1 def helper_1():"),
431 ("step", "---> 1 def helper_1():"),
429 ("next", "----> 2 helpers_helper()"),
432 ("next", "----> 2 helpers_helper()"),
430 ("next", "--Return--"),
433 ("next", "--Return--"),
431 ("next", "----> 5 __debuggerskip__ = False"),
434 ("next", "----> 5 __debuggerskip__ = False"),
432 ]:
435 ]:
433 child.expect("ipdb>")
436 child.expect("ipdb>")
434 child.sendline(input_)
437 child.sendline(input_)
435 child.expect_exact(input_)
438 child.expect_exact(input_)
436 child.expect_exact(expected)
439 child.expect_exact(expected)
437
440
438 child.close()
441 child.close()
439
442
440
443
444 @pytest.mark.skip(platform.python_implementation() == "PyPy", reason="issues on PyPy")
441 @skip_win32
445 @skip_win32
442 def test_decorator_skip_with_breakpoint():
446 def test_decorator_skip_with_breakpoint():
443 """test that decorator frame skipping can be disabled"""
447 """test that decorator frame skipping can be disabled"""
444
448
445 import pexpect
449 import pexpect
446
450
447 env = os.environ.copy()
451 env = os.environ.copy()
448 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
452 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
449
453
450 child = pexpect.spawn(
454 child = pexpect.spawn(
451 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
455 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
452 )
456 )
453 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
457 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
454
458
455 child.expect("IPython")
459 child.expect("IPython")
456 child.expect("\n")
460 child.expect("\n")
457
461
458 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
462 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
459
463
460 ### we need a filename, so we need to exec the full block with a filename
464 ### we need a filename, so we need to exec the full block with a filename
461 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
465 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
462
466
463 name = tf.name[:-3].split("/")[-1]
467 name = tf.name[:-3].split("/")[-1]
464 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
468 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
465 tf.flush()
469 tf.flush()
466 codeblock = f"from {name} import f"
470 codeblock = f"from {name} import f"
467
471
468 dedented_blocks = [
472 dedented_blocks = [
469 codeblock,
473 codeblock,
470 "f()",
474 "f()",
471 ]
475 ]
472
476
473 in_prompt_number = 1
477 in_prompt_number = 1
474 for cblock in dedented_blocks:
478 for cblock in dedented_blocks:
475 child.expect_exact(f"In [{in_prompt_number}]:")
479 child.expect_exact(f"In [{in_prompt_number}]:")
476 in_prompt_number += 1
480 in_prompt_number += 1
477 for line in cblock.splitlines():
481 for line in cblock.splitlines():
478 child.sendline(line)
482 child.sendline(line)
479 child.expect_exact(line)
483 child.expect_exact(line)
480 child.sendline("")
484 child.sendline("")
481
485
482 # as the filename does not exists, we'll rely on the filename prompt
486 # as the filename does not exists, we'll rely on the filename prompt
483 child.expect_exact("47 bar(3, 4)")
487 child.expect_exact("47 bar(3, 4)")
484
488
485 for input_, expected in [
489 for input_, expected in [
486 (f"b {name}.py:3", ""),
490 (f"b {name}.py:3", ""),
487 ("step", "1---> 3 pass # should not stop here except"),
491 ("step", "1---> 3 pass # should not stop here except"),
488 ("step", "---> 38 @pdb_skipped_decorator"),
492 ("step", "---> 38 @pdb_skipped_decorator"),
489 ("continue", ""),
493 ("continue", ""),
490 ]:
494 ]:
491 child.expect("ipdb>")
495 child.expect("ipdb>")
492 child.sendline(input_)
496 child.sendline(input_)
493 child.expect_exact(input_)
497 child.expect_exact(input_)
494 child.expect_exact(expected)
498 child.expect_exact(expected)
495
499
496 child.close()
500 child.close()
497
501
498
502
499 @skip_win32
503 @skip_win32
500 def test_where_erase_value():
504 def test_where_erase_value():
501 """Test that `where` does not access f_locals and erase values."""
505 """Test that `where` does not access f_locals and erase values."""
502 import pexpect
506 import pexpect
503
507
504 env = os.environ.copy()
508 env = os.environ.copy()
505 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
509 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
506
510
507 child = pexpect.spawn(
511 child = pexpect.spawn(
508 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
512 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
509 )
513 )
510 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
514 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
511
515
512 child.expect("IPython")
516 child.expect("IPython")
513 child.expect("\n")
517 child.expect("\n")
514 child.expect_exact("In [1]")
518 child.expect_exact("In [1]")
515
519
516 block = dedent(
520 block = dedent(
517 """
521 """
518 def simple_f():
522 def simple_f():
519 myvar = 1
523 myvar = 1
520 print(myvar)
524 print(myvar)
521 1/0
525 1/0
522 print(myvar)
526 print(myvar)
523 simple_f() """
527 simple_f() """
524 )
528 )
525
529
526 for line in block.splitlines():
530 for line in block.splitlines():
527 child.sendline(line)
531 child.sendline(line)
528 child.expect_exact(line)
532 child.expect_exact(line)
529 child.expect_exact("ZeroDivisionError")
533 child.expect_exact("ZeroDivisionError")
530 child.expect_exact("In [2]:")
534 child.expect_exact("In [2]:")
531
535
532 child.sendline("%debug")
536 child.sendline("%debug")
533
537
534 ##
538 ##
535 child.expect("ipdb>")
539 child.expect("ipdb>")
536
540
537 child.sendline("myvar")
541 child.sendline("myvar")
538 child.expect("1")
542 child.expect("1")
539
543
540 ##
544 ##
541 child.expect("ipdb>")
545 child.expect("ipdb>")
542
546
543 child.sendline("myvar = 2")
547 child.sendline("myvar = 2")
544
548
545 ##
549 ##
546 child.expect_exact("ipdb>")
550 child.expect_exact("ipdb>")
547
551
548 child.sendline("myvar")
552 child.sendline("myvar")
549
553
550 child.expect_exact("2")
554 child.expect_exact("2")
551
555
552 ##
556 ##
553 child.expect("ipdb>")
557 child.expect("ipdb>")
554 child.sendline("where")
558 child.sendline("where")
555
559
556 ##
560 ##
557 child.expect("ipdb>")
561 child.expect("ipdb>")
558 child.sendline("myvar")
562 child.sendline("myvar")
559
563
560 child.expect_exact("2")
564 child.expect_exact("2")
561 child.expect("ipdb>")
565 child.expect("ipdb>")
562
566
563 child.close()
567 child.close()
@@ -1,681 +1,692 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 PTK3 = ptk_version.startswith('3.')
54 PTK3 = ptk_version.startswith('3.')
55
55
56
56
57 class _NoStyle(Style): pass
57 class _NoStyle(Style): pass
58
58
59
59
60
60
61 _style_overrides_light_bg = {
61 _style_overrides_light_bg = {
62 Token.Prompt: '#ansibrightblue',
62 Token.Prompt: '#ansibrightblue',
63 Token.PromptNum: '#ansiblue bold',
63 Token.PromptNum: '#ansiblue bold',
64 Token.OutPrompt: '#ansibrightred',
64 Token.OutPrompt: '#ansibrightred',
65 Token.OutPromptNum: '#ansired bold',
65 Token.OutPromptNum: '#ansired bold',
66 }
66 }
67
67
68 _style_overrides_linux = {
68 _style_overrides_linux = {
69 Token.Prompt: '#ansibrightgreen',
69 Token.Prompt: '#ansibrightgreen',
70 Token.PromptNum: '#ansigreen bold',
70 Token.PromptNum: '#ansigreen bold',
71 Token.OutPrompt: '#ansibrightred',
71 Token.OutPrompt: '#ansibrightred',
72 Token.OutPromptNum: '#ansired bold',
72 Token.OutPromptNum: '#ansired bold',
73 }
73 }
74
74
75 def get_default_editor():
75 def get_default_editor():
76 try:
76 try:
77 return os.environ['EDITOR']
77 return os.environ['EDITOR']
78 except KeyError:
78 except KeyError:
79 pass
79 pass
80 except UnicodeError:
80 except UnicodeError:
81 warn("$EDITOR environment variable is not pure ASCII. Using platform "
81 warn("$EDITOR environment variable is not pure ASCII. Using platform "
82 "default editor.")
82 "default editor.")
83
83
84 if os.name == 'posix':
84 if os.name == 'posix':
85 return 'vi' # the only one guaranteed to be there!
85 return 'vi' # the only one guaranteed to be there!
86 else:
86 else:
87 return 'notepad' # same in Windows!
87 return 'notepad' # same in Windows!
88
88
89 # conservatively check for tty
89 # conservatively check for tty
90 # overridden streams can result in things like:
90 # overridden streams can result in things like:
91 # - sys.stdin = None
91 # - sys.stdin = None
92 # - no isatty method
92 # - no isatty method
93 for _name in ('stdin', 'stdout', 'stderr'):
93 for _name in ('stdin', 'stdout', 'stderr'):
94 _stream = getattr(sys, _name)
94 _stream = getattr(sys, _name)
95 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
95 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
96 _is_tty = False
96 _is_tty = False
97 break
97 break
98 else:
98 else:
99 _is_tty = True
99 _is_tty = True
100
100
101
101
102 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
102 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
103
103
104 def black_reformat_handler(text_before_cursor):
104 def black_reformat_handler(text_before_cursor):
105 """
106 We do not need to protect against error,
107 this is taken care at a higher level where any reformat error is ignored.
108 Indeed we may call reformatting on incomplete code.
109 """
105 import black
110 import black
111
106 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
112 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
107 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
113 if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
108 formatted_text = formatted_text[:-1]
114 formatted_text = formatted_text[:-1]
109 return formatted_text
115 return formatted_text
110
116
111
117
112 class TerminalInteractiveShell(InteractiveShell):
118 class TerminalInteractiveShell(InteractiveShell):
113 mime_renderers = Dict().tag(config=True)
119 mime_renderers = Dict().tag(config=True)
114
120
115 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
121 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
116 'to reserve for the tab completion menu, '
122 'to reserve for the tab completion menu, '
117 'search history, ...etc, the height of '
123 'search history, ...etc, the height of '
118 'these menus will at most this value. '
124 'these menus will at most this value. '
119 'Increase it is you prefer long and skinny '
125 'Increase it is you prefer long and skinny '
120 'menus, decrease for short and wide.'
126 'menus, decrease for short and wide.'
121 ).tag(config=True)
127 ).tag(config=True)
122
128
123 pt_app = None
129 pt_app = None
124 debugger_history = None
130 debugger_history = None
125
131
126 debugger_history_file = Unicode(
132 debugger_history_file = Unicode(
127 "~/.pdbhistory", help="File in which to store and read history"
133 "~/.pdbhistory", help="File in which to store and read history"
128 ).tag(config=True)
134 ).tag(config=True)
129
135
130 simple_prompt = Bool(_use_simple_prompt,
136 simple_prompt = Bool(_use_simple_prompt,
131 help="""Use `raw_input` for the REPL, without completion and prompt colors.
137 help="""Use `raw_input` for the REPL, without completion and prompt colors.
132
138
133 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
139 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
134 IPython own testing machinery, and emacs inferior-shell integration through elpy.
140 IPython own testing machinery, and emacs inferior-shell integration through elpy.
135
141
136 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
142 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
137 environment variable is set, or the current terminal is not a tty."""
143 environment variable is set, or the current terminal is not a tty."""
138 ).tag(config=True)
144 ).tag(config=True)
139
145
140 @property
146 @property
141 def debugger_cls(self):
147 def debugger_cls(self):
142 return Pdb if self.simple_prompt else TerminalPdb
148 return Pdb if self.simple_prompt else TerminalPdb
143
149
144 confirm_exit = Bool(True,
150 confirm_exit = Bool(True,
145 help="""
151 help="""
146 Set to confirm when you try to exit IPython with an EOF (Control-D
152 Set to confirm when you try to exit IPython with an EOF (Control-D
147 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
153 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
148 you can force a direct exit without any confirmation.""",
154 you can force a direct exit without any confirmation.""",
149 ).tag(config=True)
155 ).tag(config=True)
150
156
151 editing_mode = Unicode('emacs',
157 editing_mode = Unicode('emacs',
152 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
158 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
153 ).tag(config=True)
159 ).tag(config=True)
154
160
155 emacs_bindings_in_vi_insert_mode = Bool(
161 emacs_bindings_in_vi_insert_mode = Bool(
156 True,
162 True,
157 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
163 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
158 ).tag(config=True)
164 ).tag(config=True)
159
165
160 modal_cursor = Bool(
166 modal_cursor = Bool(
161 True,
167 True,
162 help="""
168 help="""
163 Cursor shape changes depending on vi mode: beam in vi insert mode,
169 Cursor shape changes depending on vi mode: beam in vi insert mode,
164 block in nav mode, underscore in replace mode.""",
170 block in nav mode, underscore in replace mode.""",
165 ).tag(config=True)
171 ).tag(config=True)
166
172
167 ttimeoutlen = Float(
173 ttimeoutlen = Float(
168 0.01,
174 0.01,
169 help="""The time in milliseconds that is waited for a key code
175 help="""The time in milliseconds that is waited for a key code
170 to complete.""",
176 to complete.""",
171 ).tag(config=True)
177 ).tag(config=True)
172
178
173 timeoutlen = Float(
179 timeoutlen = Float(
174 0.5,
180 0.5,
175 help="""The time in milliseconds that is waited for a mapped key
181 help="""The time in milliseconds that is waited for a mapped key
176 sequence to complete.""",
182 sequence to complete.""",
177 ).tag(config=True)
183 ).tag(config=True)
178
184
179 autoformatter = Unicode(None,
185 autoformatter = Unicode(
186 "black",
180 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
187 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
181 allow_none=True
188 allow_none=True
182 ).tag(config=True)
189 ).tag(config=True)
183
190
184 mouse_support = Bool(False,
191 mouse_support = Bool(False,
185 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
192 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
186 ).tag(config=True)
193 ).tag(config=True)
187
194
188 # We don't load the list of styles for the help string, because loading
195 # We don't load the list of styles for the help string, because loading
189 # Pygments plugins takes time and can cause unexpected errors.
196 # Pygments plugins takes time and can cause unexpected errors.
190 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
197 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
191 help="""The name or class of a Pygments style to use for syntax
198 help="""The name or class of a Pygments style to use for syntax
192 highlighting. To see available styles, run `pygmentize -L styles`."""
199 highlighting. To see available styles, run `pygmentize -L styles`."""
193 ).tag(config=True)
200 ).tag(config=True)
194
201
195 @validate('editing_mode')
202 @validate('editing_mode')
196 def _validate_editing_mode(self, proposal):
203 def _validate_editing_mode(self, proposal):
197 if proposal['value'].lower() == 'vim':
204 if proposal['value'].lower() == 'vim':
198 proposal['value']= 'vi'
205 proposal['value']= 'vi'
199 elif proposal['value'].lower() == 'default':
206 elif proposal['value'].lower() == 'default':
200 proposal['value']= 'emacs'
207 proposal['value']= 'emacs'
201
208
202 if hasattr(EditingMode, proposal['value'].upper()):
209 if hasattr(EditingMode, proposal['value'].upper()):
203 return proposal['value'].lower()
210 return proposal['value'].lower()
204
211
205 return self.editing_mode
212 return self.editing_mode
206
213
207
214
208 @observe('editing_mode')
215 @observe('editing_mode')
209 def _editing_mode(self, change):
216 def _editing_mode(self, change):
210 if self.pt_app:
217 if self.pt_app:
211 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
218 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
212
219
213 @observe('autoformatter')
220 def _set_formatter(self, formatter):
214 def _autoformatter_changed(self, change):
215 formatter = change.new
216 if formatter is None:
221 if formatter is None:
217 self.reformat_handler = lambda x:x
222 self.reformat_handler = lambda x:x
218 elif formatter == 'black':
223 elif formatter == 'black':
219 self.reformat_handler = black_reformat_handler
224 self.reformat_handler = black_reformat_handler
220 else:
225 else:
221 raise ValueError
226 raise ValueError
222
227
228 @observe("autoformatter")
229 def _autoformatter_changed(self, change):
230 formatter = change.new
231 self._set_formatter(formatter)
232
223 @observe('highlighting_style')
233 @observe('highlighting_style')
224 @observe('colors')
234 @observe('colors')
225 def _highlighting_style_changed(self, change):
235 def _highlighting_style_changed(self, change):
226 self.refresh_style()
236 self.refresh_style()
227
237
228 def refresh_style(self):
238 def refresh_style(self):
229 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
239 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
230
240
231
241
232 highlighting_style_overrides = Dict(
242 highlighting_style_overrides = Dict(
233 help="Override highlighting format for specific tokens"
243 help="Override highlighting format for specific tokens"
234 ).tag(config=True)
244 ).tag(config=True)
235
245
236 true_color = Bool(False,
246 true_color = Bool(False,
237 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
247 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
238 If your terminal supports true color, the following command should
248 If your terminal supports true color, the following command should
239 print ``TRUECOLOR`` in orange::
249 print ``TRUECOLOR`` in orange::
240
250
241 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
251 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
242 """,
252 """,
243 ).tag(config=True)
253 ).tag(config=True)
244
254
245 editor = Unicode(get_default_editor(),
255 editor = Unicode(get_default_editor(),
246 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
256 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
247 ).tag(config=True)
257 ).tag(config=True)
248
258
249 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
259 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
250
260
251 prompts = Instance(Prompts)
261 prompts = Instance(Prompts)
252
262
253 @default('prompts')
263 @default('prompts')
254 def _prompts_default(self):
264 def _prompts_default(self):
255 return self.prompts_class(self)
265 return self.prompts_class(self)
256
266
257 # @observe('prompts')
267 # @observe('prompts')
258 # def _(self, change):
268 # def _(self, change):
259 # self._update_layout()
269 # self._update_layout()
260
270
261 @default('displayhook_class')
271 @default('displayhook_class')
262 def _displayhook_class_default(self):
272 def _displayhook_class_default(self):
263 return RichPromptDisplayHook
273 return RichPromptDisplayHook
264
274
265 term_title = Bool(True,
275 term_title = Bool(True,
266 help="Automatically set the terminal title"
276 help="Automatically set the terminal title"
267 ).tag(config=True)
277 ).tag(config=True)
268
278
269 term_title_format = Unicode("IPython: {cwd}",
279 term_title_format = Unicode("IPython: {cwd}",
270 help="Customize the terminal title format. This is a python format string. " +
280 help="Customize the terminal title format. This is a python format string. " +
271 "Available substitutions are: {cwd}."
281 "Available substitutions are: {cwd}."
272 ).tag(config=True)
282 ).tag(config=True)
273
283
274 display_completions = Enum(('column', 'multicolumn','readlinelike'),
284 display_completions = Enum(('column', 'multicolumn','readlinelike'),
275 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
285 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
276 "'readlinelike'. These options are for `prompt_toolkit`, see "
286 "'readlinelike'. These options are for `prompt_toolkit`, see "
277 "`prompt_toolkit` documentation for more information."
287 "`prompt_toolkit` documentation for more information."
278 ),
288 ),
279 default_value='multicolumn').tag(config=True)
289 default_value='multicolumn').tag(config=True)
280
290
281 highlight_matching_brackets = Bool(True,
291 highlight_matching_brackets = Bool(True,
282 help="Highlight matching brackets.",
292 help="Highlight matching brackets.",
283 ).tag(config=True)
293 ).tag(config=True)
284
294
285 extra_open_editor_shortcuts = Bool(False,
295 extra_open_editor_shortcuts = Bool(False,
286 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
296 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
287 "This is in addition to the F2 binding, which is always enabled."
297 "This is in addition to the F2 binding, which is always enabled."
288 ).tag(config=True)
298 ).tag(config=True)
289
299
290 handle_return = Any(None,
300 handle_return = Any(None,
291 help="Provide an alternative handler to be called when the user presses "
301 help="Provide an alternative handler to be called when the user presses "
292 "Return. This is an advanced option intended for debugging, which "
302 "Return. This is an advanced option intended for debugging, which "
293 "may be changed or removed in later releases."
303 "may be changed or removed in later releases."
294 ).tag(config=True)
304 ).tag(config=True)
295
305
296 enable_history_search = Bool(True,
306 enable_history_search = Bool(True,
297 help="Allows to enable/disable the prompt toolkit history search"
307 help="Allows to enable/disable the prompt toolkit history search"
298 ).tag(config=True)
308 ).tag(config=True)
299
309
300 prompt_includes_vi_mode = Bool(True,
310 prompt_includes_vi_mode = Bool(True,
301 help="Display the current vi mode (when using vi editing mode)."
311 help="Display the current vi mode (when using vi editing mode)."
302 ).tag(config=True)
312 ).tag(config=True)
303
313
304 @observe('term_title')
314 @observe('term_title')
305 def init_term_title(self, change=None):
315 def init_term_title(self, change=None):
306 # Enable or disable the terminal title.
316 # Enable or disable the terminal title.
307 if self.term_title:
317 if self.term_title:
308 toggle_set_term_title(True)
318 toggle_set_term_title(True)
309 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
319 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
310 else:
320 else:
311 toggle_set_term_title(False)
321 toggle_set_term_title(False)
312
322
313 def restore_term_title(self):
323 def restore_term_title(self):
314 if self.term_title:
324 if self.term_title:
315 restore_term_title()
325 restore_term_title()
316
326
317 def init_display_formatter(self):
327 def init_display_formatter(self):
318 super(TerminalInteractiveShell, self).init_display_formatter()
328 super(TerminalInteractiveShell, self).init_display_formatter()
319 # terminal only supports plain text
329 # terminal only supports plain text
320 self.display_formatter.active_types = ['text/plain']
330 self.display_formatter.active_types = ['text/plain']
321 # disable `_ipython_display_`
331 # disable `_ipython_display_`
322 self.display_formatter.ipython_display_formatter.enabled = False
332 self.display_formatter.ipython_display_formatter.enabled = False
323
333
324 def init_prompt_toolkit_cli(self):
334 def init_prompt_toolkit_cli(self):
325 if self.simple_prompt:
335 if self.simple_prompt:
326 # Fall back to plain non-interactive output for tests.
336 # Fall back to plain non-interactive output for tests.
327 # This is very limited.
337 # This is very limited.
328 def prompt():
338 def prompt():
329 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
339 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
330 lines = [input(prompt_text)]
340 lines = [input(prompt_text)]
331 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
341 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
332 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
342 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
333 lines.append( input(prompt_continuation) )
343 lines.append( input(prompt_continuation) )
334 return '\n'.join(lines)
344 return '\n'.join(lines)
335 self.prompt_for_code = prompt
345 self.prompt_for_code = prompt
336 return
346 return
337
347
338 # Set up keyboard shortcuts
348 # Set up keyboard shortcuts
339 key_bindings = create_ipython_shortcuts(self)
349 key_bindings = create_ipython_shortcuts(self)
340
350
341 # Pre-populate history from IPython's history database
351 # Pre-populate history from IPython's history database
342 history = InMemoryHistory()
352 history = InMemoryHistory()
343 last_cell = u""
353 last_cell = u""
344 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
354 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
345 include_latest=True):
355 include_latest=True):
346 # Ignore blank lines and consecutive duplicates
356 # Ignore blank lines and consecutive duplicates
347 cell = cell.rstrip()
357 cell = cell.rstrip()
348 if cell and (cell != last_cell):
358 if cell and (cell != last_cell):
349 history.append_string(cell)
359 history.append_string(cell)
350 last_cell = cell
360 last_cell = cell
351
361
352 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
362 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
353 self.style = DynamicStyle(lambda: self._style)
363 self.style = DynamicStyle(lambda: self._style)
354
364
355 editing_mode = getattr(EditingMode, self.editing_mode.upper())
365 editing_mode = getattr(EditingMode, self.editing_mode.upper())
356
366
357 self.pt_loop = asyncio.new_event_loop()
367 self.pt_loop = asyncio.new_event_loop()
358 self.pt_app = PromptSession(
368 self.pt_app = PromptSession(
359 auto_suggest=AutoSuggestFromHistory(),
369 auto_suggest=AutoSuggestFromHistory(),
360 editing_mode=editing_mode,
370 editing_mode=editing_mode,
361 key_bindings=key_bindings,
371 key_bindings=key_bindings,
362 history=history,
372 history=history,
363 completer=IPythonPTCompleter(shell=self),
373 completer=IPythonPTCompleter(shell=self),
364 enable_history_search=self.enable_history_search,
374 enable_history_search=self.enable_history_search,
365 style=self.style,
375 style=self.style,
366 include_default_pygments_style=False,
376 include_default_pygments_style=False,
367 mouse_support=self.mouse_support,
377 mouse_support=self.mouse_support,
368 enable_open_in_editor=self.extra_open_editor_shortcuts,
378 enable_open_in_editor=self.extra_open_editor_shortcuts,
369 color_depth=self.color_depth,
379 color_depth=self.color_depth,
370 tempfile_suffix=".py",
380 tempfile_suffix=".py",
371 **self._extra_prompt_options()
381 **self._extra_prompt_options()
372 )
382 )
373
383
374 def _make_style_from_name_or_cls(self, name_or_cls):
384 def _make_style_from_name_or_cls(self, name_or_cls):
375 """
385 """
376 Small wrapper that make an IPython compatible style from a style name
386 Small wrapper that make an IPython compatible style from a style name
377
387
378 We need that to add style for prompt ... etc.
388 We need that to add style for prompt ... etc.
379 """
389 """
380 style_overrides = {}
390 style_overrides = {}
381 if name_or_cls == 'legacy':
391 if name_or_cls == 'legacy':
382 legacy = self.colors.lower()
392 legacy = self.colors.lower()
383 if legacy == 'linux':
393 if legacy == 'linux':
384 style_cls = get_style_by_name('monokai')
394 style_cls = get_style_by_name('monokai')
385 style_overrides = _style_overrides_linux
395 style_overrides = _style_overrides_linux
386 elif legacy == 'lightbg':
396 elif legacy == 'lightbg':
387 style_overrides = _style_overrides_light_bg
397 style_overrides = _style_overrides_light_bg
388 style_cls = get_style_by_name('pastie')
398 style_cls = get_style_by_name('pastie')
389 elif legacy == 'neutral':
399 elif legacy == 'neutral':
390 # The default theme needs to be visible on both a dark background
400 # The default theme needs to be visible on both a dark background
391 # and a light background, because we can't tell what the terminal
401 # and a light background, because we can't tell what the terminal
392 # looks like. These tweaks to the default theme help with that.
402 # looks like. These tweaks to the default theme help with that.
393 style_cls = get_style_by_name('default')
403 style_cls = get_style_by_name('default')
394 style_overrides.update({
404 style_overrides.update({
395 Token.Number: '#ansigreen',
405 Token.Number: '#ansigreen',
396 Token.Operator: 'noinherit',
406 Token.Operator: 'noinherit',
397 Token.String: '#ansiyellow',
407 Token.String: '#ansiyellow',
398 Token.Name.Function: '#ansiblue',
408 Token.Name.Function: '#ansiblue',
399 Token.Name.Class: 'bold #ansiblue',
409 Token.Name.Class: 'bold #ansiblue',
400 Token.Name.Namespace: 'bold #ansiblue',
410 Token.Name.Namespace: 'bold #ansiblue',
401 Token.Name.Variable.Magic: '#ansiblue',
411 Token.Name.Variable.Magic: '#ansiblue',
402 Token.Prompt: '#ansigreen',
412 Token.Prompt: '#ansigreen',
403 Token.PromptNum: '#ansibrightgreen bold',
413 Token.PromptNum: '#ansibrightgreen bold',
404 Token.OutPrompt: '#ansired',
414 Token.OutPrompt: '#ansired',
405 Token.OutPromptNum: '#ansibrightred bold',
415 Token.OutPromptNum: '#ansibrightred bold',
406 })
416 })
407
417
408 # Hack: Due to limited color support on the Windows console
418 # Hack: Due to limited color support on the Windows console
409 # the prompt colors will be wrong without this
419 # the prompt colors will be wrong without this
410 if os.name == 'nt':
420 if os.name == 'nt':
411 style_overrides.update({
421 style_overrides.update({
412 Token.Prompt: '#ansidarkgreen',
422 Token.Prompt: '#ansidarkgreen',
413 Token.PromptNum: '#ansigreen bold',
423 Token.PromptNum: '#ansigreen bold',
414 Token.OutPrompt: '#ansidarkred',
424 Token.OutPrompt: '#ansidarkred',
415 Token.OutPromptNum: '#ansired bold',
425 Token.OutPromptNum: '#ansired bold',
416 })
426 })
417 elif legacy =='nocolor':
427 elif legacy =='nocolor':
418 style_cls=_NoStyle
428 style_cls=_NoStyle
419 style_overrides = {}
429 style_overrides = {}
420 else :
430 else :
421 raise ValueError('Got unknown colors: ', legacy)
431 raise ValueError('Got unknown colors: ', legacy)
422 else :
432 else :
423 if isinstance(name_or_cls, str):
433 if isinstance(name_or_cls, str):
424 style_cls = get_style_by_name(name_or_cls)
434 style_cls = get_style_by_name(name_or_cls)
425 else:
435 else:
426 style_cls = name_or_cls
436 style_cls = name_or_cls
427 style_overrides = {
437 style_overrides = {
428 Token.Prompt: '#ansigreen',
438 Token.Prompt: '#ansigreen',
429 Token.PromptNum: '#ansibrightgreen bold',
439 Token.PromptNum: '#ansibrightgreen bold',
430 Token.OutPrompt: '#ansired',
440 Token.OutPrompt: '#ansired',
431 Token.OutPromptNum: '#ansibrightred bold',
441 Token.OutPromptNum: '#ansibrightred bold',
432 }
442 }
433 style_overrides.update(self.highlighting_style_overrides)
443 style_overrides.update(self.highlighting_style_overrides)
434 style = merge_styles([
444 style = merge_styles([
435 style_from_pygments_cls(style_cls),
445 style_from_pygments_cls(style_cls),
436 style_from_pygments_dict(style_overrides),
446 style_from_pygments_dict(style_overrides),
437 ])
447 ])
438
448
439 return style
449 return style
440
450
441 @property
451 @property
442 def pt_complete_style(self):
452 def pt_complete_style(self):
443 return {
453 return {
444 'multicolumn': CompleteStyle.MULTI_COLUMN,
454 'multicolumn': CompleteStyle.MULTI_COLUMN,
445 'column': CompleteStyle.COLUMN,
455 'column': CompleteStyle.COLUMN,
446 'readlinelike': CompleteStyle.READLINE_LIKE,
456 'readlinelike': CompleteStyle.READLINE_LIKE,
447 }[self.display_completions]
457 }[self.display_completions]
448
458
449 @property
459 @property
450 def color_depth(self):
460 def color_depth(self):
451 return (ColorDepth.TRUE_COLOR if self.true_color else None)
461 return (ColorDepth.TRUE_COLOR if self.true_color else None)
452
462
453 def _extra_prompt_options(self):
463 def _extra_prompt_options(self):
454 """
464 """
455 Return the current layout option for the current Terminal InteractiveShell
465 Return the current layout option for the current Terminal InteractiveShell
456 """
466 """
457 def get_message():
467 def get_message():
458 return PygmentsTokens(self.prompts.in_prompt_tokens())
468 return PygmentsTokens(self.prompts.in_prompt_tokens())
459
469
460 if self.editing_mode == 'emacs':
470 if self.editing_mode == 'emacs':
461 # with emacs mode the prompt is (usually) static, so we call only
471 # with emacs mode the prompt is (usually) static, so we call only
462 # the function once. With VI mode it can toggle between [ins] and
472 # the function once. With VI mode it can toggle between [ins] and
463 # [nor] so we can't precompute.
473 # [nor] so we can't precompute.
464 # here I'm going to favor the default keybinding which almost
474 # here I'm going to favor the default keybinding which almost
465 # everybody uses to decrease CPU usage.
475 # everybody uses to decrease CPU usage.
466 # if we have issues with users with custom Prompts we can see how to
476 # if we have issues with users with custom Prompts we can see how to
467 # work around this.
477 # work around this.
468 get_message = get_message()
478 get_message = get_message()
469
479
470 options = {
480 options = {
471 'complete_in_thread': False,
481 'complete_in_thread': False,
472 'lexer':IPythonPTLexer(),
482 'lexer':IPythonPTLexer(),
473 'reserve_space_for_menu':self.space_for_menu,
483 'reserve_space_for_menu':self.space_for_menu,
474 'message': get_message,
484 'message': get_message,
475 'prompt_continuation': (
485 'prompt_continuation': (
476 lambda width, lineno, is_soft_wrap:
486 lambda width, lineno, is_soft_wrap:
477 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
487 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
478 'multiline': True,
488 'multiline': True,
479 'complete_style': self.pt_complete_style,
489 'complete_style': self.pt_complete_style,
480
490
481 # Highlight matching brackets, but only when this setting is
491 # Highlight matching brackets, but only when this setting is
482 # enabled, and only when the DEFAULT_BUFFER has the focus.
492 # enabled, and only when the DEFAULT_BUFFER has the focus.
483 'input_processors': [ConditionalProcessor(
493 'input_processors': [ConditionalProcessor(
484 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
494 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
485 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
495 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
486 Condition(lambda: self.highlight_matching_brackets))],
496 Condition(lambda: self.highlight_matching_brackets))],
487 }
497 }
488 if not PTK3:
498 if not PTK3:
489 options['inputhook'] = self.inputhook
499 options['inputhook'] = self.inputhook
490
500
491 return options
501 return options
492
502
493 def prompt_for_code(self):
503 def prompt_for_code(self):
494 if self.rl_next_input:
504 if self.rl_next_input:
495 default = self.rl_next_input
505 default = self.rl_next_input
496 self.rl_next_input = None
506 self.rl_next_input = None
497 else:
507 else:
498 default = ''
508 default = ''
499
509
500 # In order to make sure that asyncio code written in the
510 # In order to make sure that asyncio code written in the
501 # interactive shell doesn't interfere with the prompt, we run the
511 # interactive shell doesn't interfere with the prompt, we run the
502 # prompt in a different event loop.
512 # prompt in a different event loop.
503 # If we don't do this, people could spawn coroutine with a
513 # If we don't do this, people could spawn coroutine with a
504 # while/true inside which will freeze the prompt.
514 # while/true inside which will freeze the prompt.
505
515
506 policy = asyncio.get_event_loop_policy()
516 policy = asyncio.get_event_loop_policy()
507 try:
517 try:
508 old_loop = policy.get_event_loop()
518 old_loop = policy.get_event_loop()
509 except RuntimeError:
519 except RuntimeError:
510 # This happens when the the event loop is closed,
520 # This happens when the the event loop is closed,
511 # e.g. by calling `asyncio.run()`.
521 # e.g. by calling `asyncio.run()`.
512 old_loop = None
522 old_loop = None
513
523
514 policy.set_event_loop(self.pt_loop)
524 policy.set_event_loop(self.pt_loop)
515 try:
525 try:
516 with patch_stdout(raw=True):
526 with patch_stdout(raw=True):
517 text = self.pt_app.prompt(
527 text = self.pt_app.prompt(
518 default=default,
528 default=default,
519 **self._extra_prompt_options())
529 **self._extra_prompt_options())
520 finally:
530 finally:
521 # Restore the original event loop.
531 # Restore the original event loop.
522 if old_loop is not None:
532 if old_loop is not None:
523 policy.set_event_loop(old_loop)
533 policy.set_event_loop(old_loop)
524
534
525 return text
535 return text
526
536
527 def enable_win_unicode_console(self):
537 def enable_win_unicode_console(self):
528 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
538 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
529 # console by default, so WUC shouldn't be needed.
539 # console by default, so WUC shouldn't be needed.
530 from warnings import warn
540 from warnings import warn
531 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
541 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
532 DeprecationWarning,
542 DeprecationWarning,
533 stacklevel=2)
543 stacklevel=2)
534
544
535 def init_io(self):
545 def init_io(self):
536 if sys.platform not in {'win32', 'cli'}:
546 if sys.platform not in {'win32', 'cli'}:
537 return
547 return
538
548
539 import colorama
549 import colorama
540 colorama.init()
550 colorama.init()
541
551
542 def init_magics(self):
552 def init_magics(self):
543 super(TerminalInteractiveShell, self).init_magics()
553 super(TerminalInteractiveShell, self).init_magics()
544 self.register_magics(TerminalMagics)
554 self.register_magics(TerminalMagics)
545
555
546 def init_alias(self):
556 def init_alias(self):
547 # The parent class defines aliases that can be safely used with any
557 # The parent class defines aliases that can be safely used with any
548 # frontend.
558 # frontend.
549 super(TerminalInteractiveShell, self).init_alias()
559 super(TerminalInteractiveShell, self).init_alias()
550
560
551 # Now define aliases that only make sense on the terminal, because they
561 # Now define aliases that only make sense on the terminal, because they
552 # need direct access to the console in a way that we can't emulate in
562 # need direct access to the console in a way that we can't emulate in
553 # GUI or web frontend
563 # GUI or web frontend
554 if os.name == 'posix':
564 if os.name == 'posix':
555 for cmd in ('clear', 'more', 'less', 'man'):
565 for cmd in ('clear', 'more', 'less', 'man'):
556 self.alias_manager.soft_define_alias(cmd, cmd)
566 self.alias_manager.soft_define_alias(cmd, cmd)
557
567
558
568
559 def __init__(self, *args, **kwargs):
569 def __init__(self, *args, **kwargs):
560 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
570 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
561 self.init_prompt_toolkit_cli()
571 self.init_prompt_toolkit_cli()
562 self.init_term_title()
572 self.init_term_title()
563 self.keep_running = True
573 self.keep_running = True
574 self._set_formatter(self.autoformatter)
564
575
565
576
566 def ask_exit(self):
577 def ask_exit(self):
567 self.keep_running = False
578 self.keep_running = False
568
579
569 rl_next_input = None
580 rl_next_input = None
570
581
571 def interact(self):
582 def interact(self):
572 self.keep_running = True
583 self.keep_running = True
573 while self.keep_running:
584 while self.keep_running:
574 print(self.separate_in, end='')
585 print(self.separate_in, end='')
575
586
576 try:
587 try:
577 code = self.prompt_for_code()
588 code = self.prompt_for_code()
578 except EOFError:
589 except EOFError:
579 if (not self.confirm_exit) \
590 if (not self.confirm_exit) \
580 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
591 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
581 self.ask_exit()
592 self.ask_exit()
582
593
583 else:
594 else:
584 if code:
595 if code:
585 self.run_cell(code, store_history=True)
596 self.run_cell(code, store_history=True)
586
597
587 def mainloop(self):
598 def mainloop(self):
588 # An extra layer of protection in case someone mashing Ctrl-C breaks
599 # An extra layer of protection in case someone mashing Ctrl-C breaks
589 # out of our internal code.
600 # out of our internal code.
590 while True:
601 while True:
591 try:
602 try:
592 self.interact()
603 self.interact()
593 break
604 break
594 except KeyboardInterrupt as e:
605 except KeyboardInterrupt as e:
595 print("\n%s escaped interact()\n" % type(e).__name__)
606 print("\n%s escaped interact()\n" % type(e).__name__)
596 finally:
607 finally:
597 # An interrupt during the eventloop will mess up the
608 # An interrupt during the eventloop will mess up the
598 # internal state of the prompt_toolkit library.
609 # internal state of the prompt_toolkit library.
599 # Stopping the eventloop fixes this, see
610 # Stopping the eventloop fixes this, see
600 # https://github.com/ipython/ipython/pull/9867
611 # https://github.com/ipython/ipython/pull/9867
601 if hasattr(self, '_eventloop'):
612 if hasattr(self, '_eventloop'):
602 self._eventloop.stop()
613 self._eventloop.stop()
603
614
604 self.restore_term_title()
615 self.restore_term_title()
605
616
606 # try to call some at-exit operation optimistically as some things can't
617 # try to call some at-exit operation optimistically as some things can't
607 # be done during interpreter shutdown. this is technically inaccurate as
618 # be done during interpreter shutdown. this is technically inaccurate as
608 # this make mainlool not re-callable, but that should be a rare if not
619 # this make mainlool not re-callable, but that should be a rare if not
609 # in existent use case.
620 # in existent use case.
610
621
611 self._atexit_once()
622 self._atexit_once()
612
623
613
624
614 _inputhook = None
625 _inputhook = None
615 def inputhook(self, context):
626 def inputhook(self, context):
616 if self._inputhook is not None:
627 if self._inputhook is not None:
617 self._inputhook(context)
628 self._inputhook(context)
618
629
619 active_eventloop = None
630 active_eventloop = None
620 def enable_gui(self, gui=None):
631 def enable_gui(self, gui=None):
621 if gui and (gui != 'inline') :
632 if gui and (gui != 'inline') :
622 self.active_eventloop, self._inputhook =\
633 self.active_eventloop, self._inputhook =\
623 get_inputhook_name_and_func(gui)
634 get_inputhook_name_and_func(gui)
624 else:
635 else:
625 self.active_eventloop = self._inputhook = None
636 self.active_eventloop = self._inputhook = None
626
637
627 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
638 # For prompt_toolkit 3.0. We have to create an asyncio event loop with
628 # this inputhook.
639 # this inputhook.
629 if PTK3:
640 if PTK3:
630 import asyncio
641 import asyncio
631 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
642 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
632
643
633 if gui == 'asyncio':
644 if gui == 'asyncio':
634 # When we integrate the asyncio event loop, run the UI in the
645 # When we integrate the asyncio event loop, run the UI in the
635 # same event loop as the rest of the code. don't use an actual
646 # same event loop as the rest of the code. don't use an actual
636 # input hook. (Asyncio is not made for nesting event loops.)
647 # input hook. (Asyncio is not made for nesting event loops.)
637 self.pt_loop = asyncio.get_event_loop_policy().get_event_loop()
648 self.pt_loop = asyncio.get_event_loop_policy().get_event_loop()
638
649
639 elif self._inputhook:
650 elif self._inputhook:
640 # If an inputhook was set, create a new asyncio event loop with
651 # If an inputhook was set, create a new asyncio event loop with
641 # this inputhook for the prompt.
652 # this inputhook for the prompt.
642 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
653 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
643 else:
654 else:
644 # When there's no inputhook, run the prompt in a separate
655 # When there's no inputhook, run the prompt in a separate
645 # asyncio event loop.
656 # asyncio event loop.
646 self.pt_loop = asyncio.new_event_loop()
657 self.pt_loop = asyncio.new_event_loop()
647
658
648 # Run !system commands directly, not through pipes, so terminal programs
659 # Run !system commands directly, not through pipes, so terminal programs
649 # work correctly.
660 # work correctly.
650 system = InteractiveShell.system_raw
661 system = InteractiveShell.system_raw
651
662
652 def auto_rewrite_input(self, cmd):
663 def auto_rewrite_input(self, cmd):
653 """Overridden from the parent class to use fancy rewriting prompt"""
664 """Overridden from the parent class to use fancy rewriting prompt"""
654 if not self.show_rewritten_input:
665 if not self.show_rewritten_input:
655 return
666 return
656
667
657 tokens = self.prompts.rewrite_prompt_tokens()
668 tokens = self.prompts.rewrite_prompt_tokens()
658 if self.pt_app:
669 if self.pt_app:
659 print_formatted_text(PygmentsTokens(tokens), end='',
670 print_formatted_text(PygmentsTokens(tokens), end='',
660 style=self.pt_app.app.style)
671 style=self.pt_app.app.style)
661 print(cmd)
672 print(cmd)
662 else:
673 else:
663 prompt = ''.join(s for t, s in tokens)
674 prompt = ''.join(s for t, s in tokens)
664 print(prompt, cmd, sep='')
675 print(prompt, cmd, sep='')
665
676
666 _prompts_before = None
677 _prompts_before = None
667 def switch_doctest_mode(self, mode):
678 def switch_doctest_mode(self, mode):
668 """Switch prompts to classic for %doctest_mode"""
679 """Switch prompts to classic for %doctest_mode"""
669 if mode:
680 if mode:
670 self._prompts_before = self.prompts
681 self._prompts_before = self.prompts
671 self.prompts = ClassicPrompts(self)
682 self.prompts = ClassicPrompts(self)
672 elif self._prompts_before:
683 elif self._prompts_before:
673 self.prompts = self._prompts_before
684 self.prompts = self._prompts_before
674 self._prompts_before = None
685 self._prompts_before = None
675 # self._update_layout()
686 # self._update_layout()
676
687
677
688
678 InteractiveShellABC.register(TerminalInteractiveShell)
689 InteractiveShellABC.register(TerminalInteractiveShell)
679
690
680 if __name__ == '__main__':
691 if __name__ == '__main__':
681 TerminalInteractiveShell.instance().interact()
692 TerminalInteractiveShell.instance().interact()
@@ -1,71 +1,72 b''
1 [metadata]
1 [metadata]
2 name = ipython
2 name = ipython
3 version = attr: IPython.core.release.__version__
3 version = attr: IPython.core.release.__version__
4 url = https://ipython.org
4 url = https://ipython.org
5 description = IPython: Productive Interactive Computing
5 description = IPython: Productive Interactive Computing
6 long_description_content_type = text/x-rst
6 long_description_content_type = text/x-rst
7 long_description = file: long_description.rst
7 long_description = file: long_description.rst
8 license_file = LICENSE
8 license_file = LICENSE
9 project_urls =
9 project_urls =
10 Documentation = https://ipython.readthedocs.io/
10 Documentation = https://ipython.readthedocs.io/
11 Funding = https://numfocus.org/
11 Funding = https://numfocus.org/
12 Source = https://github.com/ipython/ipython
12 Source = https://github.com/ipython/ipython
13 Tracker = https://github.com/ipython/ipython/issues
13 Tracker = https://github.com/ipython/ipython/issues
14 keywords = Interactive, Interpreter, Shell, Embedding
14 keywords = Interactive, Interpreter, Shell, Embedding
15 platforms = Linux, Mac OSX, Windows
15 platforms = Linux, Mac OSX, Windows
16 classifiers =
16 classifiers =
17 Framework :: IPython
17 Framework :: IPython
18 Intended Audience :: Developers
18 Intended Audience :: Developers
19 Intended Audience :: Science/Research
19 Intended Audience :: Science/Research
20 License :: OSI Approved :: BSD License
20 License :: OSI Approved :: BSD License
21 Programming Language :: Python
21 Programming Language :: Python
22 Programming Language :: Python :: 3
22 Programming Language :: Python :: 3
23 Programming Language :: Python :: 3 :: Only
23 Programming Language :: Python :: 3 :: Only
24 Topic :: System :: Shells
24 Topic :: System :: Shells
25
25
26
26
27 [options]
27 [options]
28 packages = find:
28 packages = find:
29 python_requires = >=3.8
29 python_requires = >=3.8
30 zip_safe = False
30 zip_safe = False
31 install_requires =
31 install_requires =
32 setuptools>=18.5
32 setuptools>=18.5
33 jedi>=0.16
33 jedi>=0.16
34 black
34 decorator
35 decorator
35 pickleshare
36 pickleshare
36 traitlets>=5
37 traitlets>=5
37 prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1
38 prompt_toolkit>=2.0.0,<3.1.0,!=3.0.0,!=3.0.1
38 pygments
39 pygments
39 backcall
40 backcall
40 stack_data
41 stack_data
41 matplotlib-inline
42 matplotlib-inline
42 pexpect>4.3; sys_platform != "win32"
43 pexpect>4.3; sys_platform != "win32"
43 appnope; sys_platform == "darwin"
44 appnope; sys_platform == "darwin"
44 colorama; sys_platform == "win32"
45 colorama; sys_platform == "win32"
45
46
46 [options.packages.find]
47 [options.packages.find]
47 exclude =
48 exclude =
48 setupext
49 setupext
49
50
50 [options.package_data]
51 [options.package_data]
51 IPython.core = profile/README*
52 IPython.core = profile/README*
52 IPython.core.tests = *.png, *.jpg, daft_extension/*.py
53 IPython.core.tests = *.png, *.jpg, daft_extension/*.py
53 IPython.lib.tests = *.wav
54 IPython.lib.tests = *.wav
54 IPython.testing.plugin = *.txt
55 IPython.testing.plugin = *.txt
55
56
56 [options.entry_points]
57 [options.entry_points]
57 console_scripts =
58 console_scripts =
58 ipython = IPython:start_ipython
59 ipython = IPython:start_ipython
59 ipython3 = IPython:start_ipython
60 ipython3 = IPython:start_ipython
60 pygments.lexers =
61 pygments.lexers =
61 ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer
62 ipythonconsole = IPython.lib.lexers:IPythonConsoleLexer
62 ipython = IPython.lib.lexers:IPythonLexer
63 ipython = IPython.lib.lexers:IPythonLexer
63 ipython3 = IPython.lib.lexers:IPython3Lexer
64 ipython3 = IPython.lib.lexers:IPython3Lexer
64
65
65 [velin]
66 [velin]
66 ignore_patterns =
67 ignore_patterns =
67 IPython/core/tests
68 IPython/core/tests
68 IPython/testing
69 IPython/testing
69
70
70 [tool.black]
71 [tool.black]
71 exclude = 'timing\.py'
72 exclude = 'timing\.py'
General Comments 0
You need to be logged in to leave comments. Login now