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