##// END OF EJS Templates
Add history file to debugger....
Matthias Bussonnier -
Show More
@@ -1,166 +1,181 b''
1 import asyncio
1 import asyncio
2 import os
2 import sys
3 import sys
3 import threading
4 import threading
4
5
5 from IPython.core.debugger import Pdb
6 from IPython.core.debugger import Pdb
6
7
8
7 from IPython.core.completer import IPCompleter
9 from IPython.core.completer import IPCompleter
8 from .ptutils import IPythonPTCompleter
10 from .ptutils import IPythonPTCompleter
9 from .shortcuts import create_ipython_shortcuts
11 from .shortcuts import create_ipython_shortcuts
10 from . import embed
12 from . import embed
11
13
14 from pathlib import Path
12 from pygments.token import Token
15 from pygments.token import Token
13 from prompt_toolkit.shortcuts.prompt import PromptSession
16 from prompt_toolkit.shortcuts.prompt import PromptSession
14 from prompt_toolkit.enums import EditingMode
17 from prompt_toolkit.enums import EditingMode
15 from prompt_toolkit.formatted_text import PygmentsTokens
18 from prompt_toolkit.formatted_text import PygmentsTokens
19 from prompt_toolkit.history import InMemoryHistory, FileHistory
16
20
17 from prompt_toolkit import __version__ as ptk_version
21 from prompt_toolkit import __version__ as ptk_version
18 PTK3 = ptk_version.startswith('3.')
22 PTK3 = ptk_version.startswith('3.')
19
23
20
24
21 class TerminalPdb(Pdb):
25 class TerminalPdb(Pdb):
22 """Standalone IPython debugger."""
26 """Standalone IPython debugger."""
23
27
24 def __init__(self, *args, pt_session_options=None, **kwargs):
28 def __init__(self, *args, pt_session_options=None, **kwargs):
25 Pdb.__init__(self, *args, **kwargs)
29 Pdb.__init__(self, *args, **kwargs)
26 self._ptcomp = None
30 self._ptcomp = None
27 self.pt_init(pt_session_options)
31 self.pt_init(pt_session_options)
28
32
29 def pt_init(self, pt_session_options=None):
33 def pt_init(self, pt_session_options=None):
30 """Initialize the prompt session and the prompt loop
34 """Initialize the prompt session and the prompt loop
31 and store them in self.pt_app and self.pt_loop.
35 and store them in self.pt_app and self.pt_loop.
32
36
33 Additional keyword arguments for the PromptSession class
37 Additional keyword arguments for the PromptSession class
34 can be specified in pt_session_options.
38 can be specified in pt_session_options.
35 """
39 """
36 if pt_session_options is None:
40 if pt_session_options is None:
37 pt_session_options = {}
41 pt_session_options = {}
38
42
39 def get_prompt_tokens():
43 def get_prompt_tokens():
40 return [(Token.Prompt, self.prompt)]
44 return [(Token.Prompt, self.prompt)]
41
45
42 if self._ptcomp is None:
46 if self._ptcomp is None:
43 compl = IPCompleter(
47 compl = IPCompleter(
44 shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
48 shell=self.shell, namespace={}, global_namespace={}, parent=self.shell
45 )
49 )
46 # add a completer for all the do_ methods
50 # add a completer for all the do_ methods
47 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
51 methods_names = [m[3:] for m in dir(self) if m.startswith("do_")]
48
52
49 def gen_comp(self, text):
53 def gen_comp(self, text):
50 return [m for m in methods_names if m.startswith(text)]
54 return [m for m in methods_names if m.startswith(text)]
51 import types
55 import types
52 newcomp = types.MethodType(gen_comp, compl)
56 newcomp = types.MethodType(gen_comp, compl)
53 compl.custom_matchers.insert(0, newcomp)
57 compl.custom_matchers.insert(0, newcomp)
54 # end add completer.
58 # end add completer.
55
59
56 self._ptcomp = IPythonPTCompleter(compl)
60 self._ptcomp = IPythonPTCompleter(compl)
57
61
62 # setup history only when we start pdb
63 if self.shell.debugger_history is None:
64 if self.shell.debugger_history_file is not None:
65
66 p = Path(self.shell.debugger_history_file).expanduser()
67 if not p.exists():
68 p.touch()
69 self.debugger_history = FileHistory(os.path.expanduser(str(p)))
70 else:
71 self.debugger_history = InMemoryHistory()
72
58 options = dict(
73 options = dict(
59 message=(lambda: PygmentsTokens(get_prompt_tokens())),
74 message=(lambda: PygmentsTokens(get_prompt_tokens())),
60 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
75 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
61 key_bindings=create_ipython_shortcuts(self.shell),
76 key_bindings=create_ipython_shortcuts(self.shell),
62 history=self.shell.debugger_history,
77 history=self.shell.debugger_history,
63 completer=self._ptcomp,
78 completer=self._ptcomp,
64 enable_history_search=True,
79 enable_history_search=True,
65 mouse_support=self.shell.mouse_support,
80 mouse_support=self.shell.mouse_support,
66 complete_style=self.shell.pt_complete_style,
81 complete_style=self.shell.pt_complete_style,
67 style=self.shell.style,
82 style=self.shell.style,
68 color_depth=self.shell.color_depth,
83 color_depth=self.shell.color_depth,
69 )
84 )
70
85
71 if not PTK3:
86 if not PTK3:
72 options['inputhook'] = self.shell.inputhook
87 options['inputhook'] = self.shell.inputhook
73 options.update(pt_session_options)
88 options.update(pt_session_options)
74 self.pt_loop = asyncio.new_event_loop()
89 self.pt_loop = asyncio.new_event_loop()
75 self.pt_app = PromptSession(**options)
90 self.pt_app = PromptSession(**options)
76
91
77 def cmdloop(self, intro=None):
92 def cmdloop(self, intro=None):
78 """Repeatedly issue a prompt, accept input, parse an initial prefix
93 """Repeatedly issue a prompt, accept input, parse an initial prefix
79 off the received input, and dispatch to action methods, passing them
94 off the received input, and dispatch to action methods, passing them
80 the remainder of the line as argument.
95 the remainder of the line as argument.
81
96
82 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
97 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
83 """
98 """
84 if not self.use_rawinput:
99 if not self.use_rawinput:
85 raise ValueError('Sorry ipdb does not support use_rawinput=False')
100 raise ValueError('Sorry ipdb does not support use_rawinput=False')
86
101
87 # In order to make sure that prompt, which uses asyncio doesn't
102 # In order to make sure that prompt, which uses asyncio doesn't
88 # interfere with applications in which it's used, we always run the
103 # interfere with applications in which it's used, we always run the
89 # prompt itself in a different thread (we can't start an event loop
104 # prompt itself in a different thread (we can't start an event loop
90 # within an event loop). This new thread won't have any event loop
105 # within an event loop). This new thread won't have any event loop
91 # running, and here we run our prompt-loop.
106 # running, and here we run our prompt-loop.
92
107
93 self.preloop()
108 self.preloop()
94
109
95 try:
110 try:
96 if intro is not None:
111 if intro is not None:
97 self.intro = intro
112 self.intro = intro
98 if self.intro:
113 if self.intro:
99 self.stdout.write(str(self.intro)+"\n")
114 self.stdout.write(str(self.intro)+"\n")
100 stop = None
115 stop = None
101 while not stop:
116 while not stop:
102 if self.cmdqueue:
117 if self.cmdqueue:
103 line = self.cmdqueue.pop(0)
118 line = self.cmdqueue.pop(0)
104 else:
119 else:
105 self._ptcomp.ipy_completer.namespace = self.curframe_locals
120 self._ptcomp.ipy_completer.namespace = self.curframe_locals
106 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
121 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
107
122
108 # Run the prompt in a different thread.
123 # Run the prompt in a different thread.
109 line = ''
124 line = ''
110 keyboard_interrupt = False
125 keyboard_interrupt = False
111
126
112 def in_thread():
127 def in_thread():
113 nonlocal line, keyboard_interrupt
128 nonlocal line, keyboard_interrupt
114 try:
129 try:
115 line = self.pt_app.prompt()
130 line = self.pt_app.prompt()
116 except EOFError:
131 except EOFError:
117 line = 'EOF'
132 line = 'EOF'
118 except KeyboardInterrupt:
133 except KeyboardInterrupt:
119 keyboard_interrupt = True
134 keyboard_interrupt = True
120
135
121 th = threading.Thread(target=in_thread)
136 th = threading.Thread(target=in_thread)
122 th.start()
137 th.start()
123 th.join()
138 th.join()
124
139
125 if keyboard_interrupt:
140 if keyboard_interrupt:
126 raise KeyboardInterrupt
141 raise KeyboardInterrupt
127
142
128 line = self.precmd(line)
143 line = self.precmd(line)
129 stop = self.onecmd(line)
144 stop = self.onecmd(line)
130 stop = self.postcmd(stop, line)
145 stop = self.postcmd(stop, line)
131 self.postloop()
146 self.postloop()
132 except Exception:
147 except Exception:
133 raise
148 raise
134
149
135 def do_interact(self, arg):
150 def do_interact(self, arg):
136 ipshell = embed.InteractiveShellEmbed(
151 ipshell = embed.InteractiveShellEmbed(
137 config=self.shell.config,
152 config=self.shell.config,
138 banner1="*interactive*",
153 banner1="*interactive*",
139 exit_msg="*exiting interactive console...*",
154 exit_msg="*exiting interactive console...*",
140 )
155 )
141 global_ns = self.curframe.f_globals
156 global_ns = self.curframe.f_globals
142 ipshell(
157 ipshell(
143 module=sys.modules.get(global_ns["__name__"], None),
158 module=sys.modules.get(global_ns["__name__"], None),
144 local_ns=self.curframe_locals,
159 local_ns=self.curframe_locals,
145 )
160 )
146
161
147
162
148 def set_trace(frame=None):
163 def set_trace(frame=None):
149 """
164 """
150 Start debugging from `frame`.
165 Start debugging from `frame`.
151
166
152 If frame is not specified, debugging starts from caller's frame.
167 If frame is not specified, debugging starts from caller's frame.
153 """
168 """
154 TerminalPdb().set_trace(frame or sys._getframe().f_back)
169 TerminalPdb().set_trace(frame or sys._getframe().f_back)
155
170
156
171
157 if __name__ == '__main__':
172 if __name__ == '__main__':
158 import pdb
173 import pdb
159 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
174 # IPython.core.debugger.Pdb.trace_dispatch shall not catch
160 # bdb.BdbQuit. When started through __main__ and an exception
175 # bdb.BdbQuit. When started through __main__ and an exception
161 # happened after hitting "c", this is needed in order to
176 # happened after hitting "c", this is needed in order to
162 # be able to quit the debugging session (see #9950).
177 # be able to quit the debugging session (see #9950).
163 old_trace_dispatch = pdb.Pdb.trace_dispatch
178 old_trace_dispatch = pdb.Pdb.trace_dispatch
164 pdb.Pdb = TerminalPdb # type: ignore
179 pdb.Pdb = TerminalPdb # type: ignore
165 pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore
180 pdb.Pdb.trace_dispatch = old_trace_dispatch # type: ignore
166 pdb.main()
181 pdb.main()
@@ -1,692 +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(
128 "~/.pdbhistory", help="File in which to store and read history"
129 ).tag(config=True)
130
127 simple_prompt = Bool(_use_simple_prompt,
131 simple_prompt = Bool(_use_simple_prompt,
128 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.
129
133
130 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:
131 IPython own testing machinery, and emacs inferior-shell integration through elpy.
135 IPython own testing machinery, and emacs inferior-shell integration through elpy.
132
136
133 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
137 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
134 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."""
135 ).tag(config=True)
139 ).tag(config=True)
136
140
137 @property
141 @property
138 def debugger_cls(self):
142 def debugger_cls(self):
139 return Pdb if self.simple_prompt else TerminalPdb
143 return Pdb if self.simple_prompt else TerminalPdb
140
144
141 confirm_exit = Bool(True,
145 confirm_exit = Bool(True,
142 help="""
146 help="""
143 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
144 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',
145 you can force a direct exit without any confirmation.""",
149 you can force a direct exit without any confirmation.""",
146 ).tag(config=True)
150 ).tag(config=True)
147
151
148 editing_mode = Unicode('emacs',
152 editing_mode = Unicode('emacs',
149 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
153 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
150 ).tag(config=True)
154 ).tag(config=True)
151
155
152 emacs_bindings_in_vi_insert_mode = Bool(
156 emacs_bindings_in_vi_insert_mode = Bool(
153 True,
157 True,
154 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
158 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
155 ).tag(config=True)
159 ).tag(config=True)
156
160
157 modal_cursor = Bool(
161 modal_cursor = Bool(
158 True,
162 True,
159 help="""
163 help="""
160 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,
161 block in nav mode, underscore in replace mode.""",
165 block in nav mode, underscore in replace mode.""",
162 ).tag(config=True)
166 ).tag(config=True)
163
167
164 ttimeoutlen = Float(
168 ttimeoutlen = Float(
165 0.01,
169 0.01,
166 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
167 to complete.""",
171 to complete.""",
168 ).tag(config=True)
172 ).tag(config=True)
169
173
170 timeoutlen = Float(
174 timeoutlen = Float(
171 0.5,
175 0.5,
172 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
173 sequence to complete.""",
177 sequence to complete.""",
174 ).tag(config=True)
178 ).tag(config=True)
175
179
176 autoformatter = Unicode(None,
180 autoformatter = Unicode(None,
177 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
181 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
178 allow_none=True
182 allow_none=True
179 ).tag(config=True)
183 ).tag(config=True)
180
184
181 mouse_support = Bool(False,
185 mouse_support = Bool(False,
182 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)"
183 ).tag(config=True)
187 ).tag(config=True)
184
188
185 # 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
186 # Pygments plugins takes time and can cause unexpected errors.
190 # Pygments plugins takes time and can cause unexpected errors.
187 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
191 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
188 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
189 highlighting. To see available styles, run `pygmentize -L styles`."""
193 highlighting. To see available styles, run `pygmentize -L styles`."""
190 ).tag(config=True)
194 ).tag(config=True)
191
195
192 @validate('editing_mode')
196 @validate('editing_mode')
193 def _validate_editing_mode(self, proposal):
197 def _validate_editing_mode(self, proposal):
194 if proposal['value'].lower() == 'vim':
198 if proposal['value'].lower() == 'vim':
195 proposal['value']= 'vi'
199 proposal['value']= 'vi'
196 elif proposal['value'].lower() == 'default':
200 elif proposal['value'].lower() == 'default':
197 proposal['value']= 'emacs'
201 proposal['value']= 'emacs'
198
202
199 if hasattr(EditingMode, proposal['value'].upper()):
203 if hasattr(EditingMode, proposal['value'].upper()):
200 return proposal['value'].lower()
204 return proposal['value'].lower()
201
205
202 return self.editing_mode
206 return self.editing_mode
203
207
204
208
205 @observe('editing_mode')
209 @observe('editing_mode')
206 def _editing_mode(self, change):
210 def _editing_mode(self, change):
207 if self.pt_app:
211 if self.pt_app:
208 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
212 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
209
213
210 @observe('autoformatter')
214 @observe('autoformatter')
211 def _autoformatter_changed(self, change):
215 def _autoformatter_changed(self, change):
212 formatter = change.new
216 formatter = change.new
213 if formatter is None:
217 if formatter is None:
214 self.reformat_handler = lambda x:x
218 self.reformat_handler = lambda x:x
215 elif formatter == 'black':
219 elif formatter == 'black':
216 self.reformat_handler = black_reformat_handler
220 self.reformat_handler = black_reformat_handler
217 else:
221 else:
218 raise ValueError
222 raise ValueError
219
223
220 @observe('highlighting_style')
224 @observe('highlighting_style')
221 @observe('colors')
225 @observe('colors')
222 def _highlighting_style_changed(self, change):
226 def _highlighting_style_changed(self, change):
223 self.refresh_style()
227 self.refresh_style()
224
228
225 def refresh_style(self):
229 def refresh_style(self):
226 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)
227
231
228
232
229 highlighting_style_overrides = Dict(
233 highlighting_style_overrides = Dict(
230 help="Override highlighting format for specific tokens"
234 help="Override highlighting format for specific tokens"
231 ).tag(config=True)
235 ).tag(config=True)
232
236
233 true_color = Bool(False,
237 true_color = Bool(False,
234 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
238 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
235 If your terminal supports true color, the following command should
239 If your terminal supports true color, the following command should
236 print ``TRUECOLOR`` in orange::
240 print ``TRUECOLOR`` in orange::
237
241
238 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
242 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
239 """,
243 """,
240 ).tag(config=True)
244 ).tag(config=True)
241
245
242 editor = Unicode(get_default_editor(),
246 editor = Unicode(get_default_editor(),
243 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)."
244 ).tag(config=True)
248 ).tag(config=True)
245
249
246 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)
247
251
248 prompts = Instance(Prompts)
252 prompts = Instance(Prompts)
249
253
250 @default('prompts')
254 @default('prompts')
251 def _prompts_default(self):
255 def _prompts_default(self):
252 return self.prompts_class(self)
256 return self.prompts_class(self)
253
257
254 # @observe('prompts')
258 # @observe('prompts')
255 # def _(self, change):
259 # def _(self, change):
256 # self._update_layout()
260 # self._update_layout()
257
261
258 @default('displayhook_class')
262 @default('displayhook_class')
259 def _displayhook_class_default(self):
263 def _displayhook_class_default(self):
260 return RichPromptDisplayHook
264 return RichPromptDisplayHook
261
265
262 term_title = Bool(True,
266 term_title = Bool(True,
263 help="Automatically set the terminal title"
267 help="Automatically set the terminal title"
264 ).tag(config=True)
268 ).tag(config=True)
265
269
266 term_title_format = Unicode("IPython: {cwd}",
270 term_title_format = Unicode("IPython: {cwd}",
267 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. " +
268 "Available substitutions are: {cwd}."
272 "Available substitutions are: {cwd}."
269 ).tag(config=True)
273 ).tag(config=True)
270
274
271 display_completions = Enum(('column', 'multicolumn','readlinelike'),
275 display_completions = Enum(('column', 'multicolumn','readlinelike'),
272 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
276 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
273 "'readlinelike'. These options are for `prompt_toolkit`, see "
277 "'readlinelike'. These options are for `prompt_toolkit`, see "
274 "`prompt_toolkit` documentation for more information."
278 "`prompt_toolkit` documentation for more information."
275 ),
279 ),
276 default_value='multicolumn').tag(config=True)
280 default_value='multicolumn').tag(config=True)
277
281
278 highlight_matching_brackets = Bool(True,
282 highlight_matching_brackets = Bool(True,
279 help="Highlight matching brackets.",
283 help="Highlight matching brackets.",
280 ).tag(config=True)
284 ).tag(config=True)
281
285
282 extra_open_editor_shortcuts = Bool(False,
286 extra_open_editor_shortcuts = Bool(False,
283 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. "
284 "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."
285 ).tag(config=True)
289 ).tag(config=True)
286
290
287 handle_return = Any(None,
291 handle_return = Any(None,
288 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 "
289 "Return. This is an advanced option intended for debugging, which "
293 "Return. This is an advanced option intended for debugging, which "
290 "may be changed or removed in later releases."
294 "may be changed or removed in later releases."
291 ).tag(config=True)
295 ).tag(config=True)
292
296
293 enable_history_search = Bool(True,
297 enable_history_search = Bool(True,
294 help="Allows to enable/disable the prompt toolkit history search"
298 help="Allows to enable/disable the prompt toolkit history search"
295 ).tag(config=True)
299 ).tag(config=True)
296
300
297 prompt_includes_vi_mode = Bool(True,
301 prompt_includes_vi_mode = Bool(True,
298 help="Display the current vi mode (when using vi editing mode)."
302 help="Display the current vi mode (when using vi editing mode)."
299 ).tag(config=True)
303 ).tag(config=True)
300
304
301 @observe('term_title')
305 @observe('term_title')
302 def init_term_title(self, change=None):
306 def init_term_title(self, change=None):
303 # Enable or disable the terminal title.
307 # Enable or disable the terminal title.
304 if self.term_title:
308 if self.term_title:
305 toggle_set_term_title(True)
309 toggle_set_term_title(True)
306 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
310 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
307 else:
311 else:
308 toggle_set_term_title(False)
312 toggle_set_term_title(False)
309
313
310 def restore_term_title(self):
314 def restore_term_title(self):
311 if self.term_title:
315 if self.term_title:
312 restore_term_title()
316 restore_term_title()
313
317
314 def init_display_formatter(self):
318 def init_display_formatter(self):
315 super(TerminalInteractiveShell, self).init_display_formatter()
319 super(TerminalInteractiveShell, self).init_display_formatter()
316 # terminal only supports plain text
320 # terminal only supports plain text
317 self.display_formatter.active_types = ['text/plain']
321 self.display_formatter.active_types = ['text/plain']
318 # disable `_ipython_display_`
322 # disable `_ipython_display_`
319 self.display_formatter.ipython_display_formatter.enabled = False
323 self.display_formatter.ipython_display_formatter.enabled = False
320
324
321 def init_prompt_toolkit_cli(self):
325 def init_prompt_toolkit_cli(self):
322 if self.simple_prompt:
326 if self.simple_prompt:
323 # Fall back to plain non-interactive output for tests.
327 # Fall back to plain non-interactive output for tests.
324 # This is very limited.
328 # This is very limited.
325 def prompt():
329 def prompt():
326 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())
327 lines = [input(prompt_text)]
331 lines = [input(prompt_text)]
328 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())
329 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
333 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
330 lines.append( input(prompt_continuation) )
334 lines.append( input(prompt_continuation) )
331 return '\n'.join(lines)
335 return '\n'.join(lines)
332 self.prompt_for_code = prompt
336 self.prompt_for_code = prompt
333 return
337 return
334
338
335 # Set up keyboard shortcuts
339 # Set up keyboard shortcuts
336 key_bindings = create_ipython_shortcuts(self)
340 key_bindings = create_ipython_shortcuts(self)
337
341
338 # Pre-populate history from IPython's history database
342 # Pre-populate history from IPython's history database
339 history = InMemoryHistory()
343 history = InMemoryHistory()
340 last_cell = u""
344 last_cell = u""
341 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,
342 include_latest=True):
346 include_latest=True):
343 # Ignore blank lines and consecutive duplicates
347 # Ignore blank lines and consecutive duplicates
344 cell = cell.rstrip()
348 cell = cell.rstrip()
345 if cell and (cell != last_cell):
349 if cell and (cell != last_cell):
346 history.append_string(cell)
350 history.append_string(cell)
347 last_cell = cell
351 last_cell = cell
348
352
349 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)
350 self.style = DynamicStyle(lambda: self._style)
354 self.style = DynamicStyle(lambda: self._style)
351
355
352 editing_mode = getattr(EditingMode, self.editing_mode.upper())
356 editing_mode = getattr(EditingMode, self.editing_mode.upper())
353
357
354 self.pt_loop = asyncio.new_event_loop()
358 self.pt_loop = asyncio.new_event_loop()
355 self.pt_app = PromptSession(
359 self.pt_app = PromptSession(
356 auto_suggest=AutoSuggestFromHistory(),
360 auto_suggest=AutoSuggestFromHistory(),
357 editing_mode=editing_mode,
361 editing_mode=editing_mode,
358 key_bindings=key_bindings,
362 key_bindings=key_bindings,
359 history=history,
363 history=history,
360 completer=IPythonPTCompleter(shell=self),
364 completer=IPythonPTCompleter(shell=self),
361 enable_history_search=self.enable_history_search,
365 enable_history_search=self.enable_history_search,
362 style=self.style,
366 style=self.style,
363 include_default_pygments_style=False,
367 include_default_pygments_style=False,
364 mouse_support=self.mouse_support,
368 mouse_support=self.mouse_support,
365 enable_open_in_editor=self.extra_open_editor_shortcuts,
369 enable_open_in_editor=self.extra_open_editor_shortcuts,
366 color_depth=self.color_depth,
370 color_depth=self.color_depth,
367 tempfile_suffix=".py",
371 tempfile_suffix=".py",
368 **self._extra_prompt_options()
372 **self._extra_prompt_options()
369 )
373 )
370
374
371 def _make_style_from_name_or_cls(self, name_or_cls):
375 def _make_style_from_name_or_cls(self, name_or_cls):
372 """
376 """
373 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
374
378
375 We need that to add style for prompt ... etc.
379 We need that to add style for prompt ... etc.
376 """
380 """
377 style_overrides = {}
381 style_overrides = {}
378 if name_or_cls == 'legacy':
382 if name_or_cls == 'legacy':
379 legacy = self.colors.lower()
383 legacy = self.colors.lower()
380 if legacy == 'linux':
384 if legacy == 'linux':
381 style_cls = get_style_by_name('monokai')
385 style_cls = get_style_by_name('monokai')
382 style_overrides = _style_overrides_linux
386 style_overrides = _style_overrides_linux
383 elif legacy == 'lightbg':
387 elif legacy == 'lightbg':
384 style_overrides = _style_overrides_light_bg
388 style_overrides = _style_overrides_light_bg
385 style_cls = get_style_by_name('pastie')
389 style_cls = get_style_by_name('pastie')
386 elif legacy == 'neutral':
390 elif legacy == 'neutral':
387 # 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
388 # 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
389 # looks like. These tweaks to the default theme help with that.
393 # looks like. These tweaks to the default theme help with that.
390 style_cls = get_style_by_name('default')
394 style_cls = get_style_by_name('default')
391 style_overrides.update({
395 style_overrides.update({
392 Token.Number: '#ansigreen',
396 Token.Number: '#ansigreen',
393 Token.Operator: 'noinherit',
397 Token.Operator: 'noinherit',
394 Token.String: '#ansiyellow',
398 Token.String: '#ansiyellow',
395 Token.Name.Function: '#ansiblue',
399 Token.Name.Function: '#ansiblue',
396 Token.Name.Class: 'bold #ansiblue',
400 Token.Name.Class: 'bold #ansiblue',
397 Token.Name.Namespace: 'bold #ansiblue',
401 Token.Name.Namespace: 'bold #ansiblue',
398 Token.Name.Variable.Magic: '#ansiblue',
402 Token.Name.Variable.Magic: '#ansiblue',
399 Token.Prompt: '#ansigreen',
403 Token.Prompt: '#ansigreen',
400 Token.PromptNum: '#ansibrightgreen bold',
404 Token.PromptNum: '#ansibrightgreen bold',
401 Token.OutPrompt: '#ansired',
405 Token.OutPrompt: '#ansired',
402 Token.OutPromptNum: '#ansibrightred bold',
406 Token.OutPromptNum: '#ansibrightred bold',
403 })
407 })
404
408
405 # Hack: Due to limited color support on the Windows console
409 # Hack: Due to limited color support on the Windows console
406 # the prompt colors will be wrong without this
410 # the prompt colors will be wrong without this
407 if os.name == 'nt':
411 if os.name == 'nt':
408 style_overrides.update({
412 style_overrides.update({
409 Token.Prompt: '#ansidarkgreen',
413 Token.Prompt: '#ansidarkgreen',
410 Token.PromptNum: '#ansigreen bold',
414 Token.PromptNum: '#ansigreen bold',
411 Token.OutPrompt: '#ansidarkred',
415 Token.OutPrompt: '#ansidarkred',
412 Token.OutPromptNum: '#ansired bold',
416 Token.OutPromptNum: '#ansired bold',
413 })
417 })
414 elif legacy =='nocolor':
418 elif legacy =='nocolor':
415 style_cls=_NoStyle
419 style_cls=_NoStyle
416 style_overrides = {}
420 style_overrides = {}
417 else :
421 else :
418 raise ValueError('Got unknown colors: ', legacy)
422 raise ValueError('Got unknown colors: ', legacy)
419 else :
423 else :
420 if isinstance(name_or_cls, str):
424 if isinstance(name_or_cls, str):
421 style_cls = get_style_by_name(name_or_cls)
425 style_cls = get_style_by_name(name_or_cls)
422 else:
426 else:
423 style_cls = name_or_cls
427 style_cls = name_or_cls
424 style_overrides = {
428 style_overrides = {
425 Token.Prompt: '#ansigreen',
429 Token.Prompt: '#ansigreen',
426 Token.PromptNum: '#ansibrightgreen bold',
430 Token.PromptNum: '#ansibrightgreen bold',
427 Token.OutPrompt: '#ansired',
431 Token.OutPrompt: '#ansired',
428 Token.OutPromptNum: '#ansibrightred bold',
432 Token.OutPromptNum: '#ansibrightred bold',
429 }
433 }
430 style_overrides.update(self.highlighting_style_overrides)
434 style_overrides.update(self.highlighting_style_overrides)
431 style = merge_styles([
435 style = merge_styles([
432 style_from_pygments_cls(style_cls),
436 style_from_pygments_cls(style_cls),
433 style_from_pygments_dict(style_overrides),
437 style_from_pygments_dict(style_overrides),
434 ])
438 ])
435
439
436 return style
440 return style
437
441
438 @property
442 @property
439 def pt_complete_style(self):
443 def pt_complete_style(self):
440 return {
444 return {
441 'multicolumn': CompleteStyle.MULTI_COLUMN,
445 'multicolumn': CompleteStyle.MULTI_COLUMN,
442 'column': CompleteStyle.COLUMN,
446 'column': CompleteStyle.COLUMN,
443 'readlinelike': CompleteStyle.READLINE_LIKE,
447 'readlinelike': CompleteStyle.READLINE_LIKE,
444 }[self.display_completions]
448 }[self.display_completions]
445
449
446 @property
450 @property
447 def color_depth(self):
451 def color_depth(self):
448 return (ColorDepth.TRUE_COLOR if self.true_color else None)
452 return (ColorDepth.TRUE_COLOR if self.true_color else None)
449
453
450 def _extra_prompt_options(self):
454 def _extra_prompt_options(self):
451 """
455 """
452 Return the current layout option for the current Terminal InteractiveShell
456 Return the current layout option for the current Terminal InteractiveShell
453 """
457 """
454 def get_message():
458 def get_message():
455 return PygmentsTokens(self.prompts.in_prompt_tokens())
459 return PygmentsTokens(self.prompts.in_prompt_tokens())
456
460
457 if self.editing_mode == 'emacs':
461 if self.editing_mode == 'emacs':
458 # 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
459 # 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
460 # [nor] so we can't precompute.
464 # [nor] so we can't precompute.
461 # here I'm going to favor the default keybinding which almost
465 # here I'm going to favor the default keybinding which almost
462 # everybody uses to decrease CPU usage.
466 # everybody uses to decrease CPU usage.
463 # 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
464 # work around this.
468 # work around this.
465 get_message = get_message()
469 get_message = get_message()
466
470
467 options = {
471 options = {
468 'complete_in_thread': False,
472 'complete_in_thread': False,
469 'lexer':IPythonPTLexer(),
473 'lexer':IPythonPTLexer(),
470 'reserve_space_for_menu':self.space_for_menu,
474 'reserve_space_for_menu':self.space_for_menu,
471 'message': get_message,
475 'message': get_message,
472 'prompt_continuation': (
476 'prompt_continuation': (
473 lambda width, lineno, is_soft_wrap:
477 lambda width, lineno, is_soft_wrap:
474 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
478 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
475 'multiline': True,
479 'multiline': True,
476 'complete_style': self.pt_complete_style,
480 'complete_style': self.pt_complete_style,
477
481
478 # Highlight matching brackets, but only when this setting is
482 # Highlight matching brackets, but only when this setting is
479 # enabled, and only when the DEFAULT_BUFFER has the focus.
483 # enabled, and only when the DEFAULT_BUFFER has the focus.
480 'input_processors': [ConditionalProcessor(
484 'input_processors': [ConditionalProcessor(
481 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
485 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
482 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
486 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
483 Condition(lambda: self.highlight_matching_brackets))],
487 Condition(lambda: self.highlight_matching_brackets))],
484 }
488 }
485 if not PTK3:
489 if not PTK3:
486 options['inputhook'] = self.inputhook
490 options['inputhook'] = self.inputhook
487
491
488 return options
492 return options
489
493
490 def prompt_for_code(self):
494 def prompt_for_code(self):
491 if self.rl_next_input:
495 if self.rl_next_input:
492 default = self.rl_next_input
496 default = self.rl_next_input
493 self.rl_next_input = None
497 self.rl_next_input = None
494 else:
498 else:
495 default = ''
499 default = ''
496
500
497 # In order to make sure that asyncio code written in the
501 # In order to make sure that asyncio code written in the
498 # interactive shell doesn't interfere with the prompt, we run the
502 # interactive shell doesn't interfere with the prompt, we run the
499 # prompt in a different event loop.
503 # prompt in a different event loop.
500 # 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
501 # while/true inside which will freeze the prompt.
505 # while/true inside which will freeze the prompt.
502
506
503 try:
507 try:
504 old_loop = asyncio.get_event_loop()
508 old_loop = asyncio.get_event_loop()
505 except RuntimeError:
509 except RuntimeError:
506 # This happens when the user used `asyncio.run()`.
510 # This happens when the user used `asyncio.run()`.
507 old_loop = None
511 old_loop = None
508
512
509 asyncio.set_event_loop(self.pt_loop)
513 asyncio.set_event_loop(self.pt_loop)
510 try:
514 try:
511 with patch_stdout(raw=True):
515 with patch_stdout(raw=True):
512 text = self.pt_app.prompt(
516 text = self.pt_app.prompt(
513 default=default,
517 default=default,
514 **self._extra_prompt_options())
518 **self._extra_prompt_options())
515 finally:
519 finally:
516 # Restore the original event loop.
520 # Restore the original event loop.
517 asyncio.set_event_loop(old_loop)
521 asyncio.set_event_loop(old_loop)
518
522
519 return text
523 return text
520
524
521 def enable_win_unicode_console(self):
525 def enable_win_unicode_console(self):
522 # 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
523 # console by default, so WUC shouldn't be needed.
527 # console by default, so WUC shouldn't be needed.
524 from warnings import warn
528 from warnings import warn
525 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",
526 DeprecationWarning,
530 DeprecationWarning,
527 stacklevel=2)
531 stacklevel=2)
528
532
529 def init_io(self):
533 def init_io(self):
530 if sys.platform not in {'win32', 'cli'}:
534 if sys.platform not in {'win32', 'cli'}:
531 return
535 return
532
536
533 import colorama
537 import colorama
534 colorama.init()
538 colorama.init()
535
539
536 # For some reason we make these wrappers around stdout/stderr.
540 # For some reason we make these wrappers around stdout/stderr.
537 # 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.
538 # https://github.com/ipython/ipython/issues/8669
542 # https://github.com/ipython/ipython/issues/8669
539 # 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
540 # during initialization of the deprecated API.
544 # during initialization of the deprecated API.
541 with warnings.catch_warnings():
545 with warnings.catch_warnings():
542 warnings.simplefilter('ignore', DeprecationWarning)
546 warnings.simplefilter('ignore', DeprecationWarning)
543 io.stdout = io.IOStream(sys.stdout)
547 io.stdout = io.IOStream(sys.stdout)
544 io.stderr = io.IOStream(sys.stderr)
548 io.stderr = io.IOStream(sys.stderr)
545
549
546 def init_magics(self):
550 def init_magics(self):
547 super(TerminalInteractiveShell, self).init_magics()
551 super(TerminalInteractiveShell, self).init_magics()
548 self.register_magics(TerminalMagics)
552 self.register_magics(TerminalMagics)
549
553
550 def init_alias(self):
554 def init_alias(self):
551 # 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
552 # frontend.
556 # frontend.
553 super(TerminalInteractiveShell, self).init_alias()
557 super(TerminalInteractiveShell, self).init_alias()
554
558
555 # 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
556 # 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
557 # GUI or web frontend
561 # GUI or web frontend
558 if os.name == 'posix':
562 if os.name == 'posix':
559 for cmd in ('clear', 'more', 'less', 'man'):
563 for cmd in ('clear', 'more', 'less', 'man'):
560 self.alias_manager.soft_define_alias(cmd, cmd)
564 self.alias_manager.soft_define_alias(cmd, cmd)
561
565
562
566
563 def __init__(self, *args, **kwargs):
567 def __init__(self, *args, **kwargs):
564 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
568 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
565 self.init_prompt_toolkit_cli()
569 self.init_prompt_toolkit_cli()
566 self.init_term_title()
570 self.init_term_title()
567 self.keep_running = True
571 self.keep_running = True
568
572
569 self.debugger_history = InMemoryHistory()
570
573
571 def ask_exit(self):
574 def ask_exit(self):
572 self.keep_running = False
575 self.keep_running = False
573
576
574 rl_next_input = None
577 rl_next_input = None
575
578
576 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
579 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
577
580
578 if display_banner is not DISPLAY_BANNER_DEPRECATED:
581 if display_banner is not DISPLAY_BANNER_DEPRECATED:
579 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)
580
583
581 self.keep_running = True
584 self.keep_running = True
582 while self.keep_running:
585 while self.keep_running:
583 print(self.separate_in, end='')
586 print(self.separate_in, end='')
584
587
585 try:
588 try:
586 code = self.prompt_for_code()
589 code = self.prompt_for_code()
587 except EOFError:
590 except EOFError:
588 if (not self.confirm_exit) \
591 if (not self.confirm_exit) \
589 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'):
590 self.ask_exit()
593 self.ask_exit()
591
594
592 else:
595 else:
593 if code:
596 if code:
594 self.run_cell(code, store_history=True)
597 self.run_cell(code, store_history=True)
595
598
596 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
599 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
597 # 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
598 # out of our internal code.
601 # out of our internal code.
599 if display_banner is not DISPLAY_BANNER_DEPRECATED:
602 if display_banner is not DISPLAY_BANNER_DEPRECATED:
600 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)
601 while True:
604 while True:
602 try:
605 try:
603 self.interact()
606 self.interact()
604 break
607 break
605 except KeyboardInterrupt as e:
608 except KeyboardInterrupt as e:
606 print("\n%s escaped interact()\n" % type(e).__name__)
609 print("\n%s escaped interact()\n" % type(e).__name__)
607 finally:
610 finally:
608 # An interrupt during the eventloop will mess up the
611 # An interrupt during the eventloop will mess up the
609 # internal state of the prompt_toolkit library.
612 # internal state of the prompt_toolkit library.
610 # Stopping the eventloop fixes this, see
613 # Stopping the eventloop fixes this, see
611 # https://github.com/ipython/ipython/pull/9867
614 # https://github.com/ipython/ipython/pull/9867
612 if hasattr(self, '_eventloop'):
615 if hasattr(self, '_eventloop'):
613 self._eventloop.stop()
616 self._eventloop.stop()
614
617
615 self.restore_term_title()
618 self.restore_term_title()
616
619
617 # 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
618 # be done during interpreter shutdown. this is technically inaccurate as
621 # be done during interpreter shutdown. this is technically inaccurate as
619 # 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
620 # in existent use case.
623 # in existent use case.
621
624
622 self._atexit_once()
625 self._atexit_once()
623
626
624
627
625 _inputhook = None
628 _inputhook = None
626 def inputhook(self, context):
629 def inputhook(self, context):
627 if self._inputhook is not None:
630 if self._inputhook is not None:
628 self._inputhook(context)
631 self._inputhook(context)
629
632
630 active_eventloop = None
633 active_eventloop = None
631 def enable_gui(self, gui=None):
634 def enable_gui(self, gui=None):
632 if gui and (gui != 'inline') :
635 if gui and (gui != 'inline') :
633 self.active_eventloop, self._inputhook =\
636 self.active_eventloop, self._inputhook =\
634 get_inputhook_name_and_func(gui)
637 get_inputhook_name_and_func(gui)
635 else:
638 else:
636 self.active_eventloop = self._inputhook = None
639 self.active_eventloop = self._inputhook = None
637
640
638 # 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
639 # this inputhook.
642 # this inputhook.
640 if PTK3:
643 if PTK3:
641 import asyncio
644 import asyncio
642 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
645 from prompt_toolkit.eventloop import new_eventloop_with_inputhook
643
646
644 if gui == 'asyncio':
647 if gui == 'asyncio':
645 # 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
646 # 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
647 # input hook. (Asyncio is not made for nesting event loops.)
650 # input hook. (Asyncio is not made for nesting event loops.)
648 self.pt_loop = asyncio.get_event_loop()
651 self.pt_loop = asyncio.get_event_loop()
649
652
650 elif self._inputhook:
653 elif self._inputhook:
651 # 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
652 # this inputhook for the prompt.
655 # this inputhook for the prompt.
653 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
656 self.pt_loop = new_eventloop_with_inputhook(self._inputhook)
654 else:
657 else:
655 # When there's no inputhook, run the prompt in a separate
658 # When there's no inputhook, run the prompt in a separate
656 # asyncio event loop.
659 # asyncio event loop.
657 self.pt_loop = asyncio.new_event_loop()
660 self.pt_loop = asyncio.new_event_loop()
658
661
659 # Run !system commands directly, not through pipes, so terminal programs
662 # Run !system commands directly, not through pipes, so terminal programs
660 # work correctly.
663 # work correctly.
661 system = InteractiveShell.system_raw
664 system = InteractiveShell.system_raw
662
665
663 def auto_rewrite_input(self, cmd):
666 def auto_rewrite_input(self, cmd):
664 """Overridden from the parent class to use fancy rewriting prompt"""
667 """Overridden from the parent class to use fancy rewriting prompt"""
665 if not self.show_rewritten_input:
668 if not self.show_rewritten_input:
666 return
669 return
667
670
668 tokens = self.prompts.rewrite_prompt_tokens()
671 tokens = self.prompts.rewrite_prompt_tokens()
669 if self.pt_app:
672 if self.pt_app:
670 print_formatted_text(PygmentsTokens(tokens), end='',
673 print_formatted_text(PygmentsTokens(tokens), end='',
671 style=self.pt_app.app.style)
674 style=self.pt_app.app.style)
672 print(cmd)
675 print(cmd)
673 else:
676 else:
674 prompt = ''.join(s for t, s in tokens)
677 prompt = ''.join(s for t, s in tokens)
675 print(prompt, cmd, sep='')
678 print(prompt, cmd, sep='')
676
679
677 _prompts_before = None
680 _prompts_before = None
678 def switch_doctest_mode(self, mode):
681 def switch_doctest_mode(self, mode):
679 """Switch prompts to classic for %doctest_mode"""
682 """Switch prompts to classic for %doctest_mode"""
680 if mode:
683 if mode:
681 self._prompts_before = self.prompts
684 self._prompts_before = self.prompts
682 self.prompts = ClassicPrompts(self)
685 self.prompts = ClassicPrompts(self)
683 elif self._prompts_before:
686 elif self._prompts_before:
684 self.prompts = self._prompts_before
687 self.prompts = self._prompts_before
685 self._prompts_before = None
688 self._prompts_before = None
686 # self._update_layout()
689 # self._update_layout()
687
690
688
691
689 InteractiveShellABC.register(TerminalInteractiveShell)
692 InteractiveShellABC.register(TerminalInteractiveShell)
690
693
691 if __name__ == '__main__':
694 if __name__ == '__main__':
692 TerminalInteractiveShell.instance().interact()
695 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now