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