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