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