##// END OF EJS Templates
Simplify picking debugger class
Thomas Kluyver -
Show More
@@ -1,449 +1,444 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 from warnings import warn
7 from warnings import warn
8
8
9 from IPython.core.error import TryNext
9 from IPython.core.error import TryNext
10 from IPython.core.interactiveshell import InteractiveShell
10 from IPython.core.interactiveshell import InteractiveShell
11 from IPython.utils.py3compat import cast_unicode_py2, input
11 from IPython.utils.py3compat import cast_unicode_py2, input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe
14 from traitlets import Bool, Unicode, Dict, Integer, observe
15
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
18 from prompt_toolkit.history import InMemoryHistory
18 from prompt_toolkit.history import InMemoryHistory
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
20 from prompt_toolkit.interface import CommandLineInterface
20 from prompt_toolkit.interface import CommandLineInterface
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 from prompt_toolkit.keys import Keys
22 from prompt_toolkit.keys import Keys
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.token import Token
27 from pygments.token import Token
28
28
29 from .debugger import TerminalPdb, Pdb
29 from .debugger import TerminalPdb, Pdb
30 from .pt_inputhooks import get_inputhook_func
30 from .pt_inputhooks import get_inputhook_func
31 from .interactiveshell import get_default_editor, TerminalMagics
31 from .interactiveshell import get_default_editor, TerminalMagics
32 from .ptutils import IPythonPTCompleter, IPythonPTLexer
32 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33
33
34 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
34 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
35
35
36 class TerminalInteractiveShell(InteractiveShell):
36 class TerminalInteractiveShell(InteractiveShell):
37 colors_force = True
37 colors_force = True
38
38
39 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
39 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
40 'to reserve for the completion menu'
40 'to reserve for the completion menu'
41 ).tag(config=True)
41 ).tag(config=True)
42
42
43 def _space_for_menu_changed(self, old, new):
43 def _space_for_menu_changed(self, old, new):
44 self._update_layout()
44 self._update_layout()
45
45
46 pt_cli = None
46 pt_cli = None
47 debugger_history = None
47 debugger_history = None
48 debugger_cls = TerminalPdb
49
48
50 simple_prompt = Bool(_use_simple_prompt,
49 simple_prompt = Bool(_use_simple_prompt,
51 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
50 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
52
51
53 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
52 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
54 IPython own testing machinery, and emacs inferior-shell integration through elpy.
53 IPython own testing machinery, and emacs inferior-shell integration through elpy.
55
54
56 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
55 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
57 environment variable is set, or the current terminal is not a tty.
56 environment variable is set, or the current terminal is not a tty.
58
57
59 """
58 """
60 ).tag(config=True)
59 ).tag(config=True)
61
60
62 @observe('simple_prompt')
61 @property
63 def _simple_prompt_changed(self, changes):
62 def debugger_cls(self):
64 if changes['new'] == True:
63 return Pdb if self.simple_prompt else TerminalPdb
65 self.debugger_cls = Pdb
66 else:
67 self.debugger_cls = TerminalPdb
68
69
64
70 autoedit_syntax = Bool(False,
65 autoedit_syntax = Bool(False,
71 help="auto editing of files with syntax errors.",
66 help="auto editing of files with syntax errors.",
72 ).tag(config=True)
67 ).tag(config=True)
73
68
74
69
75 confirm_exit = Bool(True,
70 confirm_exit = Bool(True,
76 help="""
71 help="""
77 Set to confirm when you try to exit IPython with an EOF (Control-D
72 Set to confirm when you try to exit IPython with an EOF (Control-D
78 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
73 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
79 you can force a direct exit without any confirmation.""",
74 you can force a direct exit without any confirmation.""",
80 ).tag(config=True)
75 ).tag(config=True)
81
76
82 editing_mode = Unicode('emacs',
77 editing_mode = Unicode('emacs',
83 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
78 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
84 ).tag(config=True)
79 ).tag(config=True)
85
80
86 mouse_support = Bool(False,
81 mouse_support = Bool(False,
87 help="Enable mouse support in the prompt"
82 help="Enable mouse support in the prompt"
88 ).tag(config=True)
83 ).tag(config=True)
89
84
90 highlighting_style = Unicode('default',
85 highlighting_style = Unicode('default',
91 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
86 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
92 ).tag(config=True)
87 ).tag(config=True)
93
88
94
89
95 @observe('highlighting_style')
90 @observe('highlighting_style')
96 def _highlighting_style_changed(self, change):
91 def _highlighting_style_changed(self, change):
97 self._style = self._make_style_from_name(self.highlighting_style)
92 self._style = self._make_style_from_name(self.highlighting_style)
98
93
99 highlighting_style_overrides = Dict(
94 highlighting_style_overrides = Dict(
100 help="Override highlighting format for specific tokens"
95 help="Override highlighting format for specific tokens"
101 ).tag(config=True)
96 ).tag(config=True)
102
97
103 editor = Unicode(get_default_editor(),
98 editor = Unicode(get_default_editor(),
104 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
99 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
105 ).tag(config=True)
100 ).tag(config=True)
106
101
107 term_title = Bool(True,
102 term_title = Bool(True,
108 help="Automatically set the terminal title"
103 help="Automatically set the terminal title"
109 ).tag(config=True)
104 ).tag(config=True)
110
105
111 display_completions_in_columns = Bool(False,
106 display_completions_in_columns = Bool(False,
112 help="Display a multi column completion menu.",
107 help="Display a multi column completion menu.",
113 ).tag(config=True)
108 ).tag(config=True)
114
109
115 highlight_matching_brackets = Bool(True,
110 highlight_matching_brackets = Bool(True,
116 help="Highlight matching brackets .",
111 help="Highlight matching brackets .",
117 ).tag(config=True)
112 ).tag(config=True)
118
113
119 @observe('term_title')
114 @observe('term_title')
120 def init_term_title(self, change=None):
115 def init_term_title(self, change=None):
121 # Enable or disable the terminal title.
116 # Enable or disable the terminal title.
122 if self.term_title:
117 if self.term_title:
123 toggle_set_term_title(True)
118 toggle_set_term_title(True)
124 set_term_title('IPython: ' + abbrev_cwd())
119 set_term_title('IPython: ' + abbrev_cwd())
125 else:
120 else:
126 toggle_set_term_title(False)
121 toggle_set_term_title(False)
127
122
128 def get_prompt_tokens(self, cli):
123 def get_prompt_tokens(self, cli):
129 return [
124 return [
130 (Token.Prompt, 'In ['),
125 (Token.Prompt, 'In ['),
131 (Token.PromptNum, str(self.execution_count)),
126 (Token.PromptNum, str(self.execution_count)),
132 (Token.Prompt, ']: '),
127 (Token.Prompt, ']: '),
133 ]
128 ]
134
129
135 def get_continuation_tokens(self, cli, width):
130 def get_continuation_tokens(self, cli, width):
136 return [
131 return [
137 (Token.Prompt, (' ' * (width - 5)) + '...: '),
132 (Token.Prompt, (' ' * (width - 5)) + '...: '),
138 ]
133 ]
139
134
140 def init_prompt_toolkit_cli(self):
135 def init_prompt_toolkit_cli(self):
141 if self.simple_prompt:
136 if self.simple_prompt:
142 # Fall back to plain non-interactive output for tests.
137 # Fall back to plain non-interactive output for tests.
143 # This is very limited, and only accepts a single line.
138 # This is very limited, and only accepts a single line.
144 def prompt():
139 def prompt():
145 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
140 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
146 self.prompt_for_code = prompt
141 self.prompt_for_code = prompt
147 return
142 return
148
143
149 kbmanager = KeyBindingManager.for_prompt()
144 kbmanager = KeyBindingManager.for_prompt()
150 insert_mode = ViInsertMode() | EmacsInsertMode()
145 insert_mode = ViInsertMode() | EmacsInsertMode()
151 # Ctrl+J == Enter, seemingly
146 # Ctrl+J == Enter, seemingly
152 @kbmanager.registry.add_binding(Keys.ControlJ,
147 @kbmanager.registry.add_binding(Keys.ControlJ,
153 filter=(HasFocus(DEFAULT_BUFFER)
148 filter=(HasFocus(DEFAULT_BUFFER)
154 & ~HasSelection()
149 & ~HasSelection()
155 & insert_mode
150 & insert_mode
156 ))
151 ))
157 def _(event):
152 def _(event):
158 b = event.current_buffer
153 b = event.current_buffer
159 d = b.document
154 d = b.document
160 if not (d.on_last_line or d.cursor_position_row >= d.line_count
155 if not (d.on_last_line or d.cursor_position_row >= d.line_count
161 - d.empty_line_count_at_the_end()):
156 - d.empty_line_count_at_the_end()):
162 b.newline()
157 b.newline()
163 return
158 return
164
159
165 status, indent = self.input_splitter.check_complete(d.text)
160 status, indent = self.input_splitter.check_complete(d.text)
166
161
167 if (status != 'incomplete') and b.accept_action.is_returnable:
162 if (status != 'incomplete') and b.accept_action.is_returnable:
168 b.accept_action.validate_and_handle(event.cli, b)
163 b.accept_action.validate_and_handle(event.cli, b)
169 else:
164 else:
170 b.insert_text('\n' + (' ' * (indent or 0)))
165 b.insert_text('\n' + (' ' * (indent or 0)))
171
166
172 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
167 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
173 def _reset_buffer(event):
168 def _reset_buffer(event):
174 event.current_buffer.reset()
169 event.current_buffer.reset()
175
170
176 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
171 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
177 def _reset_search_buffer(event):
172 def _reset_search_buffer(event):
178 if event.current_buffer.document.text:
173 if event.current_buffer.document.text:
179 event.current_buffer.reset()
174 event.current_buffer.reset()
180 else:
175 else:
181 event.cli.push_focus(DEFAULT_BUFFER)
176 event.cli.push_focus(DEFAULT_BUFFER)
182
177
183 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
178 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
184
179
185 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
180 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
186 def _suspend_to_bg(event):
181 def _suspend_to_bg(event):
187 event.cli.suspend_to_background()
182 event.cli.suspend_to_background()
188
183
189 @Condition
184 @Condition
190 def cursor_in_leading_ws(cli):
185 def cursor_in_leading_ws(cli):
191 before = cli.application.buffer.document.current_line_before_cursor
186 before = cli.application.buffer.document.current_line_before_cursor
192 return (not before) or before.isspace()
187 return (not before) or before.isspace()
193
188
194 # Ctrl+I == Tab
189 # Ctrl+I == Tab
195 @kbmanager.registry.add_binding(Keys.ControlI,
190 @kbmanager.registry.add_binding(Keys.ControlI,
196 filter=(HasFocus(DEFAULT_BUFFER)
191 filter=(HasFocus(DEFAULT_BUFFER)
197 & ~HasSelection()
192 & ~HasSelection()
198 & insert_mode
193 & insert_mode
199 & cursor_in_leading_ws
194 & cursor_in_leading_ws
200 ))
195 ))
201 def _indent_buffer(event):
196 def _indent_buffer(event):
202 event.current_buffer.insert_text(' ' * 4)
197 event.current_buffer.insert_text(' ' * 4)
203
198
204 # Pre-populate history from IPython's history database
199 # Pre-populate history from IPython's history database
205 history = InMemoryHistory()
200 history = InMemoryHistory()
206 last_cell = u""
201 last_cell = u""
207 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
202 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
208 include_latest=True):
203 include_latest=True):
209 # Ignore blank lines and consecutive duplicates
204 # Ignore blank lines and consecutive duplicates
210 cell = cell.rstrip()
205 cell = cell.rstrip()
211 if cell and (cell != last_cell):
206 if cell and (cell != last_cell):
212 history.append(cell)
207 history.append(cell)
213
208
214 self._style = self._make_style_from_name(self.highlighting_style)
209 self._style = self._make_style_from_name(self.highlighting_style)
215 style = DynamicStyle(lambda: self._style)
210 style = DynamicStyle(lambda: self._style)
216
211
217 editing_mode = getattr(EditingMode, self.editing_mode.upper())
212 editing_mode = getattr(EditingMode, self.editing_mode.upper())
218
213
219 self._app = create_prompt_application(
214 self._app = create_prompt_application(
220 editing_mode=editing_mode,
215 editing_mode=editing_mode,
221 key_bindings_registry=kbmanager.registry,
216 key_bindings_registry=kbmanager.registry,
222 history=history,
217 history=history,
223 completer=IPythonPTCompleter(self.Completer),
218 completer=IPythonPTCompleter(self.Completer),
224 enable_history_search=True,
219 enable_history_search=True,
225 style=style,
220 style=style,
226 mouse_support=self.mouse_support,
221 mouse_support=self.mouse_support,
227 **self._layout_options()
222 **self._layout_options()
228 )
223 )
229 self._eventloop = create_eventloop(self.inputhook)
224 self._eventloop = create_eventloop(self.inputhook)
230 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
225 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
231
226
232 def _make_style_from_name(self, name):
227 def _make_style_from_name(self, name):
233 """
228 """
234 Small wrapper that make an IPython compatible style from a style name
229 Small wrapper that make an IPython compatible style from a style name
235
230
236 We need that to add style for prompt ... etc.
231 We need that to add style for prompt ... etc.
237 """
232 """
238 style_cls = get_style_by_name(name)
233 style_cls = get_style_by_name(name)
239 style_overrides = {
234 style_overrides = {
240 Token.Prompt: '#009900',
235 Token.Prompt: '#009900',
241 Token.PromptNum: '#00ff00 bold',
236 Token.PromptNum: '#00ff00 bold',
242 }
237 }
243 if name == 'default':
238 if name == 'default':
244 style_cls = get_style_by_name('default')
239 style_cls = get_style_by_name('default')
245 # The default theme needs to be visible on both a dark background
240 # The default theme needs to be visible on both a dark background
246 # and a light background, because we can't tell what the terminal
241 # and a light background, because we can't tell what the terminal
247 # looks like. These tweaks to the default theme help with that.
242 # looks like. These tweaks to the default theme help with that.
248 style_overrides.update({
243 style_overrides.update({
249 Token.Number: '#007700',
244 Token.Number: '#007700',
250 Token.Operator: 'noinherit',
245 Token.Operator: 'noinherit',
251 Token.String: '#BB6622',
246 Token.String: '#BB6622',
252 Token.Name.Function: '#2080D0',
247 Token.Name.Function: '#2080D0',
253 Token.Name.Class: 'bold #2080D0',
248 Token.Name.Class: 'bold #2080D0',
254 Token.Name.Namespace: 'bold #2080D0',
249 Token.Name.Namespace: 'bold #2080D0',
255 })
250 })
256 style_overrides.update(self.highlighting_style_overrides)
251 style_overrides.update(self.highlighting_style_overrides)
257 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
252 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
258 style_dict=style_overrides)
253 style_dict=style_overrides)
259
254
260 return style
255 return style
261
256
262 def _layout_options(self):
257 def _layout_options(self):
263 """
258 """
264 Return the current layout option for the current Terminal InteractiveShell
259 Return the current layout option for the current Terminal InteractiveShell
265 """
260 """
266 return {
261 return {
267 'lexer':IPythonPTLexer(),
262 'lexer':IPythonPTLexer(),
268 'reserve_space_for_menu':self.space_for_menu,
263 'reserve_space_for_menu':self.space_for_menu,
269 'get_prompt_tokens':self.get_prompt_tokens,
264 'get_prompt_tokens':self.get_prompt_tokens,
270 'get_continuation_tokens':self.get_continuation_tokens,
265 'get_continuation_tokens':self.get_continuation_tokens,
271 'multiline':True,
266 'multiline':True,
272 'display_completions_in_columns': self.display_completions_in_columns,
267 'display_completions_in_columns': self.display_completions_in_columns,
273
268
274 # Highlight matching brackets, but only when this setting is
269 # Highlight matching brackets, but only when this setting is
275 # enabled, and only when the DEFAULT_BUFFER has the focus.
270 # enabled, and only when the DEFAULT_BUFFER has the focus.
276 'extra_input_processors': [ConditionalProcessor(
271 'extra_input_processors': [ConditionalProcessor(
277 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
272 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
278 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
273 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
279 Condition(lambda cli: self.highlight_matching_brackets))],
274 Condition(lambda cli: self.highlight_matching_brackets))],
280 }
275 }
281
276
282 def _update_layout(self):
277 def _update_layout(self):
283 """
278 """
284 Ask for a re computation of the application layout, if for example ,
279 Ask for a re computation of the application layout, if for example ,
285 some configuration options have changed.
280 some configuration options have changed.
286 """
281 """
287 self._app.layout = create_prompt_layout(**self._layout_options())
282 self._app.layout = create_prompt_layout(**self._layout_options())
288
283
289 def prompt_for_code(self):
284 def prompt_for_code(self):
290 document = self.pt_cli.run(
285 document = self.pt_cli.run(
291 pre_run=self.pre_prompt, reset_current_buffer=True)
286 pre_run=self.pre_prompt, reset_current_buffer=True)
292 return document.text
287 return document.text
293
288
294 def init_io(self):
289 def init_io(self):
295 if sys.platform not in {'win32', 'cli'}:
290 if sys.platform not in {'win32', 'cli'}:
296 return
291 return
297
292
298 import colorama
293 import colorama
299 colorama.init()
294 colorama.init()
300
295
301 # For some reason we make these wrappers around stdout/stderr.
296 # For some reason we make these wrappers around stdout/stderr.
302 # For now, we need to reset them so all output gets coloured.
297 # For now, we need to reset them so all output gets coloured.
303 # https://github.com/ipython/ipython/issues/8669
298 # https://github.com/ipython/ipython/issues/8669
304 from IPython.utils import io
299 from IPython.utils import io
305 io.stdout = io.IOStream(sys.stdout)
300 io.stdout = io.IOStream(sys.stdout)
306 io.stderr = io.IOStream(sys.stderr)
301 io.stderr = io.IOStream(sys.stderr)
307
302
308 def init_magics(self):
303 def init_magics(self):
309 super(TerminalInteractiveShell, self).init_magics()
304 super(TerminalInteractiveShell, self).init_magics()
310 self.register_magics(TerminalMagics)
305 self.register_magics(TerminalMagics)
311
306
312 def init_alias(self):
307 def init_alias(self):
313 # The parent class defines aliases that can be safely used with any
308 # The parent class defines aliases that can be safely used with any
314 # frontend.
309 # frontend.
315 super(TerminalInteractiveShell, self).init_alias()
310 super(TerminalInteractiveShell, self).init_alias()
316
311
317 # Now define aliases that only make sense on the terminal, because they
312 # Now define aliases that only make sense on the terminal, because they
318 # need direct access to the console in a way that we can't emulate in
313 # need direct access to the console in a way that we can't emulate in
319 # GUI or web frontend
314 # GUI or web frontend
320 if os.name == 'posix':
315 if os.name == 'posix':
321 for cmd in ['clear', 'more', 'less', 'man']:
316 for cmd in ['clear', 'more', 'less', 'man']:
322 self.alias_manager.soft_define_alias(cmd, cmd)
317 self.alias_manager.soft_define_alias(cmd, cmd)
323
318
324
319
325 def __init__(self, *args, **kwargs):
320 def __init__(self, *args, **kwargs):
326 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
321 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
327 self.init_prompt_toolkit_cli()
322 self.init_prompt_toolkit_cli()
328 self.init_term_title()
323 self.init_term_title()
329 self.keep_running = True
324 self.keep_running = True
330
325
331 self.debugger_history = InMemoryHistory()
326 self.debugger_history = InMemoryHistory()
332
327
333 def ask_exit(self):
328 def ask_exit(self):
334 self.keep_running = False
329 self.keep_running = False
335
330
336 rl_next_input = None
331 rl_next_input = None
337
332
338 def pre_prompt(self):
333 def pre_prompt(self):
339 if self.rl_next_input:
334 if self.rl_next_input:
340 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
335 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
341 self.rl_next_input = None
336 self.rl_next_input = None
342
337
343 def interact(self):
338 def interact(self):
344 while self.keep_running:
339 while self.keep_running:
345 print(self.separate_in, end='')
340 print(self.separate_in, end='')
346
341
347 try:
342 try:
348 code = self.prompt_for_code()
343 code = self.prompt_for_code()
349 except EOFError:
344 except EOFError:
350 if (not self.confirm_exit) \
345 if (not self.confirm_exit) \
351 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
346 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
352 self.ask_exit()
347 self.ask_exit()
353
348
354 else:
349 else:
355 if code:
350 if code:
356 self.run_cell(code, store_history=True)
351 self.run_cell(code, store_history=True)
357 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
352 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
358 self.edit_syntax_error()
353 self.edit_syntax_error()
359
354
360 def mainloop(self):
355 def mainloop(self):
361 # An extra layer of protection in case someone mashing Ctrl-C breaks
356 # An extra layer of protection in case someone mashing Ctrl-C breaks
362 # out of our internal code.
357 # out of our internal code.
363 while True:
358 while True:
364 try:
359 try:
365 self.interact()
360 self.interact()
366 break
361 break
367 except KeyboardInterrupt:
362 except KeyboardInterrupt:
368 print("\nKeyboardInterrupt escaped interact()\n")
363 print("\nKeyboardInterrupt escaped interact()\n")
369
364
370 if hasattr(self, '_eventloop'):
365 if hasattr(self, '_eventloop'):
371 self._eventloop.close()
366 self._eventloop.close()
372
367
373 _inputhook = None
368 _inputhook = None
374 def inputhook(self, context):
369 def inputhook(self, context):
375 if self._inputhook is not None:
370 if self._inputhook is not None:
376 self._inputhook(context)
371 self._inputhook(context)
377
372
378 def enable_gui(self, gui=None):
373 def enable_gui(self, gui=None):
379 if gui:
374 if gui:
380 self._inputhook = get_inputhook_func(gui)
375 self._inputhook = get_inputhook_func(gui)
381 else:
376 else:
382 self._inputhook = None
377 self._inputhook = None
383
378
384 # Methods to support auto-editing of SyntaxErrors:
379 # Methods to support auto-editing of SyntaxErrors:
385
380
386 def edit_syntax_error(self):
381 def edit_syntax_error(self):
387 """The bottom half of the syntax error handler called in the main loop.
382 """The bottom half of the syntax error handler called in the main loop.
388
383
389 Loop until syntax error is fixed or user cancels.
384 Loop until syntax error is fixed or user cancels.
390 """
385 """
391
386
392 while self.SyntaxTB.last_syntax_error:
387 while self.SyntaxTB.last_syntax_error:
393 # copy and clear last_syntax_error
388 # copy and clear last_syntax_error
394 err = self.SyntaxTB.clear_err_state()
389 err = self.SyntaxTB.clear_err_state()
395 if not self._should_recompile(err):
390 if not self._should_recompile(err):
396 return
391 return
397 try:
392 try:
398 # may set last_syntax_error again if a SyntaxError is raised
393 # may set last_syntax_error again if a SyntaxError is raised
399 self.safe_execfile(err.filename, self.user_ns)
394 self.safe_execfile(err.filename, self.user_ns)
400 except:
395 except:
401 self.showtraceback()
396 self.showtraceback()
402 else:
397 else:
403 try:
398 try:
404 with open(err.filename) as f:
399 with open(err.filename) as f:
405 # This should be inside a display_trap block and I
400 # This should be inside a display_trap block and I
406 # think it is.
401 # think it is.
407 sys.displayhook(f.read())
402 sys.displayhook(f.read())
408 except:
403 except:
409 self.showtraceback()
404 self.showtraceback()
410
405
411 def _should_recompile(self, e):
406 def _should_recompile(self, e):
412 """Utility routine for edit_syntax_error"""
407 """Utility routine for edit_syntax_error"""
413
408
414 if e.filename in ('<ipython console>', '<input>', '<string>',
409 if e.filename in ('<ipython console>', '<input>', '<string>',
415 '<console>', '<BackgroundJob compilation>',
410 '<console>', '<BackgroundJob compilation>',
416 None):
411 None):
417 return False
412 return False
418 try:
413 try:
419 if (self.autoedit_syntax and
414 if (self.autoedit_syntax and
420 not self.ask_yes_no(
415 not self.ask_yes_no(
421 'Return to editor to correct syntax error? '
416 'Return to editor to correct syntax error? '
422 '[Y/n] ', 'y')):
417 '[Y/n] ', 'y')):
423 return False
418 return False
424 except EOFError:
419 except EOFError:
425 return False
420 return False
426
421
427 def int0(x):
422 def int0(x):
428 try:
423 try:
429 return int(x)
424 return int(x)
430 except TypeError:
425 except TypeError:
431 return 0
426 return 0
432
427
433 # always pass integer line and offset values to editor hook
428 # always pass integer line and offset values to editor hook
434 try:
429 try:
435 self.hooks.fix_error_editor(e.filename,
430 self.hooks.fix_error_editor(e.filename,
436 int0(e.lineno), int0(e.offset),
431 int0(e.lineno), int0(e.offset),
437 e.msg)
432 e.msg)
438 except TryNext:
433 except TryNext:
439 warn('Could not open editor')
434 warn('Could not open editor')
440 return False
435 return False
441 return True
436 return True
442
437
443 # Run !system commands directly, not through pipes, so terminal programs
438 # Run !system commands directly, not through pipes, so terminal programs
444 # work correctly.
439 # work correctly.
445 system = InteractiveShell.system_raw
440 system = InteractiveShell.system_raw
446
441
447
442
448 if __name__ == '__main__':
443 if __name__ == '__main__':
449 TerminalInteractiveShell.instance().interact()
444 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now