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