##// END OF EJS Templates
Backport PR #9943: Only track unique history of commands...
Matthias Bussonnier -
Show More
@@ -1,493 +1,494 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
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 PY3, cast_unicode_py2, input, string_types
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input, string_types
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union
15
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
16 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
17 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
18 from prompt_toolkit.history import InMemoryHistory
18 from prompt_toolkit.history import InMemoryHistory
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
20 from prompt_toolkit.interface import CommandLineInterface
20 from prompt_toolkit.interface import CommandLineInterface
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
23 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
24
24
25 from pygments.styles import get_style_by_name, get_all_styles
25 from pygments.styles import get_style_by_name, get_all_styles
26 from pygments.style import Style
26 from pygments.style import Style
27 from pygments.token import Token
27 from pygments.token import Token
28
28
29 from .debugger import TerminalPdb, Pdb
29 from .debugger import TerminalPdb, Pdb
30 from .magics import TerminalMagics
30 from .magics import TerminalMagics
31 from .pt_inputhooks import get_inputhook_func
31 from .pt_inputhooks import get_inputhook_func
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
34 from .shortcuts import register_ipython_shortcuts
34 from .shortcuts import register_ipython_shortcuts
35
35
36 DISPLAY_BANNER_DEPRECATED = object()
36 DISPLAY_BANNER_DEPRECATED = object()
37
37
38
38
39 from pygments.style import Style
39 from pygments.style import Style
40
40
41 class _NoStyle(Style): pass
41 class _NoStyle(Style): pass
42
42
43
43
44
44
45 _style_overrides_light_bg = {
45 _style_overrides_light_bg = {
46 Token.Prompt: '#0000ff',
46 Token.Prompt: '#0000ff',
47 Token.PromptNum: '#0000ee bold',
47 Token.PromptNum: '#0000ee bold',
48 Token.OutPrompt: '#cc0000',
48 Token.OutPrompt: '#cc0000',
49 Token.OutPromptNum: '#bb0000 bold',
49 Token.OutPromptNum: '#bb0000 bold',
50 }
50 }
51
51
52 _style_overrides_linux = {
52 _style_overrides_linux = {
53 Token.Prompt: '#00cc00',
53 Token.Prompt: '#00cc00',
54 Token.PromptNum: '#00bb00 bold',
54 Token.PromptNum: '#00bb00 bold',
55 Token.OutPrompt: '#cc0000',
55 Token.OutPrompt: '#cc0000',
56 Token.OutPromptNum: '#bb0000 bold',
56 Token.OutPromptNum: '#bb0000 bold',
57 }
57 }
58
58
59
59
60
60
61 def get_default_editor():
61 def get_default_editor():
62 try:
62 try:
63 ed = os.environ['EDITOR']
63 ed = os.environ['EDITOR']
64 if not PY3:
64 if not PY3:
65 ed = ed.decode()
65 ed = ed.decode()
66 return ed
66 return ed
67 except KeyError:
67 except KeyError:
68 pass
68 pass
69 except UnicodeError:
69 except UnicodeError:
70 warn("$EDITOR environment variable is not pure ASCII. Using platform "
70 warn("$EDITOR environment variable is not pure ASCII. Using platform "
71 "default editor.")
71 "default editor.")
72
72
73 if os.name == 'posix':
73 if os.name == 'posix':
74 return 'vi' # the only one guaranteed to be there!
74 return 'vi' # the only one guaranteed to be there!
75 else:
75 else:
76 return 'notepad' # same in Windows!
76 return 'notepad' # same in Windows!
77
77
78 # conservatively check for tty
78 # conservatively check for tty
79 # overridden streams can result in things like:
79 # overridden streams can result in things like:
80 # - sys.stdin = None
80 # - sys.stdin = None
81 # - no isatty method
81 # - no isatty method
82 for _name in ('stdin', 'stdout', 'stderr'):
82 for _name in ('stdin', 'stdout', 'stderr'):
83 _stream = getattr(sys, _name)
83 _stream = getattr(sys, _name)
84 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
84 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
85 _is_tty = False
85 _is_tty = False
86 break
86 break
87 else:
87 else:
88 _is_tty = True
88 _is_tty = True
89
89
90
90
91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
92
92
93 class TerminalInteractiveShell(InteractiveShell):
93 class TerminalInteractiveShell(InteractiveShell):
94 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
94 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
95 'to reserve for the completion menu'
95 'to reserve for the completion menu'
96 ).tag(config=True)
96 ).tag(config=True)
97
97
98 def _space_for_menu_changed(self, old, new):
98 def _space_for_menu_changed(self, old, new):
99 self._update_layout()
99 self._update_layout()
100
100
101 pt_cli = None
101 pt_cli = None
102 debugger_history = None
102 debugger_history = None
103 _pt_app = None
103 _pt_app = None
104
104
105 simple_prompt = Bool(_use_simple_prompt,
105 simple_prompt = Bool(_use_simple_prompt,
106 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
106 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
107
107
108 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
108 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
109 IPython own testing machinery, and emacs inferior-shell integration through elpy.
109 IPython own testing machinery, and emacs inferior-shell integration through elpy.
110
110
111 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
111 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
112 environment variable is set, or the current terminal is not a tty.
112 environment variable is set, or the current terminal is not a tty.
113
113
114 """
114 """
115 ).tag(config=True)
115 ).tag(config=True)
116
116
117 @property
117 @property
118 def debugger_cls(self):
118 def debugger_cls(self):
119 return Pdb if self.simple_prompt else TerminalPdb
119 return Pdb if self.simple_prompt else TerminalPdb
120
120
121 confirm_exit = Bool(True,
121 confirm_exit = Bool(True,
122 help="""
122 help="""
123 Set to confirm when you try to exit IPython with an EOF (Control-D
123 Set to confirm when you try to exit IPython with an EOF (Control-D
124 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
124 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
125 you can force a direct exit without any confirmation.""",
125 you can force a direct exit without any confirmation.""",
126 ).tag(config=True)
126 ).tag(config=True)
127
127
128 editing_mode = Unicode('emacs',
128 editing_mode = Unicode('emacs',
129 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
129 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
130 ).tag(config=True)
130 ).tag(config=True)
131
131
132 mouse_support = Bool(False,
132 mouse_support = Bool(False,
133 help="Enable mouse support in the prompt"
133 help="Enable mouse support in the prompt"
134 ).tag(config=True)
134 ).tag(config=True)
135
135
136 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
136 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
137 help="""The name or class of a Pygments style to use for syntax
137 help="""The name or class of a Pygments style to use for syntax
138 highlighting: \n %s""" % ', '.join(get_all_styles())
138 highlighting: \n %s""" % ', '.join(get_all_styles())
139 ).tag(config=True)
139 ).tag(config=True)
140
140
141
141
142 @observe('highlighting_style')
142 @observe('highlighting_style')
143 @observe('colors')
143 @observe('colors')
144 def _highlighting_style_changed(self, change):
144 def _highlighting_style_changed(self, change):
145 self.refresh_style()
145 self.refresh_style()
146
146
147 def refresh_style(self):
147 def refresh_style(self):
148 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
148 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
149
149
150
150
151 highlighting_style_overrides = Dict(
151 highlighting_style_overrides = Dict(
152 help="Override highlighting format for specific tokens"
152 help="Override highlighting format for specific tokens"
153 ).tag(config=True)
153 ).tag(config=True)
154
154
155 true_color = Bool(False,
155 true_color = Bool(False,
156 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
156 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
157 "If your terminal supports true color, the following command "
157 "If your terminal supports true color, the following command "
158 "should print 'TRUECOLOR' in orange: "
158 "should print 'TRUECOLOR' in orange: "
159 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
159 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
160 ).tag(config=True)
160 ).tag(config=True)
161
161
162 editor = Unicode(get_default_editor(),
162 editor = Unicode(get_default_editor(),
163 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
163 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
164 ).tag(config=True)
164 ).tag(config=True)
165
165
166 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
166 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
167
167
168 prompts = Instance(Prompts)
168 prompts = Instance(Prompts)
169
169
170 @default('prompts')
170 @default('prompts')
171 def _prompts_default(self):
171 def _prompts_default(self):
172 return self.prompts_class(self)
172 return self.prompts_class(self)
173
173
174 @observe('prompts')
174 @observe('prompts')
175 def _(self, change):
175 def _(self, change):
176 self._update_layout()
176 self._update_layout()
177
177
178 @default('displayhook_class')
178 @default('displayhook_class')
179 def _displayhook_class_default(self):
179 def _displayhook_class_default(self):
180 return RichPromptDisplayHook
180 return RichPromptDisplayHook
181
181
182 term_title = Bool(True,
182 term_title = Bool(True,
183 help="Automatically set the terminal title"
183 help="Automatically set the terminal title"
184 ).tag(config=True)
184 ).tag(config=True)
185
185
186 display_completions = Enum(('column', 'multicolumn','readlinelike'),
186 display_completions = Enum(('column', 'multicolumn','readlinelike'),
187 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
187 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
188 "'readlinelike'. These options are for `prompt_toolkit`, see "
188 "'readlinelike'. These options are for `prompt_toolkit`, see "
189 "`prompt_toolkit` documentation for more information."
189 "`prompt_toolkit` documentation for more information."
190 ),
190 ),
191 default_value='multicolumn').tag(config=True)
191 default_value='multicolumn').tag(config=True)
192
192
193 highlight_matching_brackets = Bool(True,
193 highlight_matching_brackets = Bool(True,
194 help="Highlight matching brackets .",
194 help="Highlight matching brackets .",
195 ).tag(config=True)
195 ).tag(config=True)
196
196
197 @observe('term_title')
197 @observe('term_title')
198 def init_term_title(self, change=None):
198 def init_term_title(self, change=None):
199 # Enable or disable the terminal title.
199 # Enable or disable the terminal title.
200 if self.term_title:
200 if self.term_title:
201 toggle_set_term_title(True)
201 toggle_set_term_title(True)
202 set_term_title('IPython: ' + abbrev_cwd())
202 set_term_title('IPython: ' + abbrev_cwd())
203 else:
203 else:
204 toggle_set_term_title(False)
204 toggle_set_term_title(False)
205
205
206 def init_display_formatter(self):
206 def init_display_formatter(self):
207 super(TerminalInteractiveShell, self).init_display_formatter()
207 super(TerminalInteractiveShell, self).init_display_formatter()
208 # terminal only supports plain text
208 # terminal only supports plain text
209 self.display_formatter.active_types = ['text/plain']
209 self.display_formatter.active_types = ['text/plain']
210
210
211 def init_prompt_toolkit_cli(self):
211 def init_prompt_toolkit_cli(self):
212 if self.simple_prompt:
212 if self.simple_prompt:
213 # Fall back to plain non-interactive output for tests.
213 # Fall back to plain non-interactive output for tests.
214 # This is very limited, and only accepts a single line.
214 # This is very limited, and only accepts a single line.
215 def prompt():
215 def prompt():
216 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
216 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
217 self.prompt_for_code = prompt
217 self.prompt_for_code = prompt
218 return
218 return
219
219
220 # Set up keyboard shortcuts
220 # Set up keyboard shortcuts
221 kbmanager = KeyBindingManager.for_prompt()
221 kbmanager = KeyBindingManager.for_prompt()
222 register_ipython_shortcuts(kbmanager.registry, self)
222 register_ipython_shortcuts(kbmanager.registry, self)
223
223
224 # Pre-populate history from IPython's history database
224 # Pre-populate history from IPython's history database
225 history = InMemoryHistory()
225 history = InMemoryHistory()
226 last_cell = u""
226 last_cell = u""
227 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
227 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
228 include_latest=True):
228 include_latest=True):
229 # Ignore blank lines and consecutive duplicates
229 # Ignore blank lines and consecutive duplicates
230 cell = cell.rstrip()
230 cell = cell.rstrip()
231 if cell and (cell != last_cell):
231 if cell and (cell != last_cell):
232 history.append(cell)
232 history.append(cell)
233 last_cell = cell
233
234
234 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
235 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
235 style = DynamicStyle(lambda: self._style)
236 style = DynamicStyle(lambda: self._style)
236
237
237 editing_mode = getattr(EditingMode, self.editing_mode.upper())
238 editing_mode = getattr(EditingMode, self.editing_mode.upper())
238
239
239 self._pt_app = create_prompt_application(
240 self._pt_app = create_prompt_application(
240 editing_mode=editing_mode,
241 editing_mode=editing_mode,
241 key_bindings_registry=kbmanager.registry,
242 key_bindings_registry=kbmanager.registry,
242 history=history,
243 history=history,
243 completer=IPythonPTCompleter(shell=self),
244 completer=IPythonPTCompleter(shell=self),
244 enable_history_search=True,
245 enable_history_search=True,
245 style=style,
246 style=style,
246 mouse_support=self.mouse_support,
247 mouse_support=self.mouse_support,
247 **self._layout_options()
248 **self._layout_options()
248 )
249 )
249 self._eventloop = create_eventloop(self.inputhook)
250 self._eventloop = create_eventloop(self.inputhook)
250 self.pt_cli = CommandLineInterface(
251 self.pt_cli = CommandLineInterface(
251 self._pt_app, eventloop=self._eventloop,
252 self._pt_app, eventloop=self._eventloop,
252 output=create_output(true_color=self.true_color))
253 output=create_output(true_color=self.true_color))
253
254
254 def _make_style_from_name_or_cls(self, name_or_cls):
255 def _make_style_from_name_or_cls(self, name_or_cls):
255 """
256 """
256 Small wrapper that make an IPython compatible style from a style name
257 Small wrapper that make an IPython compatible style from a style name
257
258
258 We need that to add style for prompt ... etc.
259 We need that to add style for prompt ... etc.
259 """
260 """
260 style_overrides = {}
261 style_overrides = {}
261 if name_or_cls == 'legacy':
262 if name_or_cls == 'legacy':
262 legacy = self.colors.lower()
263 legacy = self.colors.lower()
263 if legacy == 'linux':
264 if legacy == 'linux':
264 style_cls = get_style_by_name('monokai')
265 style_cls = get_style_by_name('monokai')
265 style_overrides = _style_overrides_linux
266 style_overrides = _style_overrides_linux
266 elif legacy == 'lightbg':
267 elif legacy == 'lightbg':
267 style_overrides = _style_overrides_light_bg
268 style_overrides = _style_overrides_light_bg
268 style_cls = get_style_by_name('pastie')
269 style_cls = get_style_by_name('pastie')
269 elif legacy == 'neutral':
270 elif legacy == 'neutral':
270 # The default theme needs to be visible on both a dark background
271 # The default theme needs to be visible on both a dark background
271 # and a light background, because we can't tell what the terminal
272 # and a light background, because we can't tell what the terminal
272 # looks like. These tweaks to the default theme help with that.
273 # looks like. These tweaks to the default theme help with that.
273 style_cls = get_style_by_name('default')
274 style_cls = get_style_by_name('default')
274 style_overrides.update({
275 style_overrides.update({
275 Token.Number: '#007700',
276 Token.Number: '#007700',
276 Token.Operator: 'noinherit',
277 Token.Operator: 'noinherit',
277 Token.String: '#BB6622',
278 Token.String: '#BB6622',
278 Token.Name.Function: '#2080D0',
279 Token.Name.Function: '#2080D0',
279 Token.Name.Class: 'bold #2080D0',
280 Token.Name.Class: 'bold #2080D0',
280 Token.Name.Namespace: 'bold #2080D0',
281 Token.Name.Namespace: 'bold #2080D0',
281 Token.Prompt: '#009900',
282 Token.Prompt: '#009900',
282 Token.PromptNum: '#00ff00 bold',
283 Token.PromptNum: '#00ff00 bold',
283 Token.OutPrompt: '#990000',
284 Token.OutPrompt: '#990000',
284 Token.OutPromptNum: '#ff0000 bold',
285 Token.OutPromptNum: '#ff0000 bold',
285 })
286 })
286 elif legacy =='nocolor':
287 elif legacy =='nocolor':
287 style_cls=_NoStyle
288 style_cls=_NoStyle
288 style_overrides = {}
289 style_overrides = {}
289 else :
290 else :
290 raise ValueError('Got unknown colors: ', legacy)
291 raise ValueError('Got unknown colors: ', legacy)
291 else :
292 else :
292 if isinstance(name_or_cls, string_types):
293 if isinstance(name_or_cls, string_types):
293 style_cls = get_style_by_name(name_or_cls)
294 style_cls = get_style_by_name(name_or_cls)
294 else:
295 else:
295 style_cls = name_or_cls
296 style_cls = name_or_cls
296 style_overrides = {
297 style_overrides = {
297 Token.Prompt: '#009900',
298 Token.Prompt: '#009900',
298 Token.PromptNum: '#00ff00 bold',
299 Token.PromptNum: '#00ff00 bold',
299 Token.OutPrompt: '#990000',
300 Token.OutPrompt: '#990000',
300 Token.OutPromptNum: '#ff0000 bold',
301 Token.OutPromptNum: '#ff0000 bold',
301 }
302 }
302 style_overrides.update(self.highlighting_style_overrides)
303 style_overrides.update(self.highlighting_style_overrides)
303 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
304 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
304 style_dict=style_overrides)
305 style_dict=style_overrides)
305
306
306 return style
307 return style
307
308
308 def _layout_options(self):
309 def _layout_options(self):
309 """
310 """
310 Return the current layout option for the current Terminal InteractiveShell
311 Return the current layout option for the current Terminal InteractiveShell
311 """
312 """
312 return {
313 return {
313 'lexer':IPythonPTLexer(),
314 'lexer':IPythonPTLexer(),
314 'reserve_space_for_menu':self.space_for_menu,
315 'reserve_space_for_menu':self.space_for_menu,
315 'get_prompt_tokens':self.prompts.in_prompt_tokens,
316 'get_prompt_tokens':self.prompts.in_prompt_tokens,
316 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
317 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
317 'multiline':True,
318 'multiline':True,
318 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
319 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
319
320
320 # Highlight matching brackets, but only when this setting is
321 # Highlight matching brackets, but only when this setting is
321 # enabled, and only when the DEFAULT_BUFFER has the focus.
322 # enabled, and only when the DEFAULT_BUFFER has the focus.
322 'extra_input_processors': [ConditionalProcessor(
323 'extra_input_processors': [ConditionalProcessor(
323 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
324 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
324 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
325 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
325 Condition(lambda cli: self.highlight_matching_brackets))],
326 Condition(lambda cli: self.highlight_matching_brackets))],
326 }
327 }
327
328
328 def _update_layout(self):
329 def _update_layout(self):
329 """
330 """
330 Ask for a re computation of the application layout, if for example ,
331 Ask for a re computation of the application layout, if for example ,
331 some configuration options have changed.
332 some configuration options have changed.
332 """
333 """
333 if self._pt_app:
334 if self._pt_app:
334 self._pt_app.layout = create_prompt_layout(**self._layout_options())
335 self._pt_app.layout = create_prompt_layout(**self._layout_options())
335
336
336 def prompt_for_code(self):
337 def prompt_for_code(self):
337 document = self.pt_cli.run(
338 document = self.pt_cli.run(
338 pre_run=self.pre_prompt, reset_current_buffer=True)
339 pre_run=self.pre_prompt, reset_current_buffer=True)
339 return document.text
340 return document.text
340
341
341 def enable_win_unicode_console(self):
342 def enable_win_unicode_console(self):
342 import win_unicode_console
343 import win_unicode_console
343
344
344 if PY3:
345 if PY3:
345 win_unicode_console.enable()
346 win_unicode_console.enable()
346 else:
347 else:
347 # https://github.com/ipython/ipython/issues/9768
348 # https://github.com/ipython/ipython/issues/9768
348 from win_unicode_console.streams import (TextStreamWrapper,
349 from win_unicode_console.streams import (TextStreamWrapper,
349 stdout_text_transcoded, stderr_text_transcoded)
350 stdout_text_transcoded, stderr_text_transcoded)
350
351
351 class LenientStrStreamWrapper(TextStreamWrapper):
352 class LenientStrStreamWrapper(TextStreamWrapper):
352 def write(self, s):
353 def write(self, s):
353 if isinstance(s, bytes):
354 if isinstance(s, bytes):
354 s = s.decode(self.encoding, 'replace')
355 s = s.decode(self.encoding, 'replace')
355
356
356 self.base.write(s)
357 self.base.write(s)
357
358
358 stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded)
359 stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded)
359 stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded)
360 stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded)
360
361
361 win_unicode_console.enable(stdout=stdout_text_str,
362 win_unicode_console.enable(stdout=stdout_text_str,
362 stderr=stderr_text_str)
363 stderr=stderr_text_str)
363
364
364 def init_io(self):
365 def init_io(self):
365 if sys.platform not in {'win32', 'cli'}:
366 if sys.platform not in {'win32', 'cli'}:
366 return
367 return
367
368
368 self.enable_win_unicode_console()
369 self.enable_win_unicode_console()
369
370
370 import colorama
371 import colorama
371 colorama.init()
372 colorama.init()
372
373
373 # For some reason we make these wrappers around stdout/stderr.
374 # For some reason we make these wrappers around stdout/stderr.
374 # For now, we need to reset them so all output gets coloured.
375 # For now, we need to reset them so all output gets coloured.
375 # https://github.com/ipython/ipython/issues/8669
376 # https://github.com/ipython/ipython/issues/8669
376 # io.std* are deprecated, but don't show our own deprecation warnings
377 # io.std* are deprecated, but don't show our own deprecation warnings
377 # during initialization of the deprecated API.
378 # during initialization of the deprecated API.
378 with warnings.catch_warnings():
379 with warnings.catch_warnings():
379 warnings.simplefilter('ignore', DeprecationWarning)
380 warnings.simplefilter('ignore', DeprecationWarning)
380 io.stdout = io.IOStream(sys.stdout)
381 io.stdout = io.IOStream(sys.stdout)
381 io.stderr = io.IOStream(sys.stderr)
382 io.stderr = io.IOStream(sys.stderr)
382
383
383 def init_magics(self):
384 def init_magics(self):
384 super(TerminalInteractiveShell, self).init_magics()
385 super(TerminalInteractiveShell, self).init_magics()
385 self.register_magics(TerminalMagics)
386 self.register_magics(TerminalMagics)
386
387
387 def init_alias(self):
388 def init_alias(self):
388 # The parent class defines aliases that can be safely used with any
389 # The parent class defines aliases that can be safely used with any
389 # frontend.
390 # frontend.
390 super(TerminalInteractiveShell, self).init_alias()
391 super(TerminalInteractiveShell, self).init_alias()
391
392
392 # Now define aliases that only make sense on the terminal, because they
393 # Now define aliases that only make sense on the terminal, because they
393 # need direct access to the console in a way that we can't emulate in
394 # need direct access to the console in a way that we can't emulate in
394 # GUI or web frontend
395 # GUI or web frontend
395 if os.name == 'posix':
396 if os.name == 'posix':
396 for cmd in ['clear', 'more', 'less', 'man']:
397 for cmd in ['clear', 'more', 'less', 'man']:
397 self.alias_manager.soft_define_alias(cmd, cmd)
398 self.alias_manager.soft_define_alias(cmd, cmd)
398
399
399
400
400 def __init__(self, *args, **kwargs):
401 def __init__(self, *args, **kwargs):
401 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
402 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
402 self.init_prompt_toolkit_cli()
403 self.init_prompt_toolkit_cli()
403 self.init_term_title()
404 self.init_term_title()
404 self.keep_running = True
405 self.keep_running = True
405
406
406 self.debugger_history = InMemoryHistory()
407 self.debugger_history = InMemoryHistory()
407
408
408 def ask_exit(self):
409 def ask_exit(self):
409 self.keep_running = False
410 self.keep_running = False
410
411
411 rl_next_input = None
412 rl_next_input = None
412
413
413 def pre_prompt(self):
414 def pre_prompt(self):
414 if self.rl_next_input:
415 if self.rl_next_input:
415 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
416 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
416 self.rl_next_input = None
417 self.rl_next_input = None
417
418
418 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
419 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
419
420
420 if display_banner is not DISPLAY_BANNER_DEPRECATED:
421 if display_banner is not DISPLAY_BANNER_DEPRECATED:
421 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
422 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
422
423
423 self.keep_running = True
424 self.keep_running = True
424 while self.keep_running:
425 while self.keep_running:
425 print(self.separate_in, end='')
426 print(self.separate_in, end='')
426
427
427 try:
428 try:
428 code = self.prompt_for_code()
429 code = self.prompt_for_code()
429 except EOFError:
430 except EOFError:
430 if (not self.confirm_exit) \
431 if (not self.confirm_exit) \
431 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
432 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
432 self.ask_exit()
433 self.ask_exit()
433
434
434 else:
435 else:
435 if code:
436 if code:
436 self.run_cell(code, store_history=True)
437 self.run_cell(code, store_history=True)
437
438
438 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
439 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
439 # An extra layer of protection in case someone mashing Ctrl-C breaks
440 # An extra layer of protection in case someone mashing Ctrl-C breaks
440 # out of our internal code.
441 # out of our internal code.
441 if display_banner is not DISPLAY_BANNER_DEPRECATED:
442 if display_banner is not DISPLAY_BANNER_DEPRECATED:
442 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
443 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
443 while True:
444 while True:
444 try:
445 try:
445 self.interact()
446 self.interact()
446 break
447 break
447 except KeyboardInterrupt:
448 except KeyboardInterrupt:
448 print("\nKeyboardInterrupt escaped interact()\n")
449 print("\nKeyboardInterrupt escaped interact()\n")
449
450
450 _inputhook = None
451 _inputhook = None
451 def inputhook(self, context):
452 def inputhook(self, context):
452 if self._inputhook is not None:
453 if self._inputhook is not None:
453 self._inputhook(context)
454 self._inputhook(context)
454
455
455 def enable_gui(self, gui=None):
456 def enable_gui(self, gui=None):
456 if gui:
457 if gui:
457 self._inputhook = get_inputhook_func(gui)
458 self._inputhook = get_inputhook_func(gui)
458 else:
459 else:
459 self._inputhook = None
460 self._inputhook = None
460
461
461 # Run !system commands directly, not through pipes, so terminal programs
462 # Run !system commands directly, not through pipes, so terminal programs
462 # work correctly.
463 # work correctly.
463 system = InteractiveShell.system_raw
464 system = InteractiveShell.system_raw
464
465
465 def auto_rewrite_input(self, cmd):
466 def auto_rewrite_input(self, cmd):
466 """Overridden from the parent class to use fancy rewriting prompt"""
467 """Overridden from the parent class to use fancy rewriting prompt"""
467 if not self.show_rewritten_input:
468 if not self.show_rewritten_input:
468 return
469 return
469
470
470 tokens = self.prompts.rewrite_prompt_tokens()
471 tokens = self.prompts.rewrite_prompt_tokens()
471 if self.pt_cli:
472 if self.pt_cli:
472 self.pt_cli.print_tokens(tokens)
473 self.pt_cli.print_tokens(tokens)
473 print(cmd)
474 print(cmd)
474 else:
475 else:
475 prompt = ''.join(s for t, s in tokens)
476 prompt = ''.join(s for t, s in tokens)
476 print(prompt, cmd, sep='')
477 print(prompt, cmd, sep='')
477
478
478 _prompts_before = None
479 _prompts_before = None
479 def switch_doctest_mode(self, mode):
480 def switch_doctest_mode(self, mode):
480 """Switch prompts to classic for %doctest_mode"""
481 """Switch prompts to classic for %doctest_mode"""
481 if mode:
482 if mode:
482 self._prompts_before = self.prompts
483 self._prompts_before = self.prompts
483 self.prompts = ClassicPrompts(self)
484 self.prompts = ClassicPrompts(self)
484 elif self._prompts_before:
485 elif self._prompts_before:
485 self.prompts = self._prompts_before
486 self.prompts = self._prompts_before
486 self._prompts_before = None
487 self._prompts_before = None
487 self._update_layout()
488 self._update_layout()
488
489
489
490
490 InteractiveShellABC.register(TerminalInteractiveShell)
491 InteractiveShellABC.register(TerminalInteractiveShell)
491
492
492 if __name__ == '__main__':
493 if __name__ == '__main__':
493 TerminalInteractiveShell.instance().interact()
494 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now