##// END OF EJS Templates
Add flag for simple_prompt....
Matthias Bussonnier -
Show More
@@ -0,0 +1,4 b''
1 when using IPython as a subprocess, like for emacs inferior-shell, IPython can
2 be started with --simple-prompt flag, which will bypass the prompt_toolkit
3 input layer. In this mode completion, prompt color and many other features are
4 disabled.
@@ -1,371 +1,376 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 """
4 4 The :class:`~IPython.core.application.Application` object for the command
5 5 line :command:`ipython` program.
6 6 """
7 7
8 8 # Copyright (c) IPython Development Team.
9 9 # Distributed under the terms of the Modified BSD License.
10 10
11 11 from __future__ import absolute_import
12 12 from __future__ import print_function
13 13
14 14 import logging
15 15 import os
16 16 import sys
17 17 import warnings
18 18
19 19 from traitlets.config.loader import Config
20 20 from traitlets.config.application import boolean_flag, catch_config_error, Application
21 21 from IPython.core import release
22 22 from IPython.core import usage
23 23 from IPython.core.completer import IPCompleter
24 24 from IPython.core.crashhandler import CrashHandler
25 25 from IPython.core.formatters import PlainTextFormatter
26 26 from IPython.core.history import HistoryManager
27 27 from IPython.core.prompts import PromptManager
28 28 from IPython.core.application import (
29 29 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
30 30 )
31 31 from IPython.core.magics import ScriptMagics
32 32 from IPython.core.shellapp import (
33 33 InteractiveShellApp, shell_flags, shell_aliases
34 34 )
35 35 from IPython.extensions.storemagic import StoreMagics
36 36 from .ptshell import TerminalInteractiveShell
37 37 from IPython.paths import get_ipython_dir
38 38 from traitlets import (
39 39 Bool, List, Dict, default, observe,
40 40 )
41 41
42 42 #-----------------------------------------------------------------------------
43 43 # Globals, utilities and helpers
44 44 #-----------------------------------------------------------------------------
45 45
46 46 _examples = """
47 47 ipython --matplotlib # enable matplotlib integration
48 48 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
49 49
50 50 ipython --log-level=DEBUG # set logging to DEBUG
51 51 ipython --profile=foo # start with profile foo
52 52
53 53 ipython profile create foo # create profile foo w/ default config files
54 54 ipython help profile # show the help for the profile subcmd
55 55
56 56 ipython locate # print the path to the IPython directory
57 57 ipython locate profile foo # print the path to the directory for profile `foo`
58 58 """
59 59
60 60 #-----------------------------------------------------------------------------
61 61 # Crash handler for this application
62 62 #-----------------------------------------------------------------------------
63 63
64 64 class IPAppCrashHandler(CrashHandler):
65 65 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
66 66
67 67 def __init__(self, app):
68 68 contact_name = release.author
69 69 contact_email = release.author_email
70 70 bug_tracker = 'https://github.com/ipython/ipython/issues'
71 71 super(IPAppCrashHandler,self).__init__(
72 72 app, contact_name, contact_email, bug_tracker
73 73 )
74 74
75 75 def make_report(self,traceback):
76 76 """Return a string containing a crash report."""
77 77
78 78 sec_sep = self.section_sep
79 79 # Start with parent report
80 80 report = [super(IPAppCrashHandler, self).make_report(traceback)]
81 81 # Add interactive-specific info we may have
82 82 rpt_add = report.append
83 83 try:
84 84 rpt_add(sec_sep+"History of session input:")
85 85 for line in self.app.shell.user_ns['_ih']:
86 86 rpt_add(line)
87 87 rpt_add('\n*** Last line of input (may not be in above history):\n')
88 88 rpt_add(self.app.shell._last_input_line+'\n')
89 89 except:
90 90 pass
91 91
92 92 return ''.join(report)
93 93
94 94 #-----------------------------------------------------------------------------
95 95 # Aliases and Flags
96 96 #-----------------------------------------------------------------------------
97 97 flags = dict(base_flags)
98 98 flags.update(shell_flags)
99 99 frontend_flags = {}
100 100 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
101 101 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
102 102 'Turn on auto editing of files with syntax errors.',
103 103 'Turn off auto editing of files with syntax errors.'
104 104 )
105 addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
106 "Force simple minimal prompt using `raw_input`",
107 "Use a rich interactive prompt with prompt_toolkit",
108 )
109
105 110 addflag('banner', 'TerminalIPythonApp.display_banner',
106 111 "Display a banner upon starting IPython.",
107 112 "Don't display a banner upon starting IPython."
108 113 )
109 114 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
110 115 """Set to confirm when you try to exit IPython with an EOF (Control-D
111 116 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
112 117 you can force a direct exit without any confirmation.""",
113 118 "Don't prompt the user when exiting."
114 119 )
115 120 addflag('term-title', 'TerminalInteractiveShell.term_title',
116 121 "Enable auto setting the terminal title.",
117 122 "Disable auto setting the terminal title."
118 123 )
119 124 classic_config = Config()
120 125 classic_config.InteractiveShell.cache_size = 0
121 126 classic_config.PlainTextFormatter.pprint = False
122 127 classic_config.PromptManager.in_template = '>>> '
123 128 classic_config.PromptManager.in2_template = '... '
124 129 classic_config.PromptManager.out_template = ''
125 130 classic_config.InteractiveShell.separate_in = ''
126 131 classic_config.InteractiveShell.separate_out = ''
127 132 classic_config.InteractiveShell.separate_out2 = ''
128 133 classic_config.InteractiveShell.colors = 'NoColor'
129 134 classic_config.InteractiveShell.xmode = 'Plain'
130 135
131 136 frontend_flags['classic']=(
132 137 classic_config,
133 138 "Gives IPython a similar feel to the classic Python prompt."
134 139 )
135 140 # # log doesn't make so much sense this way anymore
136 141 # paa('--log','-l',
137 142 # action='store_true', dest='InteractiveShell.logstart',
138 143 # help="Start logging to the default log file (./ipython_log.py).")
139 144 #
140 145 # # quick is harder to implement
141 146 frontend_flags['quick']=(
142 147 {'TerminalIPythonApp' : {'quick' : True}},
143 148 "Enable quick startup with no config files."
144 149 )
145 150
146 151 frontend_flags['i'] = (
147 152 {'TerminalIPythonApp' : {'force_interact' : True}},
148 153 """If running code from the command line, become interactive afterwards.
149 154 It is often useful to follow this with `--` to treat remaining flags as
150 155 script arguments.
151 156 """
152 157 )
153 158 flags.update(frontend_flags)
154 159
155 160 aliases = dict(base_aliases)
156 161 aliases.update(shell_aliases)
157 162
158 163 #-----------------------------------------------------------------------------
159 164 # Main classes and functions
160 165 #-----------------------------------------------------------------------------
161 166
162 167
163 168 class LocateIPythonApp(BaseIPythonApplication):
164 169 description = """print the path to the IPython dir"""
165 170 subcommands = Dict(dict(
166 171 profile=('IPython.core.profileapp.ProfileLocate',
167 172 "print the path to an IPython profile directory",
168 173 ),
169 174 ))
170 175 def start(self):
171 176 if self.subapp is not None:
172 177 return self.subapp.start()
173 178 else:
174 179 print(self.ipython_dir)
175 180
176 181
177 182 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
178 183 name = u'ipython'
179 184 description = usage.cl_usage
180 185 crash_handler_class = IPAppCrashHandler
181 186 examples = _examples
182 187
183 188 flags = Dict(flags)
184 189 aliases = Dict(aliases)
185 190 classes = List()
186 191 @default('classes')
187 192 def _classes_default(self):
188 193 """This has to be in a method, for TerminalIPythonApp to be available."""
189 194 return [
190 195 InteractiveShellApp, # ShellApp comes before TerminalApp, because
191 196 self.__class__, # it will also affect subclasses (e.g. QtConsole)
192 197 TerminalInteractiveShell,
193 198 PromptManager,
194 199 HistoryManager,
195 200 ProfileDir,
196 201 PlainTextFormatter,
197 202 IPCompleter,
198 203 ScriptMagics,
199 204 StoreMagics,
200 205 ]
201 206
202 207 deprecated_subcommands = dict(
203 208 qtconsole=('qtconsole.qtconsoleapp.JupyterQtConsoleApp',
204 209 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter Qt Console."""
205 210 ),
206 211 notebook=('notebook.notebookapp.NotebookApp',
207 212 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter HTML Notebook Server."""
208 213 ),
209 214 console=('jupyter_console.app.ZMQTerminalIPythonApp',
210 215 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter terminal-based Console."""
211 216 ),
212 217 nbconvert=('nbconvert.nbconvertapp.NbConvertApp',
213 218 "DEPRECATED, Will be removed in IPython 6.0 : Convert notebooks to/from other formats."
214 219 ),
215 220 trust=('nbformat.sign.TrustNotebookApp',
216 221 "DEPRECATED, Will be removed in IPython 6.0 : Sign notebooks to trust their potentially unsafe contents at load."
217 222 ),
218 223 kernelspec=('jupyter_client.kernelspecapp.KernelSpecApp',
219 224 "DEPRECATED, Will be removed in IPython 6.0 : Manage Jupyter kernel specifications."
220 225 ),
221 226 )
222 227 subcommands = dict(
223 228 profile = ("IPython.core.profileapp.ProfileApp",
224 229 "Create and manage IPython profiles."
225 230 ),
226 231 kernel = ("ipykernel.kernelapp.IPKernelApp",
227 232 "Start a kernel without an attached frontend."
228 233 ),
229 234 locate=('IPython.terminal.ipapp.LocateIPythonApp',
230 235 LocateIPythonApp.description
231 236 ),
232 237 history=('IPython.core.historyapp.HistoryApp',
233 238 "Manage the IPython history database."
234 239 ),
235 240 )
236 241 deprecated_subcommands['install-nbextension'] = (
237 242 "notebook.nbextensions.InstallNBExtensionApp",
238 243 "DEPRECATED, Will be removed in IPython 6.0 : Install Jupyter notebook extension files"
239 244 )
240 245 subcommands.update(deprecated_subcommands)
241 246
242 247 # *do* autocreate requested profile, but don't create the config file.
243 248 auto_create=Bool(True)
244 249 # configurables
245 250 quick = Bool(False,
246 251 help="""Start IPython quickly by skipping the loading of config files."""
247 252 ).tag(config=True)
248 253 @observe('quick')
249 254 def _quick_changed(self, change):
250 255 if change['new']:
251 256 self.load_config_file = lambda *a, **kw: None
252 257
253 258 display_banner = Bool(True,
254 259 help="Whether to display a banner upon starting IPython."
255 260 ).tag(config=True)
256 261
257 262 # if there is code of files to run from the cmd line, don't interact
258 263 # unless the --i flag (App.force_interact) is true.
259 264 force_interact = Bool(False,
260 265 help="""If a command or file is given via the command-line,
261 266 e.g. 'ipython foo.py', start an interactive shell after executing the
262 267 file or command."""
263 268 ).tag(config=True)
264 269 @observe('force_interact')
265 270 def _force_interact_changed(self, change):
266 271 if change['new']:
267 272 self.interact = True
268 273
269 274 @observe('file_to_run', 'code_to_run', 'module_to_run')
270 275 def _file_to_run_changed(self, change):
271 276 new = change['new']
272 277 if new:
273 278 self.something_to_run = True
274 279 if new and not self.force_interact:
275 280 self.interact = False
276 281
277 282 # internal, not-configurable
278 283 something_to_run=Bool(False)
279 284
280 285 def parse_command_line(self, argv=None):
281 286 """override to allow old '-pylab' flag with deprecation warning"""
282 287
283 288 argv = sys.argv[1:] if argv is None else argv
284 289
285 290 if '-pylab' in argv:
286 291 # deprecated `-pylab` given,
287 292 # warn and transform into current syntax
288 293 argv = argv[:] # copy, don't clobber
289 294 idx = argv.index('-pylab')
290 295 warnings.warn("`-pylab` flag has been deprecated.\n"
291 296 " Use `--matplotlib <backend>` and import pylab manually.")
292 297 argv[idx] = '--pylab'
293 298
294 299 return super(TerminalIPythonApp, self).parse_command_line(argv)
295 300
296 301 @catch_config_error
297 302 def initialize(self, argv=None):
298 303 """Do actions after construct, but before starting the app."""
299 304 super(TerminalIPythonApp, self).initialize(argv)
300 305 if self.subapp is not None:
301 306 # don't bother initializing further, starting subapp
302 307 return
303 308 # print self.extra_args
304 309 if self.extra_args and not self.something_to_run:
305 310 self.file_to_run = self.extra_args[0]
306 311 self.init_path()
307 312 # create the shell
308 313 self.init_shell()
309 314 # and draw the banner
310 315 self.init_banner()
311 316 # Now a variety of things that happen after the banner is printed.
312 317 self.init_gui_pylab()
313 318 self.init_extensions()
314 319 self.init_code()
315 320
316 321 def init_shell(self):
317 322 """initialize the InteractiveShell instance"""
318 323 # Create an InteractiveShell instance.
319 324 # shell.display_banner should always be False for the terminal
320 325 # based app, because we call shell.show_banner() by hand below
321 326 # so the banner shows *before* all extension loading stuff.
322 327 self.shell = TerminalInteractiveShell.instance(parent=self,
323 328 profile_dir=self.profile_dir,
324 329 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
325 330 self.shell.configurables.append(self)
326 331
327 332 def init_banner(self):
328 333 """optionally display the banner"""
329 334 if self.display_banner and self.interact:
330 335 self.shell.show_banner()
331 336 # Make sure there is a space below the banner.
332 337 if self.log_level <= logging.INFO: print()
333 338
334 339 def _pylab_changed(self, name, old, new):
335 340 """Replace --pylab='inline' with --pylab='auto'"""
336 341 if new == 'inline':
337 342 warnings.warn("'inline' not available as pylab backend, "
338 343 "using 'auto' instead.")
339 344 self.pylab = 'auto'
340 345
341 346 def start(self):
342 347 if self.subapp is not None:
343 348 return self.subapp.start()
344 349 # perform any prexec steps:
345 350 if self.interact:
346 351 self.log.debug("Starting IPython's mainloop...")
347 352 self.shell.mainloop()
348 353 else:
349 354 self.log.debug("IPython not interactive...")
350 355
351 356 def load_default_config(ipython_dir=None):
352 357 """Load the default config file from the default ipython_dir.
353 358
354 359 This is useful for embedded shells.
355 360 """
356 361 if ipython_dir is None:
357 362 ipython_dir = get_ipython_dir()
358 363
359 364 profile_dir = os.path.join(ipython_dir, 'profile_default')
360 365
361 366 config = Config()
362 367 for cf in Application._load_config_files("ipython_config", path=profile_dir):
363 368 config.update(cf)
364 369
365 370 return config
366 371
367 372 launch_new_instance = TerminalIPythonApp.launch_instance
368 373
369 374
370 375 if __name__ == '__main__':
371 376 launch_new_instance()
@@ -1,428 +1,441 b''
1 1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 2 from __future__ import print_function
3 3
4 4 import os
5 5 import sys
6 6 import signal
7 7 from warnings import warn
8 8
9 9 from IPython.core.error import TryNext
10 10 from IPython.core.interactiveshell import InteractiveShell
11 11 from IPython.utils.py3compat import cast_unicode_py2, input
12 12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 13 from IPython.utils.process import abbrev_cwd
14 14 from traitlets import Bool, Unicode, Dict, Integer, observe
15 15
16 16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
18 18 from prompt_toolkit.history import InMemoryHistory
19 19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
20 20 from prompt_toolkit.interface import CommandLineInterface
21 21 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 22 from prompt_toolkit.keys import Keys
23 23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
24 24 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
25 25
26 26 from pygments.styles import get_style_by_name, get_all_styles
27 27 from pygments.token import Token
28 28
29 29 from .debugger import TerminalPdb
30 30 from .pt_inputhooks import get_inputhook_func
31 31 from .interactiveshell import get_default_editor, TerminalMagics
32 32 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 33
34 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
34 35
35 36 class TerminalInteractiveShell(InteractiveShell):
36 37 colors_force = True
37 38
38 39 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
39 40 'to reserve for the completion menu'
40 41 ).tag(config=True)
41 42
42 43 def _space_for_menu_changed(self, old, new):
43 44 self._update_layout()
44 45
45 46 pt_cli = None
46 47 debugger_history = None
47 48 debugger_cls = TerminalPdb
48 49
50 simple_prompt = Bool(_use_simple_prompt,
51 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
52
53 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.
55
56 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
57 environment variable is set, or the current terminal is not a tty.
58
59 """
60 ).tag(config=True)
61
49 62 autoedit_syntax = Bool(False,
50 63 help="auto editing of files with syntax errors.",
51 64 ).tag(config=True)
52 65
53 66
54 67 confirm_exit = Bool(True,
55 68 help="""
56 69 Set to confirm when you try to exit IPython with an EOF (Control-D
57 70 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
58 71 you can force a direct exit without any confirmation.""",
59 72 ).tag(config=True)
60 73
61 74 editing_mode = Unicode('emacs',
62 75 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
63 76 ).tag(config=True)
64 77
65 78 mouse_support = Bool(False,
66 79 help="Enable mouse support in the prompt"
67 80 ).tag(config=True)
68 81
69 82 highlighting_style = Unicode('default',
70 83 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
71 84 ).tag(config=True)
72 85
73 86
74 87 @observe('highlighting_style')
75 88 def _highlighting_style_changed(self, change):
76 89 self._style = self._make_style_from_name(self.highlighting_style)
77 90
78 91 highlighting_style_overrides = Dict(
79 92 help="Override highlighting format for specific tokens"
80 93 ).tag(config=True)
81 94
82 95 editor = Unicode(get_default_editor(),
83 96 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
84 97 ).tag(config=True)
85 98
86 99 term_title = Bool(True,
87 100 help="Automatically set the terminal title"
88 101 ).tag(config=True)
89 102
90 103 display_completions_in_columns = Bool(False,
91 104 help="Display a multi column completion menu.",
92 105 ).tag(config=True)
93 106
94 107 highlight_matching_brackets = Bool(True,
95 108 help="Highlight matching brackets .",
96 109 ).tag(config=True)
97 110
98 111 @observe('term_title')
99 112 def init_term_title(self, change=None):
100 113 # Enable or disable the terminal title.
101 114 if self.term_title:
102 115 toggle_set_term_title(True)
103 116 set_term_title('IPython: ' + abbrev_cwd())
104 117 else:
105 118 toggle_set_term_title(False)
106 119
107 120 def get_prompt_tokens(self, cli):
108 121 return [
109 122 (Token.Prompt, 'In ['),
110 123 (Token.PromptNum, str(self.execution_count)),
111 124 (Token.Prompt, ']: '),
112 125 ]
113 126
114 127 def get_continuation_tokens(self, cli, width):
115 128 return [
116 129 (Token.Prompt, (' ' * (width - 5)) + '...: '),
117 130 ]
118 131
119 132 def init_prompt_toolkit_cli(self):
120 if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty():
133 if self.simple_prompt:
121 134 # Fall back to plain non-interactive output for tests.
122 135 # This is very limited, and only accepts a single line.
123 136 def prompt():
124 137 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
125 138 self.prompt_for_code = prompt
126 139 return
127 140
128 141 kbmanager = KeyBindingManager.for_prompt()
129 142 insert_mode = ViInsertMode() | EmacsInsertMode()
130 143 # Ctrl+J == Enter, seemingly
131 144 @kbmanager.registry.add_binding(Keys.ControlJ,
132 145 filter=(HasFocus(DEFAULT_BUFFER)
133 146 & ~HasSelection()
134 147 & insert_mode
135 148 ))
136 149 def _(event):
137 150 b = event.current_buffer
138 151 d = b.document
139 152 if not (d.on_last_line or d.cursor_position_row >= d.line_count
140 153 - d.empty_line_count_at_the_end()):
141 154 b.newline()
142 155 return
143 156
144 157 status, indent = self.input_splitter.check_complete(d.text)
145 158
146 159 if (status != 'incomplete') and b.accept_action.is_returnable:
147 160 b.accept_action.validate_and_handle(event.cli, b)
148 161 else:
149 162 b.insert_text('\n' + (' ' * (indent or 0)))
150 163
151 164 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
152 165 def _reset_buffer(event):
153 166 event.current_buffer.reset()
154 167
155 168 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
156 169 def _reset_search_buffer(event):
157 170 if event.current_buffer.document.text:
158 171 event.current_buffer.reset()
159 172 else:
160 173 event.cli.push_focus(DEFAULT_BUFFER)
161 174
162 175 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
163 176
164 177 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
165 178 def _suspend_to_bg(event):
166 179 event.cli.suspend_to_background()
167 180
168 181 @Condition
169 182 def cursor_in_leading_ws(cli):
170 183 before = cli.application.buffer.document.current_line_before_cursor
171 184 return (not before) or before.isspace()
172 185
173 186 # Ctrl+I == Tab
174 187 @kbmanager.registry.add_binding(Keys.ControlI,
175 188 filter=(HasFocus(DEFAULT_BUFFER)
176 189 & ~HasSelection()
177 190 & insert_mode
178 191 & cursor_in_leading_ws
179 192 ))
180 193 def _indent_buffer(event):
181 194 event.current_buffer.insert_text(' ' * 4)
182 195
183 196 # Pre-populate history from IPython's history database
184 197 history = InMemoryHistory()
185 198 last_cell = u""
186 199 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
187 200 include_latest=True):
188 201 # Ignore blank lines and consecutive duplicates
189 202 cell = cell.rstrip()
190 203 if cell and (cell != last_cell):
191 204 history.append(cell)
192 205
193 206 self._style = self._make_style_from_name(self.highlighting_style)
194 207 style = DynamicStyle(lambda: self._style)
195 208
196 209 editing_mode = getattr(EditingMode, self.editing_mode.upper())
197 210
198 211 self._app = create_prompt_application(
199 212 editing_mode=editing_mode,
200 213 key_bindings_registry=kbmanager.registry,
201 214 history=history,
202 215 completer=IPythonPTCompleter(self.Completer),
203 216 enable_history_search=True,
204 217 style=style,
205 218 mouse_support=self.mouse_support,
206 219 **self._layout_options()
207 220 )
208 221 self._eventloop = create_eventloop(self.inputhook)
209 222 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
210 223
211 224 def _make_style_from_name(self, name):
212 225 """
213 226 Small wrapper that make an IPython compatible style from a style name
214 227
215 228 We need that to add style for prompt ... etc.
216 229 """
217 230 style_cls = get_style_by_name(name)
218 231 style_overrides = {
219 232 Token.Prompt: '#009900',
220 233 Token.PromptNum: '#00ff00 bold',
221 234 }
222 235 if name == 'default':
223 236 style_cls = get_style_by_name('default')
224 237 # The default theme needs to be visible on both a dark background
225 238 # and a light background, because we can't tell what the terminal
226 239 # looks like. These tweaks to the default theme help with that.
227 240 style_overrides.update({
228 241 Token.Number: '#007700',
229 242 Token.Operator: 'noinherit',
230 243 Token.String: '#BB6622',
231 244 Token.Name.Function: '#2080D0',
232 245 Token.Name.Class: 'bold #2080D0',
233 246 Token.Name.Namespace: 'bold #2080D0',
234 247 })
235 248 style_overrides.update(self.highlighting_style_overrides)
236 249 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
237 250 style_dict=style_overrides)
238 251
239 252 return style
240 253
241 254 def _layout_options(self):
242 255 """
243 256 Return the current layout option for the current Terminal InteractiveShell
244 257 """
245 258 return {
246 259 'lexer':IPythonPTLexer(),
247 260 'reserve_space_for_menu':self.space_for_menu,
248 261 'get_prompt_tokens':self.get_prompt_tokens,
249 262 'get_continuation_tokens':self.get_continuation_tokens,
250 263 'multiline':True,
251 264 'display_completions_in_columns': self.display_completions_in_columns,
252 265
253 266 # Highlight matching brackets, but only when this setting is
254 267 # enabled, and only when the DEFAULT_BUFFER has the focus.
255 268 'extra_input_processors': [ConditionalProcessor(
256 269 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
257 270 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
258 271 Condition(lambda cli: self.highlight_matching_brackets))],
259 272 }
260 273
261 274 def _update_layout(self):
262 275 """
263 276 Ask for a re computation of the application layout, if for example ,
264 277 some configuration options have changed.
265 278 """
266 279 self._app.layout = create_prompt_layout(**self._layout_options())
267 280
268 281 def prompt_for_code(self):
269 282 document = self.pt_cli.run(
270 283 pre_run=self.pre_prompt, reset_current_buffer=True)
271 284 return document.text
272 285
273 286 def init_io(self):
274 287 if sys.platform not in {'win32', 'cli'}:
275 288 return
276 289
277 290 import colorama
278 291 colorama.init()
279 292
280 293 # For some reason we make these wrappers around stdout/stderr.
281 294 # For now, we need to reset them so all output gets coloured.
282 295 # https://github.com/ipython/ipython/issues/8669
283 296 from IPython.utils import io
284 297 io.stdout = io.IOStream(sys.stdout)
285 298 io.stderr = io.IOStream(sys.stderr)
286 299
287 300 def init_magics(self):
288 301 super(TerminalInteractiveShell, self).init_magics()
289 302 self.register_magics(TerminalMagics)
290 303
291 304 def init_alias(self):
292 305 # The parent class defines aliases that can be safely used with any
293 306 # frontend.
294 307 super(TerminalInteractiveShell, self).init_alias()
295 308
296 309 # Now define aliases that only make sense on the terminal, because they
297 310 # need direct access to the console in a way that we can't emulate in
298 311 # GUI or web frontend
299 312 if os.name == 'posix':
300 313 for cmd in ['clear', 'more', 'less', 'man']:
301 314 self.alias_manager.soft_define_alias(cmd, cmd)
302 315
303 316
304 317 def __init__(self, *args, **kwargs):
305 318 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
306 319 self.init_prompt_toolkit_cli()
307 320 self.init_term_title()
308 321 self.keep_running = True
309 322
310 323 self.debugger_history = InMemoryHistory()
311 324
312 325 def ask_exit(self):
313 326 self.keep_running = False
314 327
315 328 rl_next_input = None
316 329
317 330 def pre_prompt(self):
318 331 if self.rl_next_input:
319 332 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
320 333 self.rl_next_input = None
321 334
322 335 def interact(self):
323 336 while self.keep_running:
324 337 print(self.separate_in, end='')
325 338
326 339 try:
327 340 code = self.prompt_for_code()
328 341 except EOFError:
329 342 if (not self.confirm_exit) \
330 343 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
331 344 self.ask_exit()
332 345
333 346 else:
334 347 if code:
335 348 self.run_cell(code, store_history=True)
336 349 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
337 350 self.edit_syntax_error()
338 351
339 352 def mainloop(self):
340 353 # An extra layer of protection in case someone mashing Ctrl-C breaks
341 354 # out of our internal code.
342 355 while True:
343 356 try:
344 357 self.interact()
345 358 break
346 359 except KeyboardInterrupt:
347 360 print("\nKeyboardInterrupt escaped interact()\n")
348 361
349 362 if hasattr(self, '_eventloop'):
350 363 self._eventloop.close()
351 364
352 365 _inputhook = None
353 366 def inputhook(self, context):
354 367 if self._inputhook is not None:
355 368 self._inputhook(context)
356 369
357 370 def enable_gui(self, gui=None):
358 371 if gui:
359 372 self._inputhook = get_inputhook_func(gui)
360 373 else:
361 374 self._inputhook = None
362 375
363 376 # Methods to support auto-editing of SyntaxErrors:
364 377
365 378 def edit_syntax_error(self):
366 379 """The bottom half of the syntax error handler called in the main loop.
367 380
368 381 Loop until syntax error is fixed or user cancels.
369 382 """
370 383
371 384 while self.SyntaxTB.last_syntax_error:
372 385 # copy and clear last_syntax_error
373 386 err = self.SyntaxTB.clear_err_state()
374 387 if not self._should_recompile(err):
375 388 return
376 389 try:
377 390 # may set last_syntax_error again if a SyntaxError is raised
378 391 self.safe_execfile(err.filename, self.user_ns)
379 392 except:
380 393 self.showtraceback()
381 394 else:
382 395 try:
383 396 with open(err.filename) as f:
384 397 # This should be inside a display_trap block and I
385 398 # think it is.
386 399 sys.displayhook(f.read())
387 400 except:
388 401 self.showtraceback()
389 402
390 403 def _should_recompile(self, e):
391 404 """Utility routine for edit_syntax_error"""
392 405
393 406 if e.filename in ('<ipython console>', '<input>', '<string>',
394 407 '<console>', '<BackgroundJob compilation>',
395 408 None):
396 409 return False
397 410 try:
398 411 if (self.autoedit_syntax and
399 412 not self.ask_yes_no(
400 413 'Return to editor to correct syntax error? '
401 414 '[Y/n] ', 'y')):
402 415 return False
403 416 except EOFError:
404 417 return False
405 418
406 419 def int0(x):
407 420 try:
408 421 return int(x)
409 422 except TypeError:
410 423 return 0
411 424
412 425 # always pass integer line and offset values to editor hook
413 426 try:
414 427 self.hooks.fix_error_editor(e.filename,
415 428 int0(e.lineno), int0(e.offset),
416 429 e.msg)
417 430 except TryNext:
418 431 warn('Could not open editor')
419 432 return False
420 433 return True
421 434
422 435 # Run !system commands directly, not through pipes, so terminal programs
423 436 # work correctly.
424 437 system = InteractiveShell.system_raw
425 438
426 439
427 440 if __name__ == '__main__':
428 441 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now