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