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