##// END OF EJS Templates
Remove the readline shell machinery...
Thomas Kluyver -
Show More
@@ -0,0 +1,207 b''
1 """Extra magics for terminal use."""
2
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
5
6 from __future__ import print_function
7
8 from logging import error
9 import os
10 import sys
11
12 from IPython.core.error import TryNext, UsageError
13 from IPython.core.inputsplitter import IPythonInputSplitter
14 from IPython.core.magic import Magics, magics_class, line_magic
15 from IPython.lib.clipboard import ClipboardEmpty
16 from IPython.utils.text import SList, strip_email_quotes
17 from IPython.utils import py3compat
18
19 def get_pasted_lines(sentinel, l_input=py3compat.input, quiet=False):
20 """ Yield pasted lines until the user enters the given sentinel value.
21 """
22 if not quiet:
23 print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
24 % sentinel)
25 prompt = ":"
26 else:
27 prompt = ""
28 while True:
29 try:
30 l = py3compat.str_to_unicode(l_input(prompt))
31 if l == sentinel:
32 return
33 else:
34 yield l
35 except EOFError:
36 print('<EOF>')
37 return
38
39
40 @magics_class
41 class TerminalMagics(Magics):
42 def __init__(self, shell):
43 super(TerminalMagics, self).__init__(shell)
44 self.input_splitter = IPythonInputSplitter()
45
46 def store_or_execute(self, block, name):
47 """ Execute a block, or store it in a variable, per the user's request.
48 """
49 if name:
50 # If storing it for further editing
51 self.shell.user_ns[name] = SList(block.splitlines())
52 print("Block assigned to '%s'" % name)
53 else:
54 b = self.preclean_input(block)
55 self.shell.user_ns['pasted_block'] = b
56 self.shell.using_paste_magics = True
57 try:
58 self.shell.run_cell(b)
59 finally:
60 self.shell.using_paste_magics = False
61
62 def preclean_input(self, block):
63 lines = block.splitlines()
64 while lines and not lines[0].strip():
65 lines = lines[1:]
66 return strip_email_quotes('\n'.join(lines))
67
68 def rerun_pasted(self, name='pasted_block'):
69 """ Rerun a previously pasted command.
70 """
71 b = self.shell.user_ns.get(name)
72
73 # Sanity checks
74 if b is None:
75 raise UsageError('No previous pasted block available')
76 if not isinstance(b, py3compat.string_types):
77 raise UsageError(
78 "Variable 'pasted_block' is not a string, can't execute")
79
80 print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
81 self.shell.run_cell(b)
82
83 @line_magic
84 def autoindent(self, parameter_s = ''):
85 """Toggle autoindent on/off (if available)."""
86
87 self.shell.set_autoindent()
88 print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
89
90 @line_magic
91 def cpaste(self, parameter_s=''):
92 """Paste & execute a pre-formatted code block from clipboard.
93
94 You must terminate the block with '--' (two minus-signs) or Ctrl-D
95 alone on the line. You can also provide your own sentinel with '%paste
96 -s %%' ('%%' is the new sentinel for this operation).
97
98 The block is dedented prior to execution to enable execution of method
99 definitions. '>' and '+' characters at the beginning of a line are
100 ignored, to allow pasting directly from e-mails, diff files and
101 doctests (the '...' continuation prompt is also stripped). The
102 executed block is also assigned to variable named 'pasted_block' for
103 later editing with '%edit pasted_block'.
104
105 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
106 This assigns the pasted block to variable 'foo' as string, without
107 dedenting or executing it (preceding >>> and + is still stripped)
108
109 '%cpaste -r' re-executes the block previously entered by cpaste.
110 '%cpaste -q' suppresses any additional output messages.
111
112 Do not be alarmed by garbled output on Windows (it's a readline bug).
113 Just press enter and type -- (and press enter again) and the block
114 will be what was just pasted.
115
116 IPython statements (magics, shell escapes) are not supported (yet).
117
118 See also
119 --------
120 paste: automatically pull code from clipboard.
121
122 Examples
123 --------
124 ::
125
126 In [8]: %cpaste
127 Pasting code; enter '--' alone on the line to stop.
128 :>>> a = ["world!", "Hello"]
129 :>>> print " ".join(sorted(a))
130 :--
131 Hello world!
132 """
133 opts, name = self.parse_options(parameter_s, 'rqs:', mode='string')
134 if 'r' in opts:
135 self.rerun_pasted()
136 return
137
138 quiet = ('q' in opts)
139
140 sentinel = opts.get('s', u'--')
141 block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet))
142 self.store_or_execute(block, name)
143
144 @line_magic
145 def paste(self, parameter_s=''):
146 """Paste & execute a pre-formatted code block from clipboard.
147
148 The text is pulled directly from the clipboard without user
149 intervention and printed back on the screen before execution (unless
150 the -q flag is given to force quiet mode).
151
152 The block is dedented prior to execution to enable execution of method
153 definitions. '>' and '+' characters at the beginning of a line are
154 ignored, to allow pasting directly from e-mails, diff files and
155 doctests (the '...' continuation prompt is also stripped). The
156 executed block is also assigned to variable named 'pasted_block' for
157 later editing with '%edit pasted_block'.
158
159 You can also pass a variable name as an argument, e.g. '%paste foo'.
160 This assigns the pasted block to variable 'foo' as string, without
161 executing it (preceding >>> and + is still stripped).
162
163 Options:
164
165 -r: re-executes the block previously entered by cpaste.
166
167 -q: quiet mode: do not echo the pasted text back to the terminal.
168
169 IPython statements (magics, shell escapes) are not supported (yet).
170
171 See also
172 --------
173 cpaste: manually paste code into terminal until you mark its end.
174 """
175 opts, name = self.parse_options(parameter_s, 'rq', mode='string')
176 if 'r' in opts:
177 self.rerun_pasted()
178 return
179 try:
180 block = self.shell.hooks.clipboard_get()
181 except TryNext as clipboard_exc:
182 message = getattr(clipboard_exc, 'args')
183 if message:
184 error(message[0])
185 else:
186 error('Could not get text from the clipboard.')
187 return
188 except ClipboardEmpty:
189 raise UsageError("The clipboard appears to be empty")
190
191 # By default, echo back to terminal unless quiet mode is requested
192 if 'q' not in opts:
193 write = self.shell.write
194 write(self.shell.pycolorize(block))
195 if not block.endswith('\n'):
196 write('\n')
197 write("## -- End pasted text --\n")
198
199 self.store_or_execute(block, name)
200
201 # Class-level: add a '%cls' magic only on Windows
202 if sys.platform == 'win32':
203 @line_magic
204 def cls(self, s):
205 """Clear screen.
206 """
207 os.system("cls")
This diff has been collapsed as it changes many lines, (810 lines changed) Show them Hide them
@@ -1,810 +1,18 b''
1 1 # -*- coding: utf-8 -*-
2 """Subclass of InteractiveShell for terminal based frontends."""
2 """DEPRECATED: old import location of TerminalInteractiveShell"""
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 from __future__ import print_function
8
9 import bdb
10 import os
11 import sys
12
13 from IPython.core.error import TryNext, UsageError
14 from IPython.core.usage import interactive_usage
15 from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC
16 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
17 from IPython.core.magic import Magics, magics_class, line_magic
18 from IPython.lib.clipboard import ClipboardEmpty
19 from IPython.utils.contexts import NoOpContext
20 from IPython.utils.decorators import undoc
21 from IPython.utils.encoding import get_stream_enc
22 from IPython.utils import py3compat
23 from IPython.utils.terminal import toggle_set_term_title, set_term_title
24 from IPython.utils.process import abbrev_cwd
25 7 from warnings import warn
26 from logging import error
27 from IPython.utils.text import num_ini_spaces, SList, strip_email_quotes
28 from traitlets import Integer, CBool, Unicode
29
30
31 def get_default_editor():
32 try:
33 ed = os.environ['EDITOR']
34 if not py3compat.PY3:
35 ed = ed.decode()
36 return ed
37 except KeyError:
38 pass
39 except UnicodeError:
40 warn("$EDITOR environment variable is not pure ASCII. Using platform "
41 "default editor.")
42 8
43 if os.name == 'posix':
44 return 'vi' # the only one guaranteed to be there!
45 else:
46 return 'notepad' # same in Windows!
47
48 def get_pasted_lines(sentinel, l_input=py3compat.input, quiet=False):
49 """ Yield pasted lines until the user enters the given sentinel value.
50 """
51 if not quiet:
52 print("Pasting code; enter '%s' alone on the line to stop or use Ctrl-D." \
53 % sentinel)
54 prompt = ":"
55 else:
56 prompt = ""
57 while True:
58 try:
59 l = py3compat.str_to_unicode(l_input(prompt))
60 if l == sentinel:
61 return
62 else:
63 yield l
64 except EOFError:
65 print('<EOF>')
66 return
9 from IPython.utils.decorators import undoc
10 from .ptshell import TerminalInteractiveShell as PromptToolkitShell
67 11
68 12 @undoc
69 def no_op(*a, **kw): pass
70
71
72 class ReadlineNoRecord(object):
73 """Context manager to execute some code, then reload readline history
74 so that interactive input to the code doesn't appear when pressing up."""
75 def __init__(self, shell):
76 self.shell = shell
77 self._nested_level = 0
78
79 def __enter__(self):
80 if self._nested_level == 0:
81 try:
82 self.orig_length = self.current_length()
83 self.readline_tail = self.get_readline_tail()
84 except (AttributeError, IndexError): # Can fail with pyreadline
85 self.orig_length, self.readline_tail = 999999, []
86 self._nested_level += 1
87
88 def __exit__(self, type, value, traceback):
89 self._nested_level -= 1
90 if self._nested_level == 0:
91 # Try clipping the end if it's got longer
92 try:
93 e = self.current_length() - self.orig_length
94 if e > 0:
95 for _ in range(e):
96 self.shell.readline.remove_history_item(self.orig_length)
97
98 # If it still doesn't match, just reload readline history.
99 if self.current_length() != self.orig_length \
100 or self.get_readline_tail() != self.readline_tail:
101 self.shell.refill_readline_hist()
102 except (AttributeError, IndexError):
103 pass
104 # Returning False will cause exceptions to propagate
105 return False
106
107 def current_length(self):
108 return self.shell.readline.get_current_history_length()
109
110 def get_readline_tail(self, n=10):
111 """Get the last n items in readline history."""
112 end = self.shell.readline.get_current_history_length() + 1
113 start = max(end-n, 1)
114 ghi = self.shell.readline.get_history_item
115 return [ghi(x) for x in range(start, end)]
116
117
118 @magics_class
119 class TerminalMagics(Magics):
120 def __init__(self, shell):
121 super(TerminalMagics, self).__init__(shell)
122 self.input_splitter = IPythonInputSplitter()
123
124 def store_or_execute(self, block, name):
125 """ Execute a block, or store it in a variable, per the user's request.
126 """
127 if name:
128 # If storing it for further editing
129 self.shell.user_ns[name] = SList(block.splitlines())
130 print("Block assigned to '%s'" % name)
131 else:
132 b = self.preclean_input(block)
133 self.shell.user_ns['pasted_block'] = b
134 self.shell.using_paste_magics = True
135 try:
136 self.shell.run_cell(b)
137 finally:
138 self.shell.using_paste_magics = False
139
140 def preclean_input(self, block):
141 lines = block.splitlines()
142 while lines and not lines[0].strip():
143 lines = lines[1:]
144 return strip_email_quotes('\n'.join(lines))
145
146 def rerun_pasted(self, name='pasted_block'):
147 """ Rerun a previously pasted command.
148 """
149 b = self.shell.user_ns.get(name)
150
151 # Sanity checks
152 if b is None:
153 raise UsageError('No previous pasted block available')
154 if not isinstance(b, py3compat.string_types):
155 raise UsageError(
156 "Variable 'pasted_block' is not a string, can't execute")
157
158 print("Re-executing '%s...' (%d chars)"% (b.split('\n',1)[0], len(b)))
159 self.shell.run_cell(b)
160
161 @line_magic
162 def autoindent(self, parameter_s = ''):
163 """Toggle autoindent on/off (if available)."""
164
165 self.shell.set_autoindent()
166 print("Automatic indentation is:",['OFF','ON'][self.shell.autoindent])
167
168 @line_magic
169 def cpaste(self, parameter_s=''):
170 """Paste & execute a pre-formatted code block from clipboard.
171
172 You must terminate the block with '--' (two minus-signs) or Ctrl-D
173 alone on the line. You can also provide your own sentinel with '%paste
174 -s %%' ('%%' is the new sentinel for this operation).
175
176 The block is dedented prior to execution to enable execution of method
177 definitions. '>' and '+' characters at the beginning of a line are
178 ignored, to allow pasting directly from e-mails, diff files and
179 doctests (the '...' continuation prompt is also stripped). The
180 executed block is also assigned to variable named 'pasted_block' for
181 later editing with '%edit pasted_block'.
182
183 You can also pass a variable name as an argument, e.g. '%cpaste foo'.
184 This assigns the pasted block to variable 'foo' as string, without
185 dedenting or executing it (preceding >>> and + is still stripped)
186
187 '%cpaste -r' re-executes the block previously entered by cpaste.
188 '%cpaste -q' suppresses any additional output messages.
189
190 Do not be alarmed by garbled output on Windows (it's a readline bug).
191 Just press enter and type -- (and press enter again) and the block
192 will be what was just pasted.
193
194 IPython statements (magics, shell escapes) are not supported (yet).
195
196 See also
197 --------
198 paste: automatically pull code from clipboard.
199
200 Examples
201 --------
202 ::
203
204 In [8]: %cpaste
205 Pasting code; enter '--' alone on the line to stop.
206 :>>> a = ["world!", "Hello"]
207 :>>> print " ".join(sorted(a))
208 :--
209 Hello world!
210 """
211 opts, name = self.parse_options(parameter_s, 'rqs:', mode='string')
212 if 'r' in opts:
213 self.rerun_pasted()
214 return
215
216 quiet = ('q' in opts)
217
218 sentinel = opts.get('s', u'--')
219 block = '\n'.join(get_pasted_lines(sentinel, quiet=quiet))
220 self.store_or_execute(block, name)
221
222 @line_magic
223 def paste(self, parameter_s=''):
224 """Paste & execute a pre-formatted code block from clipboard.
225
226 The text is pulled directly from the clipboard without user
227 intervention and printed back on the screen before execution (unless
228 the -q flag is given to force quiet mode).
229
230 The block is dedented prior to execution to enable execution of method
231 definitions. '>' and '+' characters at the beginning of a line are
232 ignored, to allow pasting directly from e-mails, diff files and
233 doctests (the '...' continuation prompt is also stripped). The
234 executed block is also assigned to variable named 'pasted_block' for
235 later editing with '%edit pasted_block'.
236
237 You can also pass a variable name as an argument, e.g. '%paste foo'.
238 This assigns the pasted block to variable 'foo' as string, without
239 executing it (preceding >>> and + is still stripped).
240
241 Options:
242
243 -r: re-executes the block previously entered by cpaste.
244
245 -q: quiet mode: do not echo the pasted text back to the terminal.
246
247 IPython statements (magics, shell escapes) are not supported (yet).
248
249 See also
250 --------
251 cpaste: manually paste code into terminal until you mark its end.
252 """
253 opts, name = self.parse_options(parameter_s, 'rq', mode='string')
254 if 'r' in opts:
255 self.rerun_pasted()
256 return
257 try:
258 block = self.shell.hooks.clipboard_get()
259 except TryNext as clipboard_exc:
260 message = getattr(clipboard_exc, 'args')
261 if message:
262 error(message[0])
263 else:
264 error('Could not get text from the clipboard.')
265 return
266 except ClipboardEmpty:
267 raise UsageError("The clipboard appears to be empty")
268
269 # By default, echo back to terminal unless quiet mode is requested
270 if 'q' not in opts:
271 write = self.shell.write
272 write(self.shell.pycolorize(block))
273 if not block.endswith('\n'):
274 write('\n')
275 write("## -- End pasted text --\n")
276
277 self.store_or_execute(block, name)
278
279 # Class-level: add a '%cls' magic only on Windows
280 if sys.platform == 'win32':
281 @line_magic
282 def cls(self, s):
283 """Clear screen.
284 """
285 os.system("cls")
286
287
288 class TerminalInteractiveShell(InteractiveShell):
289
290 autoedit_syntax = CBool(False, config=True,
291 help="auto editing of files with syntax errors.")
292 confirm_exit = CBool(True, config=True,
293 help="""
294 Set to confirm when you try to exit IPython with an EOF (Control-D
295 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
296 you can force a direct exit without any confirmation.""",
297 )
298 # This display_banner only controls whether or not self.show_banner()
299 # is called when mainloop/interact are called. The default is False
300 # because for the terminal based application, the banner behavior
301 # is controlled by the application.
302 display_banner = CBool(False) # This isn't configurable!
303 embedded = CBool(False)
304 embedded_active = CBool(False)
305 editor = Unicode(get_default_editor(), config=True,
306 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
307 )
308 pager = Unicode('less', config=True,
309 help="The shell program to be used for paging.")
310
311 screen_length = Integer(0, config=True,
312 help=
313 """Number of lines of your screen, used to control printing of very
314 long strings. Strings longer than this number of lines will be sent
315 through a pager instead of directly printed. The default value for
316 this is 0, which means IPython will auto-detect your screen size every
317 time it needs to print certain potentially long strings (this doesn't
318 change the behavior of the 'print' keyword, it's only triggered
319 internally). If for some reason this isn't working well (it needs
320 curses support), specify it yourself. Otherwise don't change the
321 default.""",
322 )
323 term_title = CBool(False, config=True,
324 help="Enable auto setting the terminal title."
325 )
326 usage = Unicode(interactive_usage)
327
328 # This `using_paste_magics` is used to detect whether the code is being
329 # executed via paste magics functions
330 using_paste_magics = CBool(False)
331
332 # In the terminal, GUI control is done via PyOS_InputHook
333 @staticmethod
334 def enable_gui(gui=None, app=None):
335 """Switch amongst GUI input hooks by name.
336 """
337 # Deferred import
338 from IPython.lib.inputhook import enable_gui as real_enable_gui
339 try:
340 return real_enable_gui(gui, app)
341 except ValueError as e:
342 raise UsageError("%s" % e)
343
344 system = InteractiveShell.system_raw
345
346 #-------------------------------------------------------------------------
347 # Overrides of init stages
348 #-------------------------------------------------------------------------
349
350 def init_display_formatter(self):
351 super(TerminalInteractiveShell, self).init_display_formatter()
352 # terminal only supports plaintext
353 self.display_formatter.active_types = ['text/plain']
354
355 #-------------------------------------------------------------------------
356 # Things related to readline
357 #-------------------------------------------------------------------------
358
359 def init_readline(self):
360 """Command history completion/saving/reloading."""
361
362 if self.readline_use:
363 import IPython.utils.rlineimpl as readline
364
365 self.rl_next_input = None
366 self.rl_do_indent = False
367
368 if not self.readline_use or not readline.have_readline:
369 self.readline = None
370 # Set a number of methods that depend on readline to be no-op
371 self.readline_no_record = NoOpContext()
372 self.set_readline_completer = no_op
373 self.set_custom_completer = no_op
374 if self.readline_use:
375 warn('Readline services not available or not loaded.')
376 else:
377 self.has_readline = True
378 self.readline = readline
379 sys.modules['readline'] = readline
380
381 # Platform-specific configuration
382 if os.name == 'nt':
383 # FIXME - check with Frederick to see if we can harmonize
384 # naming conventions with pyreadline to avoid this
385 # platform-dependent check
386 self.readline_startup_hook = readline.set_pre_input_hook
387 else:
388 self.readline_startup_hook = readline.set_startup_hook
389
390 # Readline config order:
391 # - IPython config (default value)
392 # - custom inputrc
393 # - IPython config (user customized)
394
395 # load IPython config before inputrc if default
396 # skip if libedit because parse_and_bind syntax is different
397 if not self._custom_readline_config and not readline.uses_libedit:
398 for rlcommand in self.readline_parse_and_bind:
399 readline.parse_and_bind(rlcommand)
400
401 # Load user's initrc file (readline config)
402 # Or if libedit is used, load editrc.
403 inputrc_name = os.environ.get('INPUTRC')
404 if inputrc_name is None:
405 inputrc_name = '.inputrc'
406 if readline.uses_libedit:
407 inputrc_name = '.editrc'
408 inputrc_name = os.path.join(self.home_dir, inputrc_name)
409 if os.path.isfile(inputrc_name):
410 try:
411 readline.read_init_file(inputrc_name)
412 except:
413 warn('Problems reading readline initialization file <%s>'
414 % inputrc_name)
415
416 # load IPython config after inputrc if user has customized
417 if self._custom_readline_config:
418 for rlcommand in self.readline_parse_and_bind:
419 readline.parse_and_bind(rlcommand)
420
421 # Remove some chars from the delimiters list. If we encounter
422 # unicode chars, discard them.
423 delims = readline.get_completer_delims()
424 if not py3compat.PY3:
425 delims = delims.encode("ascii", "ignore")
426 for d in self.readline_remove_delims:
427 delims = delims.replace(d, "")
428 delims = delims.replace(ESC_MAGIC, '')
429 readline.set_completer_delims(delims)
430 # Store these so we can restore them if something like rpy2 modifies
431 # them.
432 self.readline_delims = delims
433 # otherwise we end up with a monster history after a while:
434 readline.set_history_length(self.history_length)
435
436 self.refill_readline_hist()
437 self.readline_no_record = ReadlineNoRecord(self)
438
439 # Configure auto-indent for all platforms
440 self.set_autoindent(self.autoindent)
441
442 def init_completer(self):
443 super(TerminalInteractiveShell, self).init_completer()
444
445 # Only configure readline if we truly are using readline.
446 if self.has_readline:
447 self.set_readline_completer()
448
449 def set_readline_completer(self):
450 """Reset readline's completer to be our own."""
451 self.readline.set_completer(self.Completer.rlcomplete)
452
453
454 def pre_readline(self):
455 """readline hook to be used at the start of each line.
456
457 It handles auto-indent and text from set_next_input."""
458
459 if self.rl_do_indent:
460 self.readline.insert_text(self._indent_current_str())
461 if self.rl_next_input is not None:
462 self.readline.insert_text(self.rl_next_input)
463 self.rl_next_input = None
464
465 def refill_readline_hist(self):
466 # Load the last 1000 lines from history
467 self.readline.clear_history()
468 stdin_encoding = sys.stdin.encoding or "utf-8"
469 last_cell = u""
470 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
471 include_latest=True):
472 # Ignore blank lines and consecutive duplicates
473 cell = cell.rstrip()
474 if cell and (cell != last_cell):
475 try:
476 if self.multiline_history:
477 self.readline.add_history(py3compat.unicode_to_str(cell,
478 stdin_encoding))
479 else:
480 for line in cell.splitlines():
481 self.readline.add_history(py3compat.unicode_to_str(line,
482 stdin_encoding))
483 last_cell = cell
484
485 except (TypeError, ValueError) as e:
486 # The history DB can get corrupted so it returns strings
487 # containing null bytes, which readline objects to.
488 warn(("Failed to add string to readline history.\n"
489 "Error: {}\n"
490 "Cell: {!r}").format(e, cell))
491
492 #-------------------------------------------------------------------------
493 # Things related to the terminal
494 #-------------------------------------------------------------------------
495
496 @property
497 def usable_screen_length(self):
498 if self.screen_length == 0:
499 return 0
500 else:
501 num_lines_bot = self.separate_in.count('\n')+1
502 return self.screen_length - num_lines_bot
503
504 def _term_title_changed(self, name, new_value):
505 self.init_term_title()
506
507 def init_term_title(self):
508 # Enable or disable the terminal title.
509 if self.term_title:
510 toggle_set_term_title(True)
511 set_term_title('IPython: ' + abbrev_cwd())
512 else:
513 toggle_set_term_title(False)
514
515 #-------------------------------------------------------------------------
516 # Things related to aliases
517 #-------------------------------------------------------------------------
518
519 def init_alias(self):
520 # The parent class defines aliases that can be safely used with any
521 # frontend.
522 super(TerminalInteractiveShell, self).init_alias()
523
524 # Now define aliases that only make sense on the terminal, because they
525 # need direct access to the console in a way that we can't emulate in
526 # GUI or web frontend
527 if os.name == 'posix':
528 aliases = [('clear', 'clear'), ('more', 'more'), ('less', 'less'),
529 ('man', 'man')]
530 else :
531 aliases = []
532
533 for name, cmd in aliases:
534 self.alias_manager.soft_define_alias(name, cmd)
535
536 #-------------------------------------------------------------------------
537 # Mainloop and code execution logic
538 #-------------------------------------------------------------------------
539
540 def mainloop(self, display_banner=None):
541 """Start the mainloop.
542
543 If an optional banner argument is given, it will override the
544 internally created default banner.
545 """
546
547 with self.builtin_trap, self.display_trap:
548
549 while 1:
550 try:
551 self.interact(display_banner=display_banner)
552 #self.interact_with_readline()
553 # XXX for testing of a readline-decoupled repl loop, call
554 # interact_with_readline above
555 break
556 except KeyboardInterrupt:
557 # this should not be necessary, but KeyboardInterrupt
558 # handling seems rather unpredictable...
559 self.write("\nKeyboardInterrupt in interact()\n")
560
561 def _replace_rlhist_multiline(self, source_raw, hlen_before_cell):
562 """Store multiple lines as a single entry in history"""
563
564 # do nothing without readline or disabled multiline
565 if not self.has_readline or not self.multiline_history:
566 return hlen_before_cell
567
568 # windows rl has no remove_history_item
569 if not hasattr(self.readline, "remove_history_item"):
570 return hlen_before_cell
571
572 # skip empty cells
573 if not source_raw.rstrip():
574 return hlen_before_cell
575
576 # nothing changed do nothing, e.g. when rl removes consecutive dups
577 hlen = self.readline.get_current_history_length()
578 if hlen == hlen_before_cell:
579 return hlen_before_cell
580
581 for i in range(hlen - hlen_before_cell):
582 self.readline.remove_history_item(hlen - i - 1)
583 stdin_encoding = get_stream_enc(sys.stdin, 'utf-8')
584 self.readline.add_history(py3compat.unicode_to_str(source_raw.rstrip(),
585 stdin_encoding))
586 return self.readline.get_current_history_length()
587
588 def interact(self, display_banner=None):
589 """Closely emulate the interactive Python console."""
590
591 # batch run -> do not interact
592 if self.exit_now:
593 return
594
595 if display_banner is None:
596 display_banner = self.display_banner
597
598 if isinstance(display_banner, py3compat.string_types):
599 self.show_banner(display_banner)
600 elif display_banner:
601 self.show_banner()
602
603 more = False
604
605 if self.has_readline:
606 self.readline_startup_hook(self.pre_readline)
607 hlen_b4_cell = self.readline.get_current_history_length()
608 else:
609 hlen_b4_cell = 0
610 # exit_now is set by a call to %Exit or %Quit, through the
611 # ask_exit callback.
612
613 while not self.exit_now:
614 self.hooks.pre_prompt_hook()
615 if more:
616 try:
617 prompt = ' ...: '
618 except:
619 self.showtraceback()
620 if self.autoindent:
621 self.rl_do_indent = True
622
623 else:
624 try:
625 prompt = self.separate_in + 'In [{}]: '.format(self.execution_count)
626 except:
627 self.showtraceback()
628 try:
629 line = self.raw_input(prompt)
630 if self.exit_now:
631 # quick exit on sys.std[in|out] close
632 break
633 if self.autoindent:
634 self.rl_do_indent = False
635
636 except KeyboardInterrupt:
637 #double-guard against keyboardinterrupts during kbdint handling
638 try:
639 self.write('\n' + self.get_exception_only())
640 source_raw = self.input_splitter.raw_reset()
641 hlen_b4_cell = \
642 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
643 more = False
644 except KeyboardInterrupt:
645 pass
646 except EOFError:
647 if self.autoindent:
648 self.rl_do_indent = False
649 if self.has_readline:
650 self.readline_startup_hook(None)
651 self.write('\n')
652 self.exit()
653 except bdb.BdbQuit:
654 warn('The Python debugger has exited with a BdbQuit exception.\n'
655 'Because of how pdb handles the stack, it is impossible\n'
656 'for IPython to properly format this particular exception.\n'
657 'IPython will resume normal operation.')
658 except:
659 # exceptions here are VERY RARE, but they can be triggered
660 # asynchronously by signal handlers, for example.
661 self.showtraceback()
662 else:
663 try:
664 self.input_splitter.push(line)
665 more = self.input_splitter.push_accepts_more()
666 except SyntaxError:
667 # Run the code directly - run_cell takes care of displaying
668 # the exception.
669 more = False
670 if (self.SyntaxTB.last_syntax_error and
671 self.autoedit_syntax):
672 self.edit_syntax_error()
673 if not more:
674 source_raw = self.input_splitter.raw_reset()
675 self.run_cell(source_raw, store_history=True)
676 hlen_b4_cell = \
677 self._replace_rlhist_multiline(source_raw, hlen_b4_cell)
678
679 # Turn off the exit flag, so the mainloop can be restarted if desired
680 self.exit_now = False
681
682 def raw_input(self, prompt=''):
683 """Write a prompt and read a line.
684
685 The returned line does not include the trailing newline.
686 When the user enters the EOF key sequence, EOFError is raised.
687
688 Parameters
689 ----------
690
691 prompt : str, optional
692 A string to be printed to prompt the user.
693 """
694 # raw_input expects str, but we pass it unicode sometimes
695 prompt = py3compat.cast_bytes_py2(prompt)
696
697 try:
698 line = py3compat.cast_unicode_py2(self.raw_input_original(prompt))
699 except ValueError:
700 warn("\n********\nYou or a %run:ed script called sys.stdin.close()"
701 " or sys.stdout.close()!\nExiting IPython!\n")
702 self.ask_exit()
703 return ""
704
705 # Try to be reasonably smart about not re-indenting pasted input more
706 # than necessary. We do this by trimming out the auto-indent initial
707 # spaces, if the user's actual input started itself with whitespace.
708 if self.autoindent:
709 if num_ini_spaces(line) > self.indent_current_nsp:
710 line = line[self.indent_current_nsp:]
711 self.indent_current_nsp = 0
712
713 return line
714
715 #-------------------------------------------------------------------------
716 # Methods to support auto-editing of SyntaxErrors.
717 #-------------------------------------------------------------------------
718
719 def edit_syntax_error(self):
720 """The bottom half of the syntax error handler called in the main loop.
721
722 Loop until syntax error is fixed or user cancels.
723 """
724
725 while self.SyntaxTB.last_syntax_error:
726 # copy and clear last_syntax_error
727 err = self.SyntaxTB.clear_err_state()
728 if not self._should_recompile(err):
729 return
730 try:
731 # may set last_syntax_error again if a SyntaxError is raised
732 self.safe_execfile(err.filename,self.user_ns)
733 except:
734 self.showtraceback()
735 else:
736 try:
737 f = open(err.filename)
738 try:
739 # This should be inside a display_trap block and I
740 # think it is.
741 sys.displayhook(f.read())
742 finally:
743 f.close()
744 except:
745 self.showtraceback()
746
747 def _should_recompile(self,e):
748 """Utility routine for edit_syntax_error"""
749
750 if e.filename in ('<ipython console>','<input>','<string>',
751 '<console>','<BackgroundJob compilation>',
752 None):
753
754 return False
755 try:
756 if (self.autoedit_syntax and
757 not self.ask_yes_no('Return to editor to correct syntax error? '
758 '[Y/n] ','y')):
759 return False
760 except EOFError:
761 return False
762
763 def int0(x):
764 try:
765 return int(x)
766 except TypeError:
767 return 0
768 # always pass integer line and offset values to editor hook
769 try:
770 self.hooks.fix_error_editor(e.filename,
771 int0(e.lineno),int0(e.offset),e.msg)
772 except TryNext:
773 warn('Could not open editor')
774 return False
775 return True
776
777 #-------------------------------------------------------------------------
778 # Things related to exiting
779 #-------------------------------------------------------------------------
780
781 def ask_exit(self):
782 """ Ask the shell to exit. Can be overiden and used as a callback. """
783 self.exit_now = True
784
785 def exit(self):
786 """Handle interactive exit.
787
788 This method calls the ask_exit callback."""
789 if self.confirm_exit:
790 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
791 self.ask_exit()
792 else:
793 self.ask_exit()
794
795 #-------------------------------------------------------------------------
796 # Things related to magics
797 #-------------------------------------------------------------------------
798
799 def init_magics(self):
800 super(TerminalInteractiveShell, self).init_magics()
801 self.register_magics(TerminalMagics)
802
803 def showindentationerror(self):
804 super(TerminalInteractiveShell, self).showindentationerror()
805 if not self.using_paste_magics:
806 print("If you want to paste code into IPython, try the "
807 "%paste and %cpaste magic functions.")
808
809
810 InteractiveShellABC.register(TerminalInteractiveShell)
13 class TerminalInteractiveShell(PromptToolkitShell):
14 def __init__(self, *args, **kwargs):
15 warn("This is a deprecated alias for IPython.terminal.ptshell.TerminalInteractiveShell. "
16 "The terminal interface of this class now uses prompt_toolkit instead of readline.",
17 DeprecationWarning, stacklevel=2)
18 PromptToolkitShell.__init__(self, *args, **kwargs)
@@ -1,470 +1,490 b''
1 1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 2 from __future__ import print_function
3 3
4 4 import os
5 5 import sys
6 6 import signal
7 7 from warnings import warn
8 8
9 9 from IPython.core.error import TryNext
10 from IPython.core.interactiveshell import InteractiveShell
11 from IPython.utils.py3compat import cast_unicode_py2, input
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
12 12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 13 from IPython.utils.process import abbrev_cwd
14 14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance
15 15
16 16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
18 18 from prompt_toolkit.history import InMemoryHistory
19 19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
20 20 from prompt_toolkit.interface import CommandLineInterface
21 21 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 22 from prompt_toolkit.keys import Keys
23 23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
24 24 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
25 25
26 26 from pygments.styles import get_style_by_name, get_all_styles
27 27 from pygments.token import Token
28 28
29 29 from .debugger import TerminalPdb, Pdb
30 from .magics import TerminalMagics
30 31 from .pt_inputhooks import get_inputhook_func
31 from .interactiveshell import get_default_editor, TerminalMagics
32 32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
33 33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
34 34
35
36 def get_default_editor():
37 try:
38 ed = os.environ['EDITOR']
39 if not PY3:
40 ed = ed.decode()
41 return ed
42 except KeyError:
43 pass
44 except UnicodeError:
45 warn("$EDITOR environment variable is not pure ASCII. Using platform "
46 "default editor.")
47
48 if os.name == 'posix':
49 return 'vi' # the only one guaranteed to be there!
50 else:
51 return 'notepad' # same in Windows!
52
35 53 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
36 54
37 55 class TerminalInteractiveShell(InteractiveShell):
38 56 colors_force = True
39 57
40 58 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
41 59 'to reserve for the completion menu'
42 60 ).tag(config=True)
43 61
44 62 def _space_for_menu_changed(self, old, new):
45 63 self._update_layout()
46 64
47 65 pt_cli = None
48 66 debugger_history = None
49 67
50 68 simple_prompt = Bool(_use_simple_prompt,
51 69 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
52 70
53 71 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
54 72 IPython own testing machinery, and emacs inferior-shell integration through elpy.
55 73
56 74 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
57 75 environment variable is set, or the current terminal is not a tty.
58 76
59 77 """
60 78 ).tag(config=True)
61 79
62 80 @property
63 81 def debugger_cls(self):
64 82 return Pdb if self.simple_prompt else TerminalPdb
65 83
66 84 autoedit_syntax = Bool(False,
67 85 help="auto editing of files with syntax errors.",
68 86 ).tag(config=True)
69 87
70 88
71 89 confirm_exit = Bool(True,
72 90 help="""
73 91 Set to confirm when you try to exit IPython with an EOF (Control-D
74 92 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
75 93 you can force a direct exit without any confirmation.""",
76 94 ).tag(config=True)
77 95
78 96 editing_mode = Unicode('emacs',
79 97 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
80 98 ).tag(config=True)
81 99
82 100 mouse_support = Bool(False,
83 101 help="Enable mouse support in the prompt"
84 102 ).tag(config=True)
85 103
86 104 highlighting_style = Unicode('default',
87 105 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
88 106 ).tag(config=True)
89 107
90 108
91 109 @observe('highlighting_style')
92 110 def _highlighting_style_changed(self, change):
93 111 self._style = self._make_style_from_name(self.highlighting_style)
94 112
95 113 highlighting_style_overrides = Dict(
96 114 help="Override highlighting format for specific tokens"
97 115 ).tag(config=True)
98 116
99 117 editor = Unicode(get_default_editor(),
100 118 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
101 119 ).tag(config=True)
102 120
103 121 prompts = Instance(Prompts)
104 122
105 123 def _prompts_default(self):
106 124 return Prompts(self)
107 125
108 126 @observe('prompts')
109 127 def _(self, change):
110 128 self._update_layout()
111 129
112 130 def _displayhook_class_default(self):
113 131 return RichPromptDisplayHook
114 132
115 133 term_title = Bool(True,
116 134 help="Automatically set the terminal title"
117 135 ).tag(config=True)
118 136
119 137 display_completions_in_columns = Bool(False,
120 138 help="Display a multi column completion menu.",
121 139 ).tag(config=True)
122 140
123 141 highlight_matching_brackets = Bool(True,
124 142 help="Highlight matching brackets .",
125 143 ).tag(config=True)
126 144
127 145 @observe('term_title')
128 146 def init_term_title(self, change=None):
129 147 # Enable or disable the terminal title.
130 148 if self.term_title:
131 149 toggle_set_term_title(True)
132 150 set_term_title('IPython: ' + abbrev_cwd())
133 151 else:
134 152 toggle_set_term_title(False)
135 153
136 154 def init_prompt_toolkit_cli(self):
137 155 if self.simple_prompt:
138 156 # Fall back to plain non-interactive output for tests.
139 157 # This is very limited, and only accepts a single line.
140 158 def prompt():
141 159 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
142 160 self.prompt_for_code = prompt
143 161 return
144 162
145 163 kbmanager = KeyBindingManager.for_prompt()
146 164 insert_mode = ViInsertMode() | EmacsInsertMode()
147 165 # Ctrl+J == Enter, seemingly
148 166 @kbmanager.registry.add_binding(Keys.ControlJ,
149 167 filter=(HasFocus(DEFAULT_BUFFER)
150 168 & ~HasSelection()
151 169 & insert_mode
152 170 ))
153 171 def _(event):
154 172 b = event.current_buffer
155 173 d = b.document
156 174 if not (d.on_last_line or d.cursor_position_row >= d.line_count
157 175 - d.empty_line_count_at_the_end()):
158 176 b.newline()
159 177 return
160 178
161 179 status, indent = self.input_splitter.check_complete(d.text)
162 180
163 181 if (status != 'incomplete') and b.accept_action.is_returnable:
164 182 b.accept_action.validate_and_handle(event.cli, b)
165 183 else:
166 184 b.insert_text('\n' + (' ' * (indent or 0)))
167 185
168 186 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
169 187 def _reset_buffer(event):
170 188 event.current_buffer.reset()
171 189
172 190 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
173 191 def _reset_search_buffer(event):
174 192 if event.current_buffer.document.text:
175 193 event.current_buffer.reset()
176 194 else:
177 195 event.cli.push_focus(DEFAULT_BUFFER)
178 196
179 197 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
180 198
181 199 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
182 200 def _suspend_to_bg(event):
183 201 event.cli.suspend_to_background()
184 202
185 203 @Condition
186 204 def cursor_in_leading_ws(cli):
187 205 before = cli.application.buffer.document.current_line_before_cursor
188 206 return (not before) or before.isspace()
189 207
190 208 # Ctrl+I == Tab
191 209 @kbmanager.registry.add_binding(Keys.ControlI,
192 210 filter=(HasFocus(DEFAULT_BUFFER)
193 211 & ~HasSelection()
194 212 & insert_mode
195 213 & cursor_in_leading_ws
196 214 ))
197 215 def _indent_buffer(event):
198 216 event.current_buffer.insert_text(' ' * 4)
199 217
200 218 # Pre-populate history from IPython's history database
201 219 history = InMemoryHistory()
202 220 last_cell = u""
203 221 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
204 222 include_latest=True):
205 223 # Ignore blank lines and consecutive duplicates
206 224 cell = cell.rstrip()
207 225 if cell and (cell != last_cell):
208 226 history.append(cell)
209 227
210 228 self._style = self._make_style_from_name(self.highlighting_style)
211 229 style = DynamicStyle(lambda: self._style)
212 230
213 231 editing_mode = getattr(EditingMode, self.editing_mode.upper())
214 232
215 233 self._app = create_prompt_application(
216 234 editing_mode=editing_mode,
217 235 key_bindings_registry=kbmanager.registry,
218 236 history=history,
219 237 completer=IPythonPTCompleter(self.Completer),
220 238 enable_history_search=True,
221 239 style=style,
222 240 mouse_support=self.mouse_support,
223 241 **self._layout_options()
224 242 )
225 243 self._eventloop = create_eventloop(self.inputhook)
226 244 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
227 245
228 246 def _make_style_from_name(self, name):
229 247 """
230 248 Small wrapper that make an IPython compatible style from a style name
231 249
232 250 We need that to add style for prompt ... etc.
233 251 """
234 252 style_cls = get_style_by_name(name)
235 253 style_overrides = {
236 254 Token.Prompt: '#009900',
237 255 Token.PromptNum: '#00ff00 bold',
238 256 Token.OutPrompt: '#990000',
239 257 Token.OutPromptNum: '#ff0000 bold',
240 258 }
241 259 if name == 'default':
242 260 style_cls = get_style_by_name('default')
243 261 # The default theme needs to be visible on both a dark background
244 262 # and a light background, because we can't tell what the terminal
245 263 # looks like. These tweaks to the default theme help with that.
246 264 style_overrides.update({
247 265 Token.Number: '#007700',
248 266 Token.Operator: 'noinherit',
249 267 Token.String: '#BB6622',
250 268 Token.Name.Function: '#2080D0',
251 269 Token.Name.Class: 'bold #2080D0',
252 270 Token.Name.Namespace: 'bold #2080D0',
253 271 })
254 272 style_overrides.update(self.highlighting_style_overrides)
255 273 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
256 274 style_dict=style_overrides)
257 275
258 276 return style
259 277
260 278 def _layout_options(self):
261 279 """
262 280 Return the current layout option for the current Terminal InteractiveShell
263 281 """
264 282 return {
265 283 'lexer':IPythonPTLexer(),
266 284 'reserve_space_for_menu':self.space_for_menu,
267 285 'get_prompt_tokens':self.prompts.in_prompt_tokens,
268 286 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
269 287 'multiline':True,
270 288 'display_completions_in_columns': self.display_completions_in_columns,
271 289
272 290 # Highlight matching brackets, but only when this setting is
273 291 # enabled, and only when the DEFAULT_BUFFER has the focus.
274 292 'extra_input_processors': [ConditionalProcessor(
275 293 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
276 294 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
277 295 Condition(lambda cli: self.highlight_matching_brackets))],
278 296 }
279 297
280 298 def _update_layout(self):
281 299 """
282 300 Ask for a re computation of the application layout, if for example ,
283 301 some configuration options have changed.
284 302 """
285 303 self._app.layout = create_prompt_layout(**self._layout_options())
286 304
287 305 def prompt_for_code(self):
288 306 document = self.pt_cli.run(
289 307 pre_run=self.pre_prompt, reset_current_buffer=True)
290 308 return document.text
291 309
292 310 def init_io(self):
293 311 if sys.platform not in {'win32', 'cli'}:
294 312 return
295 313
296 314 import colorama
297 315 colorama.init()
298 316
299 317 # For some reason we make these wrappers around stdout/stderr.
300 318 # For now, we need to reset them so all output gets coloured.
301 319 # https://github.com/ipython/ipython/issues/8669
302 320 from IPython.utils import io
303 321 io.stdout = io.IOStream(sys.stdout)
304 322 io.stderr = io.IOStream(sys.stderr)
305 323
306 324 def init_magics(self):
307 325 super(TerminalInteractiveShell, self).init_magics()
308 326 self.register_magics(TerminalMagics)
309 327
310 328 def init_alias(self):
311 329 # The parent class defines aliases that can be safely used with any
312 330 # frontend.
313 331 super(TerminalInteractiveShell, self).init_alias()
314 332
315 333 # Now define aliases that only make sense on the terminal, because they
316 334 # need direct access to the console in a way that we can't emulate in
317 335 # GUI or web frontend
318 336 if os.name == 'posix':
319 337 for cmd in ['clear', 'more', 'less', 'man']:
320 338 self.alias_manager.soft_define_alias(cmd, cmd)
321 339
322 340
323 341 def __init__(self, *args, **kwargs):
324 342 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
325 343 self.init_prompt_toolkit_cli()
326 344 self.init_term_title()
327 345 self.keep_running = True
328 346
329 347 self.debugger_history = InMemoryHistory()
330 348
331 349 def ask_exit(self):
332 350 self.keep_running = False
333 351
334 352 rl_next_input = None
335 353
336 354 def pre_prompt(self):
337 355 if self.rl_next_input:
338 356 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
339 357 self.rl_next_input = None
340 358
341 359 def interact(self):
342 360 while self.keep_running:
343 361 print(self.separate_in, end='')
344 362
345 363 try:
346 364 code = self.prompt_for_code()
347 365 except EOFError:
348 366 if (not self.confirm_exit) \
349 367 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
350 368 self.ask_exit()
351 369
352 370 else:
353 371 if code:
354 372 self.run_cell(code, store_history=True)
355 373 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
356 374 self.edit_syntax_error()
357 375
358 376 def mainloop(self):
359 377 # An extra layer of protection in case someone mashing Ctrl-C breaks
360 378 # out of our internal code.
361 379 while True:
362 380 try:
363 381 self.interact()
364 382 break
365 383 except KeyboardInterrupt:
366 384 print("\nKeyboardInterrupt escaped interact()\n")
367 385
368 386 if hasattr(self, '_eventloop'):
369 387 self._eventloop.close()
370 388
371 389 _inputhook = None
372 390 def inputhook(self, context):
373 391 if self._inputhook is not None:
374 392 self._inputhook(context)
375 393
376 394 def enable_gui(self, gui=None):
377 395 if gui:
378 396 self._inputhook = get_inputhook_func(gui)
379 397 else:
380 398 self._inputhook = None
381 399
382 400 # Methods to support auto-editing of SyntaxErrors:
383 401
384 402 def edit_syntax_error(self):
385 403 """The bottom half of the syntax error handler called in the main loop.
386 404
387 405 Loop until syntax error is fixed or user cancels.
388 406 """
389 407
390 408 while self.SyntaxTB.last_syntax_error:
391 409 # copy and clear last_syntax_error
392 410 err = self.SyntaxTB.clear_err_state()
393 411 if not self._should_recompile(err):
394 412 return
395 413 try:
396 414 # may set last_syntax_error again if a SyntaxError is raised
397 415 self.safe_execfile(err.filename, self.user_ns)
398 416 except:
399 417 self.showtraceback()
400 418 else:
401 419 try:
402 420 with open(err.filename) as f:
403 421 # This should be inside a display_trap block and I
404 422 # think it is.
405 423 sys.displayhook(f.read())
406 424 except:
407 425 self.showtraceback()
408 426
409 427 def _should_recompile(self, e):
410 428 """Utility routine for edit_syntax_error"""
411 429
412 430 if e.filename in ('<ipython console>', '<input>', '<string>',
413 431 '<console>', '<BackgroundJob compilation>',
414 432 None):
415 433 return False
416 434 try:
417 435 if (self.autoedit_syntax and
418 436 not self.ask_yes_no(
419 437 'Return to editor to correct syntax error? '
420 438 '[Y/n] ', 'y')):
421 439 return False
422 440 except EOFError:
423 441 return False
424 442
425 443 def int0(x):
426 444 try:
427 445 return int(x)
428 446 except TypeError:
429 447 return 0
430 448
431 449 # always pass integer line and offset values to editor hook
432 450 try:
433 451 self.hooks.fix_error_editor(e.filename,
434 452 int0(e.lineno), int0(e.offset),
435 453 e.msg)
436 454 except TryNext:
437 455 warn('Could not open editor')
438 456 return False
439 457 return True
440 458
441 459 # Run !system commands directly, not through pipes, so terminal programs
442 460 # work correctly.
443 461 system = InteractiveShell.system_raw
444 462
445 463 def auto_rewrite_input(self, cmd):
446 464 """Overridden from the parent class to use fancy rewriting prompt"""
447 465 if not self.show_rewritten_input:
448 466 return
449 467
450 468 tokens = self.prompts.rewrite_prompt_tokens()
451 469 if self.pt_cli:
452 470 self.pt_cli.print_tokens(tokens)
453 471 print(cmd)
454 472 else:
455 473 prompt = ''.join(s for t, s in tokens)
456 474 print(prompt, cmd, sep='')
457 475
458 476 _prompts_before = None
459 477 def switch_doctest_mode(self, mode):
460 478 """Switch prompts to classic for %doctest_mode"""
461 479 if mode:
462 480 self._prompts_before = self.prompts
463 481 self.prompts = ClassicPrompts(self)
464 482 elif self._prompts_before:
465 483 self.prompts = self._prompts_before
466 484 self._prompts_before = None
467 485
468 486
487 InteractiveShellABC.register(TerminalInteractiveShell)
488
469 489 if __name__ == '__main__':
470 490 TerminalInteractiveShell.instance().interact()
@@ -1,295 +1,124 b''
1 1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
3
4 Authors
5 -------
6 * Julian Taylor
7 """
2 """Tests for the TerminalInteractiveShell and related pieces."""
8 3 #-----------------------------------------------------------------------------
9 4 # Copyright (C) 2011 The IPython Development Team
10 5 #
11 6 # Distributed under the terms of the BSD License. The full license is in
12 7 # the file COPYING, distributed as part of this software.
13 8 #-----------------------------------------------------------------------------
14 9
15 #-----------------------------------------------------------------------------
16 # Imports
17 #-----------------------------------------------------------------------------
18 # stdlib
19 10 import sys
20 import types
21 11 import unittest
22 12
23 13 from IPython.core.inputtransformer import InputTransformer
24 from IPython.testing.decorators import skipif
25 from IPython.utils import py3compat
26 14 from IPython.testing import tools as tt
27 15
28 16 # Decorator for interaction loop tests -----------------------------------------
29 17
30 18 class mock_input_helper(object):
31 19 """Machinery for tests of the main interact loop.
32 20
33 21 Used by the mock_input decorator.
34 22 """
35 23 def __init__(self, testgen):
36 24 self.testgen = testgen
37 25 self.exception = None
38 26 self.ip = get_ipython()
39 27
40 28 def __enter__(self):
41 self.orig_raw_input = self.ip.raw_input
42 self.ip.raw_input = self.fake_input
29 self.orig_prompt_for_code = self.ip.prompt_for_code
30 self.ip.prompt_for_code = self.fake_input
43 31 return self
44 32
45 33 def __exit__(self, etype, value, tb):
46 self.ip.raw_input = self.orig_raw_input
34 self.ip.prompt_for_code = self.orig_prompt_for_code
47 35
48 def fake_input(self, prompt):
36 def fake_input(self):
49 37 try:
50 38 return next(self.testgen)
51 39 except StopIteration:
52 self.ip.exit_now = True
40 self.ip.keep_running = False
53 41 return u''
54 42 except:
55 43 self.exception = sys.exc_info()
56 self.ip.exit_now = True
44 self.ip.keep_running = False
57 45 return u''
58 46
59 47 def mock_input(testfunc):
60 48 """Decorator for tests of the main interact loop.
61 49
62 50 Write the test as a generator, yield-ing the input strings, which IPython
63 51 will see as if they were typed in at the prompt.
64 52 """
65 53 def test_method(self):
66 54 testgen = testfunc(self)
67 55 with mock_input_helper(testgen) as mih:
68 mih.ip.interact(display_banner=False)
56 mih.ip.interact()
69 57
70 58 if mih.exception is not None:
71 59 # Re-raise captured exception
72 60 etype, value, tb = mih.exception
73 61 import traceback
74 62 traceback.print_tb(tb, file=sys.stdout)
75 63 del tb # Avoid reference loop
76 64 raise value
77 65
78 66 return test_method
79 67
80 68 # Test classes -----------------------------------------------------------------
81 69
82 70 class InteractiveShellTestCase(unittest.TestCase):
83 71 def rl_hist_entries(self, rl, n):
84 72 """Get last n readline history entries as a list"""
85 73 return [rl.get_history_item(rl.get_current_history_length() - x)
86 74 for x in range(n - 1, -1, -1)]
87 75
88 def test_runs_without_rl(self):
89 """Test that function does not throw without readline"""
90 ip = get_ipython()
91 ip.has_readline = False
92 ip.readline = None
93 ip._replace_rlhist_multiline(u'source', 0)
94
95 @skipif(not get_ipython().has_readline, 'no readline')
96 def test_runs_without_remove_history_item(self):
97 """Test that function does not throw on windows without
98 remove_history_item"""
99 ip = get_ipython()
100 if hasattr(ip.readline, 'remove_history_item'):
101 del ip.readline.remove_history_item
102 ip._replace_rlhist_multiline(u'source', 0)
103
104 @skipif(not get_ipython().has_readline, 'no readline')
105 @skipif(not hasattr(get_ipython().readline, 'remove_history_item'),
106 'no remove_history_item')
107 def test_replace_multiline_hist_disabled(self):
108 """Test that multiline replace does nothing if disabled"""
109 ip = get_ipython()
110 ip.multiline_history = False
111
112 ghist = [u'line1', u'line2']
113 for h in ghist:
114 ip.readline.add_history(h)
115 hlen_b4_cell = ip.readline.get_current_history_length()
116 hlen_b4_cell = ip._replace_rlhist_multiline(u'sourc€\nsource2',
117 hlen_b4_cell)
118
119 self.assertEqual(ip.readline.get_current_history_length(),
120 hlen_b4_cell)
121 hist = self.rl_hist_entries(ip.readline, 2)
122 self.assertEqual(hist, ghist)
123
124 @skipif(not get_ipython().has_readline, 'no readline')
125 @skipif(not hasattr(get_ipython().readline, 'remove_history_item'),
126 'no remove_history_item')
127 def test_replace_multiline_hist_adds(self):
128 """Test that multiline replace function adds history"""
129 ip = get_ipython()
130
131 hlen_b4_cell = ip.readline.get_current_history_length()
132 hlen_b4_cell = ip._replace_rlhist_multiline(u'sourc€', hlen_b4_cell)
133
134 self.assertEqual(hlen_b4_cell,
135 ip.readline.get_current_history_length())
136
137 @skipif(not get_ipython().has_readline, 'no readline')
138 @skipif(not hasattr(get_ipython().readline, 'remove_history_item'),
139 'no remove_history_item')
140 def test_replace_multiline_hist_keeps_history(self):
141 """Test that multiline replace does not delete history"""
142 ip = get_ipython()
143 ip.multiline_history = True
144
145 ghist = [u'line1', u'line2']
146 for h in ghist:
147 ip.readline.add_history(h)
148
149 #start cell
150 hlen_b4_cell = ip.readline.get_current_history_length()
151 # nothing added to rl history, should do nothing
152 hlen_b4_cell = ip._replace_rlhist_multiline(u'sourc€\nsource2',
153 hlen_b4_cell)
154
155 self.assertEqual(ip.readline.get_current_history_length(),
156 hlen_b4_cell)
157 hist = self.rl_hist_entries(ip.readline, 2)
158 self.assertEqual(hist, ghist)
159
160
161 @skipif(not get_ipython().has_readline, 'no readline')
162 @skipif(not hasattr(get_ipython().readline, 'remove_history_item'),
163 'no remove_history_item')
164 def test_replace_multiline_hist_replaces_twice(self):
165 """Test that multiline entries are replaced twice"""
166 ip = get_ipython()
167 ip.multiline_history = True
168
169 ip.readline.add_history(u'line0')
170 #start cell
171 hlen_b4_cell = ip.readline.get_current_history_length()
172 ip.readline.add_history('l€ne1')
173 ip.readline.add_history('line2')
174 #replace cell with single line
175 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne1\nline2',
176 hlen_b4_cell)
177 ip.readline.add_history('l€ne3')
178 ip.readline.add_history('line4')
179 #replace cell with single line
180 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne3\nline4',
181 hlen_b4_cell)
182
183 self.assertEqual(ip.readline.get_current_history_length(),
184 hlen_b4_cell)
185 hist = self.rl_hist_entries(ip.readline, 3)
186 expected = [u'line0', u'l€ne1\nline2', u'l€ne3\nline4']
187 # perform encoding, in case of casting due to ASCII locale
188 enc = sys.stdin.encoding or "utf-8"
189 expected = [ py3compat.unicode_to_str(e, enc) for e in expected ]
190 self.assertEqual(hist, expected)
191
192
193 @skipif(not get_ipython().has_readline, 'no readline')
194 @skipif(not hasattr(get_ipython().readline, 'remove_history_item'),
195 'no remove_history_item')
196 def test_replace_multiline_hist_replaces_empty_line(self):
197 """Test that multiline history skips empty line cells"""
198 ip = get_ipython()
199 ip.multiline_history = True
200
201 ip.readline.add_history(u'line0')
202 #start cell
203 hlen_b4_cell = ip.readline.get_current_history_length()
204 ip.readline.add_history('l€ne1')
205 ip.readline.add_history('line2')
206 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne1\nline2',
207 hlen_b4_cell)
208 ip.readline.add_history('')
209 hlen_b4_cell = ip._replace_rlhist_multiline(u'', hlen_b4_cell)
210 ip.readline.add_history('l€ne3')
211 hlen_b4_cell = ip._replace_rlhist_multiline(u'l€ne3', hlen_b4_cell)
212 ip.readline.add_history(' ')
213 hlen_b4_cell = ip._replace_rlhist_multiline(' ', hlen_b4_cell)
214 ip.readline.add_history('\t')
215 ip.readline.add_history('\t ')
216 hlen_b4_cell = ip._replace_rlhist_multiline('\t', hlen_b4_cell)
217 ip.readline.add_history('line4')
218 hlen_b4_cell = ip._replace_rlhist_multiline(u'line4', hlen_b4_cell)
219
220 self.assertEqual(ip.readline.get_current_history_length(),
221 hlen_b4_cell)
222 hist = self.rl_hist_entries(ip.readline, 4)
223 # expect no empty cells in history
224 expected = [u'line0', u'l€ne1\nline2', u'l€ne3', u'line4']
225 # perform encoding, in case of casting due to ASCII locale
226 enc = sys.stdin.encoding or "utf-8"
227 expected = [ py3compat.unicode_to_str(e, enc) for e in expected ]
228 self.assertEqual(hist, expected)
229
230 76 @mock_input
231 77 def test_inputtransformer_syntaxerror(self):
232 78 ip = get_ipython()
233 79 transformer = SyntaxErrorTransformer()
234 80 ip.input_splitter.python_line_transforms.append(transformer)
235 81 ip.input_transformer_manager.python_line_transforms.append(transformer)
236 82
237 83 try:
238 84 #raise Exception
239 85 with tt.AssertPrints('4', suppress=False):
240 86 yield u'print(2*2)'
241 87
242 88 with tt.AssertPrints('SyntaxError: input contains', suppress=False):
243 89 yield u'print(2345) # syntaxerror'
244 90
245 91 with tt.AssertPrints('16', suppress=False):
246 92 yield u'print(4*4)'
247 93
248 94 finally:
249 95 ip.input_splitter.python_line_transforms.remove(transformer)
250 96 ip.input_transformer_manager.python_line_transforms.remove(transformer)
251 97
252 98
253 99 class SyntaxErrorTransformer(InputTransformer):
254 100 def push(self, line):
255 101 pos = line.find('syntaxerror')
256 102 if pos >= 0:
257 103 e = SyntaxError('input contains "syntaxerror"')
258 104 e.text = line
259 105 e.offset = pos + 1
260 106 raise e
261 107 return line
262 108
263 109 def reset(self):
264 110 pass
265 111
266 112 class TerminalMagicsTestCase(unittest.TestCase):
267 def test_paste_magics_message(self):
268 """Test that an IndentationError while using paste magics doesn't
269 trigger a message about paste magics and also the opposite."""
270
271 ip = get_ipython()
272 s = ('for a in range(5):\n'
273 'print(a)')
274
275 tm = ip.magics_manager.registry['TerminalMagics']
276 with tt.AssertPrints("If you want to paste code into IPython, try the "
277 "%paste and %cpaste magic functions."):
278 ip.run_cell(s)
279
280 with tt.AssertNotPrints("If you want to paste code into IPython, try the "
281 "%paste and %cpaste magic functions."):
282 tm.store_or_execute(s, name=None)
283
284 113 def test_paste_magics_blankline(self):
285 114 """Test that code with a blank line doesn't get split (gh-3246)."""
286 115 ip = get_ipython()
287 116 s = ('def pasted_func(a):\n'
288 117 ' b = a+1\n'
289 118 '\n'
290 119 ' return b')
291 120
292 121 tm = ip.magics_manager.registry['TerminalMagics']
293 122 tm.store_or_execute(s, name=None)
294 123
295 124 self.assertEqual(ip.user_ns['pasted_func'](54), 55)
General Comments 0
You need to be logged in to leave comments. Login now