##// END OF EJS Templates
Merge pull request #9398 from takluyver/ptshell-system-raw...
Min RK -
r22240:c506b6e1 merge
parent child Browse files
Show More
@@ -1,404 +1,409 b''
1 """IPython terminal interface using prompt_toolkit in place of readline"""
1 """IPython terminal interface using prompt_toolkit in place of readline"""
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 signal
6 import signal
7 import unicodedata
7 import unicodedata
8 from warnings import warn
8 from warnings import warn
9 from wcwidth import wcwidth
9 from wcwidth import wcwidth
10
10
11 from IPython.core.error import TryNext
11 from IPython.core.error import TryNext
12 from IPython.core.interactiveshell import InteractiveShell
12 from IPython.core.interactiveshell import InteractiveShell
13 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
13 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
14 from IPython.utils.terminal import toggle_set_term_title, set_term_title
14 from IPython.utils.terminal import toggle_set_term_title, set_term_title
15 from IPython.utils.process import abbrev_cwd
15 from IPython.utils.process import abbrev_cwd
16 from traitlets import Bool, CBool, Unicode, Dict
16 from traitlets import Bool, CBool, Unicode, Dict
17
17
18 from prompt_toolkit.completion import Completer, Completion
18 from prompt_toolkit.completion import Completer, Completion
19 from prompt_toolkit.enums import DEFAULT_BUFFER
19 from prompt_toolkit.enums import DEFAULT_BUFFER
20 from prompt_toolkit.filters import HasFocus, HasSelection, Condition
20 from prompt_toolkit.filters import HasFocus, HasSelection, Condition
21 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
23 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.interface import CommandLineInterface
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
25 from prompt_toolkit.key_binding.vi_state import InputMode
25 from prompt_toolkit.key_binding.vi_state import InputMode
26 from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
26 from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
27 from prompt_toolkit.keys import Keys
27 from prompt_toolkit.keys import Keys
28 from prompt_toolkit.layout.lexers import Lexer
28 from prompt_toolkit.layout.lexers import Lexer
29 from prompt_toolkit.layout.lexers import PygmentsLexer
29 from prompt_toolkit.layout.lexers import PygmentsLexer
30 from prompt_toolkit.styles import PygmentsStyle
30 from prompt_toolkit.styles import PygmentsStyle
31
31
32 from pygments.styles import get_style_by_name
32 from pygments.styles import get_style_by_name
33 from pygments.lexers import Python3Lexer, BashLexer, PythonLexer
33 from pygments.lexers import Python3Lexer, BashLexer, PythonLexer
34 from pygments.token import Token
34 from pygments.token import Token
35
35
36 from .pt_inputhooks import get_inputhook_func
36 from .pt_inputhooks import get_inputhook_func
37 from .interactiveshell import get_default_editor, TerminalMagics
37 from .interactiveshell import get_default_editor, TerminalMagics
38
38
39
39
40 class IPythonPTCompleter(Completer):
40 class IPythonPTCompleter(Completer):
41 """Adaptor to provide IPython completions to prompt_toolkit"""
41 """Adaptor to provide IPython completions to prompt_toolkit"""
42 def __init__(self, ipy_completer):
42 def __init__(self, ipy_completer):
43 self.ipy_completer = ipy_completer
43 self.ipy_completer = ipy_completer
44
44
45 def get_completions(self, document, complete_event):
45 def get_completions(self, document, complete_event):
46 if not document.current_line.strip():
46 if not document.current_line.strip():
47 return
47 return
48
48
49 used, matches = self.ipy_completer.complete(
49 used, matches = self.ipy_completer.complete(
50 line_buffer=document.current_line,
50 line_buffer=document.current_line,
51 cursor_pos=document.cursor_position_col
51 cursor_pos=document.cursor_position_col
52 )
52 )
53 start_pos = -len(used)
53 start_pos = -len(used)
54 for m in matches:
54 for m in matches:
55 m = unicodedata.normalize('NFC', m)
55 m = unicodedata.normalize('NFC', m)
56
56
57 # When the first character of the completion has a zero length,
57 # When the first character of the completion has a zero length,
58 # then it's probably a decomposed unicode character. E.g. caused by
58 # then it's probably a decomposed unicode character. E.g. caused by
59 # the "\dot" completion. Try to compose again with the previous
59 # the "\dot" completion. Try to compose again with the previous
60 # character.
60 # character.
61 if wcwidth(m[0]) == 0:
61 if wcwidth(m[0]) == 0:
62 if document.cursor_position + start_pos > 0:
62 if document.cursor_position + start_pos > 0:
63 char_before = document.text[document.cursor_position + start_pos - 1]
63 char_before = document.text[document.cursor_position + start_pos - 1]
64 m = unicodedata.normalize('NFC', char_before + m)
64 m = unicodedata.normalize('NFC', char_before + m)
65
65
66 # Yield the modified completion instead, if this worked.
66 # Yield the modified completion instead, if this worked.
67 if wcwidth(m[0:1]) == 1:
67 if wcwidth(m[0:1]) == 1:
68 yield Completion(m, start_position=start_pos - 1)
68 yield Completion(m, start_position=start_pos - 1)
69 continue
69 continue
70
70
71 yield Completion(m, start_position=start_pos)
71 yield Completion(m, start_position=start_pos)
72
72
73
73
74 class IPythonPTLexer(Lexer):
74 class IPythonPTLexer(Lexer):
75 """
75 """
76 Wrapper around PythonLexer and BashLexer.
76 Wrapper around PythonLexer and BashLexer.
77 """
77 """
78 def __init__(self):
78 def __init__(self):
79 self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer)
79 self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer)
80 self.shell_lexer = PygmentsLexer(BashLexer)
80 self.shell_lexer = PygmentsLexer(BashLexer)
81
81
82 def lex_document(self, cli, document):
82 def lex_document(self, cli, document):
83 if document.text.startswith('!'):
83 if document.text.startswith('!'):
84 return self.shell_lexer.lex_document(cli, document)
84 return self.shell_lexer.lex_document(cli, document)
85 else:
85 else:
86 return self.python_lexer.lex_document(cli, document)
86 return self.python_lexer.lex_document(cli, document)
87
87
88
88
89 class TerminalInteractiveShell(InteractiveShell):
89 class TerminalInteractiveShell(InteractiveShell):
90 colors_force = True
90 colors_force = True
91
91
92 pt_cli = None
92 pt_cli = None
93
93
94 autoedit_syntax = CBool(False, config=True,
94 autoedit_syntax = CBool(False, config=True,
95 help="auto editing of files with syntax errors.")
95 help="auto editing of files with syntax errors.")
96
96
97 confirm_exit = CBool(True, config=True,
97 confirm_exit = CBool(True, config=True,
98 help="""
98 help="""
99 Set to confirm when you try to exit IPython with an EOF (Control-D
99 Set to confirm when you try to exit IPython with an EOF (Control-D
100 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
100 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
101 you can force a direct exit without any confirmation.""",
101 you can force a direct exit without any confirmation.""",
102 )
102 )
103 vi_mode = Bool(False, config=True,
103 vi_mode = Bool(False, config=True,
104 help="Use vi style keybindings at the prompt",
104 help="Use vi style keybindings at the prompt",
105 )
105 )
106
106
107 mouse_support = Bool(False, config=True,
107 mouse_support = Bool(False, config=True,
108 help="Enable mouse support in the prompt"
108 help="Enable mouse support in the prompt"
109 )
109 )
110
110
111 highlighting_style = Unicode('', config=True,
111 highlighting_style = Unicode('', config=True,
112 help="The name of a Pygments style to use for syntax highlighting"
112 help="The name of a Pygments style to use for syntax highlighting"
113 )
113 )
114
114
115 highlighting_style_overrides = Dict(config=True,
115 highlighting_style_overrides = Dict(config=True,
116 help="Override highlighting format for specific tokens"
116 help="Override highlighting format for specific tokens"
117 )
117 )
118
118
119 editor = Unicode(get_default_editor(), config=True,
119 editor = Unicode(get_default_editor(), config=True,
120 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
120 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
121 )
121 )
122
122
123 term_title = Bool(True, config=True,
123 term_title = Bool(True, config=True,
124 help="Automatically set the terminal title"
124 help="Automatically set the terminal title"
125 )
125 )
126 def _term_title_changed(self, name, new_value):
126 def _term_title_changed(self, name, new_value):
127 self.init_term_title()
127 self.init_term_title()
128
128
129 def init_term_title(self):
129 def init_term_title(self):
130 # Enable or disable the terminal title.
130 # Enable or disable the terminal title.
131 if self.term_title:
131 if self.term_title:
132 toggle_set_term_title(True)
132 toggle_set_term_title(True)
133 set_term_title('IPython: ' + abbrev_cwd())
133 set_term_title('IPython: ' + abbrev_cwd())
134 else:
134 else:
135 toggle_set_term_title(False)
135 toggle_set_term_title(False)
136
136
137 def get_prompt_tokens(self, cli):
137 def get_prompt_tokens(self, cli):
138 return [
138 return [
139 (Token.Prompt, 'In ['),
139 (Token.Prompt, 'In ['),
140 (Token.PromptNum, str(self.execution_count)),
140 (Token.PromptNum, str(self.execution_count)),
141 (Token.Prompt, ']: '),
141 (Token.Prompt, ']: '),
142 ]
142 ]
143
143
144 def get_continuation_tokens(self, cli, width):
144 def get_continuation_tokens(self, cli, width):
145 return [
145 return [
146 (Token.Prompt, (' ' * (width - 5)) + '...: '),
146 (Token.Prompt, (' ' * (width - 5)) + '...: '),
147 ]
147 ]
148
148
149 def init_prompt_toolkit_cli(self):
149 def init_prompt_toolkit_cli(self):
150 if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty():
150 if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty():
151 # Fall back to plain non-interactive output for tests.
151 # Fall back to plain non-interactive output for tests.
152 # This is very limited, and only accepts a single line.
152 # This is very limited, and only accepts a single line.
153 def prompt():
153 def prompt():
154 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
154 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
155 self.prompt_for_code = prompt
155 self.prompt_for_code = prompt
156 return
156 return
157
157
158 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
158 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
159 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
159 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
160 # Ctrl+J == Enter, seemingly
160 # Ctrl+J == Enter, seemingly
161 @kbmanager.registry.add_binding(Keys.ControlJ,
161 @kbmanager.registry.add_binding(Keys.ControlJ,
162 filter=(HasFocus(DEFAULT_BUFFER)
162 filter=(HasFocus(DEFAULT_BUFFER)
163 & ~HasSelection()
163 & ~HasSelection()
164 & insert_mode
164 & insert_mode
165 ))
165 ))
166 def _(event):
166 def _(event):
167 b = event.current_buffer
167 b = event.current_buffer
168 d = b.document
168 d = b.document
169 if not (d.on_last_line or d.cursor_position_row >= d.line_count
169 if not (d.on_last_line or d.cursor_position_row >= d.line_count
170 - d.empty_line_count_at_the_end()):
170 - d.empty_line_count_at_the_end()):
171 b.newline()
171 b.newline()
172 return
172 return
173
173
174 status, indent = self.input_splitter.check_complete(d.text)
174 status, indent = self.input_splitter.check_complete(d.text)
175
175
176 if (status != 'incomplete') and b.accept_action.is_returnable:
176 if (status != 'incomplete') and b.accept_action.is_returnable:
177 b.accept_action.validate_and_handle(event.cli, b)
177 b.accept_action.validate_and_handle(event.cli, b)
178 else:
178 else:
179 b.insert_text('\n' + (' ' * (indent or 0)))
179 b.insert_text('\n' + (' ' * (indent or 0)))
180
180
181 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
181 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
182 def _(event):
182 def _(event):
183 event.current_buffer.reset()
183 event.current_buffer.reset()
184
184
185 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
185 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
186
186
187 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
187 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
188 def _(event):
188 def _(event):
189 event.cli.suspend_to_background()
189 event.cli.suspend_to_background()
190
190
191 @Condition
191 @Condition
192 def cursor_in_leading_ws(cli):
192 def cursor_in_leading_ws(cli):
193 before = cli.application.buffer.document.current_line_before_cursor
193 before = cli.application.buffer.document.current_line_before_cursor
194 return (not before) or before.isspace()
194 return (not before) or before.isspace()
195
195
196 # Ctrl+I == Tab
196 # Ctrl+I == Tab
197 @kbmanager.registry.add_binding(Keys.ControlI,
197 @kbmanager.registry.add_binding(Keys.ControlI,
198 filter=(HasFocus(DEFAULT_BUFFER)
198 filter=(HasFocus(DEFAULT_BUFFER)
199 & ~HasSelection()
199 & ~HasSelection()
200 & insert_mode
200 & insert_mode
201 & cursor_in_leading_ws
201 & cursor_in_leading_ws
202 ))
202 ))
203 def _(event):
203 def _(event):
204 event.current_buffer.insert_text(' ' * 4)
204 event.current_buffer.insert_text(' ' * 4)
205
205
206 # Pre-populate history from IPython's history database
206 # Pre-populate history from IPython's history database
207 history = InMemoryHistory()
207 history = InMemoryHistory()
208 last_cell = u""
208 last_cell = u""
209 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
209 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
210 include_latest=True):
210 include_latest=True):
211 # Ignore blank lines and consecutive duplicates
211 # Ignore blank lines and consecutive duplicates
212 cell = cell.rstrip()
212 cell = cell.rstrip()
213 if cell and (cell != last_cell):
213 if cell and (cell != last_cell):
214 history.append(cell)
214 history.append(cell)
215
215
216 style_overrides = {
216 style_overrides = {
217 Token.Prompt: '#009900',
217 Token.Prompt: '#009900',
218 Token.PromptNum: '#00ff00 bold',
218 Token.PromptNum: '#00ff00 bold',
219 }
219 }
220 if self.highlighting_style:
220 if self.highlighting_style:
221 style_cls = get_style_by_name(self.highlighting_style)
221 style_cls = get_style_by_name(self.highlighting_style)
222 else:
222 else:
223 style_cls = get_style_by_name('default')
223 style_cls = get_style_by_name('default')
224 # The default theme needs to be visible on both a dark background
224 # The default theme needs to be visible on both a dark background
225 # and a light background, because we can't tell what the terminal
225 # and a light background, because we can't tell what the terminal
226 # looks like. These tweaks to the default theme help with that.
226 # looks like. These tweaks to the default theme help with that.
227 style_overrides.update({
227 style_overrides.update({
228 Token.Number: '#007700',
228 Token.Number: '#007700',
229 Token.Operator: 'noinherit',
229 Token.Operator: 'noinherit',
230 Token.String: '#BB6622',
230 Token.String: '#BB6622',
231 Token.Name.Function: '#2080D0',
231 Token.Name.Function: '#2080D0',
232 Token.Name.Class: 'bold #2080D0',
232 Token.Name.Class: 'bold #2080D0',
233 Token.Name.Namespace: 'bold #2080D0',
233 Token.Name.Namespace: 'bold #2080D0',
234 })
234 })
235 style_overrides.update(self.highlighting_style_overrides)
235 style_overrides.update(self.highlighting_style_overrides)
236 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
236 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
237 style_dict=style_overrides)
237 style_dict=style_overrides)
238
238
239 app = create_prompt_application(multiline=True,
239 app = create_prompt_application(multiline=True,
240 lexer=IPythonPTLexer(),
240 lexer=IPythonPTLexer(),
241 get_prompt_tokens=self.get_prompt_tokens,
241 get_prompt_tokens=self.get_prompt_tokens,
242 get_continuation_tokens=self.get_continuation_tokens,
242 get_continuation_tokens=self.get_continuation_tokens,
243 key_bindings_registry=kbmanager.registry,
243 key_bindings_registry=kbmanager.registry,
244 history=history,
244 history=history,
245 completer=IPythonPTCompleter(self.Completer),
245 completer=IPythonPTCompleter(self.Completer),
246 enable_history_search=True,
246 enable_history_search=True,
247 style=style,
247 style=style,
248 mouse_support=self.mouse_support,
248 mouse_support=self.mouse_support,
249 reserve_space_for_menu=6,
249 reserve_space_for_menu=6,
250 )
250 )
251
251
252 self.pt_cli = CommandLineInterface(app,
252 self.pt_cli = CommandLineInterface(app,
253 eventloop=create_eventloop(self.inputhook))
253 eventloop=create_eventloop(self.inputhook))
254
254
255 def prompt_for_code(self):
255 def prompt_for_code(self):
256 document = self.pt_cli.run(pre_run=self.pre_prompt)
256 document = self.pt_cli.run(pre_run=self.pre_prompt)
257 return document.text
257 return document.text
258
258
259 def init_io(self):
259 def init_io(self):
260 if sys.platform not in {'win32', 'cli'}:
260 if sys.platform not in {'win32', 'cli'}:
261 return
261 return
262
262
263 import colorama
263 import colorama
264 colorama.init()
264 colorama.init()
265
265
266 # For some reason we make these wrappers around stdout/stderr.
266 # For some reason we make these wrappers around stdout/stderr.
267 # For now, we need to reset them so all output gets coloured.
267 # For now, we need to reset them so all output gets coloured.
268 # https://github.com/ipython/ipython/issues/8669
268 # https://github.com/ipython/ipython/issues/8669
269 from IPython.utils import io
269 from IPython.utils import io
270 io.stdout = io.IOStream(sys.stdout)
270 io.stdout = io.IOStream(sys.stdout)
271 io.stderr = io.IOStream(sys.stderr)
271 io.stderr = io.IOStream(sys.stderr)
272
272
273 def init_magics(self):
273 def init_magics(self):
274 super(TerminalInteractiveShell, self).init_magics()
274 super(TerminalInteractiveShell, self).init_magics()
275 self.register_magics(TerminalMagics)
275 self.register_magics(TerminalMagics)
276
276
277 def init_alias(self):
277 def init_alias(self):
278 # The parent class defines aliases that can be safely used with any
278 # The parent class defines aliases that can be safely used with any
279 # frontend.
279 # frontend.
280 super(TerminalInteractiveShell, self).init_alias()
280 super(TerminalInteractiveShell, self).init_alias()
281
281
282 # Now define aliases that only make sense on the terminal, because they
282 # Now define aliases that only make sense on the terminal, because they
283 # need direct access to the console in a way that we can't emulate in
283 # need direct access to the console in a way that we can't emulate in
284 # GUI or web frontend
284 # GUI or web frontend
285 if os.name == 'posix':
285 if os.name == 'posix':
286 for cmd in ['clear', 'more', 'less', 'man']:
286 for cmd in ['clear', 'more', 'less', 'man']:
287 self.alias_manager.soft_define_alias(cmd, cmd)
287 self.alias_manager.soft_define_alias(cmd, cmd)
288
288
289
289
290 def __init__(self, *args, **kwargs):
290 def __init__(self, *args, **kwargs):
291 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
291 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
292 self.init_prompt_toolkit_cli()
292 self.init_prompt_toolkit_cli()
293 self.init_term_title()
293 self.init_term_title()
294 self.keep_running = True
294 self.keep_running = True
295
295
296 def ask_exit(self):
296 def ask_exit(self):
297 self.keep_running = False
297 self.keep_running = False
298
298
299 rl_next_input = None
299 rl_next_input = None
300
300
301 def pre_prompt(self):
301 def pre_prompt(self):
302 if self.rl_next_input:
302 if self.rl_next_input:
303 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
303 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
304 self.rl_next_input = None
304 self.rl_next_input = None
305
305
306 def interact(self):
306 def interact(self):
307 while self.keep_running:
307 while self.keep_running:
308 print(self.separate_in, end='')
308 print(self.separate_in, end='')
309
309
310 try:
310 try:
311 code = self.prompt_for_code()
311 code = self.prompt_for_code()
312 except EOFError:
312 except EOFError:
313 if (not self.confirm_exit) \
313 if (not self.confirm_exit) \
314 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
314 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
315 self.ask_exit()
315 self.ask_exit()
316
316
317 else:
317 else:
318 if code:
318 if code:
319 self.run_cell(code, store_history=True)
319 self.run_cell(code, store_history=True)
320 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
320 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
321 self.edit_syntax_error()
321 self.edit_syntax_error()
322
322
323 def mainloop(self):
323 def mainloop(self):
324 # An extra layer of protection in case someone mashing Ctrl-C breaks
324 # An extra layer of protection in case someone mashing Ctrl-C breaks
325 # out of our internal code.
325 # out of our internal code.
326 while True:
326 while True:
327 try:
327 try:
328 self.interact()
328 self.interact()
329 break
329 break
330 except KeyboardInterrupt:
330 except KeyboardInterrupt:
331 print("\nKeyboardInterrupt escaped interact()\n")
331 print("\nKeyboardInterrupt escaped interact()\n")
332
332
333 _inputhook = None
333 _inputhook = None
334 def inputhook(self, context):
334 def inputhook(self, context):
335 if self._inputhook is not None:
335 if self._inputhook is not None:
336 self._inputhook(context)
336 self._inputhook(context)
337
337
338 def enable_gui(self, gui=None):
338 def enable_gui(self, gui=None):
339 if gui:
339 if gui:
340 self._inputhook = get_inputhook_func(gui)
340 self._inputhook = get_inputhook_func(gui)
341 else:
341 else:
342 self._inputhook = None
342 self._inputhook = None
343
343
344 # Methods to support auto-editing of SyntaxErrors:
344 # Methods to support auto-editing of SyntaxErrors:
345
345
346 def edit_syntax_error(self):
346 def edit_syntax_error(self):
347 """The bottom half of the syntax error handler called in the main loop.
347 """The bottom half of the syntax error handler called in the main loop.
348
348
349 Loop until syntax error is fixed or user cancels.
349 Loop until syntax error is fixed or user cancels.
350 """
350 """
351
351
352 while self.SyntaxTB.last_syntax_error:
352 while self.SyntaxTB.last_syntax_error:
353 # copy and clear last_syntax_error
353 # copy and clear last_syntax_error
354 err = self.SyntaxTB.clear_err_state()
354 err = self.SyntaxTB.clear_err_state()
355 if not self._should_recompile(err):
355 if not self._should_recompile(err):
356 return
356 return
357 try:
357 try:
358 # may set last_syntax_error again if a SyntaxError is raised
358 # may set last_syntax_error again if a SyntaxError is raised
359 self.safe_execfile(err.filename, self.user_ns)
359 self.safe_execfile(err.filename, self.user_ns)
360 except:
360 except:
361 self.showtraceback()
361 self.showtraceback()
362 else:
362 else:
363 try:
363 try:
364 with open(err.filename) as f:
364 with open(err.filename) as f:
365 # This should be inside a display_trap block and I
365 # This should be inside a display_trap block and I
366 # think it is.
366 # think it is.
367 sys.displayhook(f.read())
367 sys.displayhook(f.read())
368 except:
368 except:
369 self.showtraceback()
369 self.showtraceback()
370
370
371 def _should_recompile(self, e):
371 def _should_recompile(self, e):
372 """Utility routine for edit_syntax_error"""
372 """Utility routine for edit_syntax_error"""
373
373
374 if e.filename in ('<ipython console>', '<input>', '<string>',
374 if e.filename in ('<ipython console>', '<input>', '<string>',
375 '<console>', '<BackgroundJob compilation>',
375 '<console>', '<BackgroundJob compilation>',
376 None):
376 None):
377 return False
377 return False
378 try:
378 try:
379 if (self.autoedit_syntax and
379 if (self.autoedit_syntax and
380 not self.ask_yes_no(
380 not self.ask_yes_no(
381 'Return to editor to correct syntax error? '
381 'Return to editor to correct syntax error? '
382 '[Y/n] ', 'y')):
382 '[Y/n] ', 'y')):
383 return False
383 return False
384 except EOFError:
384 except EOFError:
385 return False
385 return False
386
386
387 def int0(x):
387 def int0(x):
388 try:
388 try:
389 return int(x)
389 return int(x)
390 except TypeError:
390 except TypeError:
391 return 0
391 return 0
392
392
393 # always pass integer line and offset values to editor hook
393 # always pass integer line and offset values to editor hook
394 try:
394 try:
395 self.hooks.fix_error_editor(e.filename,
395 self.hooks.fix_error_editor(e.filename,
396 int0(e.lineno), int0(e.offset),
396 int0(e.lineno), int0(e.offset),
397 e.msg)
397 e.msg)
398 except TryNext:
398 except TryNext:
399 warn('Could not open editor')
399 warn('Could not open editor')
400 return False
400 return False
401 return True
401 return True
402
402
403 # Run !system commands directly, not through pipes, so terminal programs
404 # work correctly.
405 system = InteractiveShell.system_raw
406
407
403 if __name__ == '__main__':
408 if __name__ == '__main__':
404 TerminalInteractiveShell.instance().interact()
409 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now