##// END OF EJS Templates
Rename PTInteractiveShell to TerminalInteractiveShell...
Thomas Kluyver -
Show More
@@ -1,368 +1,368 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The :class:`~IPython.core.application.Application` object for the command
4 The :class:`~IPython.core.application.Application` object for the command
5 line :command:`ipython` program.
5 line :command:`ipython` program.
6 """
6 """
7
7
8 # Copyright (c) IPython Development Team.
8 # Copyright (c) IPython Development Team.
9 # Distributed under the terms of the Modified BSD License.
9 # Distributed under the terms of the Modified BSD License.
10
10
11 from __future__ import absolute_import
11 from __future__ import absolute_import
12 from __future__ import print_function
12 from __future__ import print_function
13
13
14 import logging
14 import logging
15 import os
15 import os
16 import sys
16 import sys
17
17
18 from traitlets.config.loader import Config
18 from traitlets.config.loader import Config
19 from traitlets.config.application import boolean_flag, catch_config_error, Application
19 from traitlets.config.application import boolean_flag, catch_config_error, Application
20 from IPython.core import release
20 from IPython.core import release
21 from IPython.core import usage
21 from IPython.core import usage
22 from IPython.core.completer import IPCompleter
22 from IPython.core.completer import IPCompleter
23 from IPython.core.crashhandler import CrashHandler
23 from IPython.core.crashhandler import CrashHandler
24 from IPython.core.formatters import PlainTextFormatter
24 from IPython.core.formatters import PlainTextFormatter
25 from IPython.core.history import HistoryManager
25 from IPython.core.history import HistoryManager
26 from IPython.core.prompts import PromptManager
26 from IPython.core.prompts import PromptManager
27 from IPython.core.application import (
27 from IPython.core.application import (
28 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
28 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
29 )
29 )
30 from IPython.core.magics import ScriptMagics
30 from IPython.core.magics import ScriptMagics
31 from IPython.core.shellapp import (
31 from IPython.core.shellapp import (
32 InteractiveShellApp, shell_flags, shell_aliases
32 InteractiveShellApp, shell_flags, shell_aliases
33 )
33 )
34 from IPython.extensions.storemagic import StoreMagics
34 from IPython.extensions.storemagic import StoreMagics
35 from .ptshell import PTInteractiveShell as TerminalInteractiveShell
35 from .ptshell import TerminalInteractiveShell
36 from IPython.utils import warn
36 from IPython.utils import warn
37 from IPython.paths import get_ipython_dir
37 from IPython.paths import get_ipython_dir
38 from traitlets import (
38 from traitlets import (
39 Bool, List, Dict,
39 Bool, List, Dict,
40 )
40 )
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Globals, utilities and helpers
43 # Globals, utilities and helpers
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 _examples = """
46 _examples = """
47 ipython --matplotlib # enable matplotlib integration
47 ipython --matplotlib # enable matplotlib integration
48 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
48 ipython --matplotlib=qt # enable matplotlib integration with qt4 backend
49
49
50 ipython --log-level=DEBUG # set logging to DEBUG
50 ipython --log-level=DEBUG # set logging to DEBUG
51 ipython --profile=foo # start with profile foo
51 ipython --profile=foo # start with profile foo
52
52
53 ipython profile create foo # create profile foo w/ default config files
53 ipython profile create foo # create profile foo w/ default config files
54 ipython help profile # show the help for the profile subcmd
54 ipython help profile # show the help for the profile subcmd
55
55
56 ipython locate # print the path to the IPython directory
56 ipython locate # print the path to the IPython directory
57 ipython locate profile foo # print the path to the directory for profile `foo`
57 ipython locate profile foo # print the path to the directory for profile `foo`
58 """
58 """
59
59
60 #-----------------------------------------------------------------------------
60 #-----------------------------------------------------------------------------
61 # Crash handler for this application
61 # Crash handler for this application
62 #-----------------------------------------------------------------------------
62 #-----------------------------------------------------------------------------
63
63
64 class IPAppCrashHandler(CrashHandler):
64 class IPAppCrashHandler(CrashHandler):
65 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
65 """sys.excepthook for IPython itself, leaves a detailed report on disk."""
66
66
67 def __init__(self, app):
67 def __init__(self, app):
68 contact_name = release.author
68 contact_name = release.author
69 contact_email = release.author_email
69 contact_email = release.author_email
70 bug_tracker = 'https://github.com/ipython/ipython/issues'
70 bug_tracker = 'https://github.com/ipython/ipython/issues'
71 super(IPAppCrashHandler,self).__init__(
71 super(IPAppCrashHandler,self).__init__(
72 app, contact_name, contact_email, bug_tracker
72 app, contact_name, contact_email, bug_tracker
73 )
73 )
74
74
75 def make_report(self,traceback):
75 def make_report(self,traceback):
76 """Return a string containing a crash report."""
76 """Return a string containing a crash report."""
77
77
78 sec_sep = self.section_sep
78 sec_sep = self.section_sep
79 # Start with parent report
79 # Start with parent report
80 report = [super(IPAppCrashHandler, self).make_report(traceback)]
80 report = [super(IPAppCrashHandler, self).make_report(traceback)]
81 # Add interactive-specific info we may have
81 # Add interactive-specific info we may have
82 rpt_add = report.append
82 rpt_add = report.append
83 try:
83 try:
84 rpt_add(sec_sep+"History of session input:")
84 rpt_add(sec_sep+"History of session input:")
85 for line in self.app.shell.user_ns['_ih']:
85 for line in self.app.shell.user_ns['_ih']:
86 rpt_add(line)
86 rpt_add(line)
87 rpt_add('\n*** Last line of input (may not be in above history):\n')
87 rpt_add('\n*** Last line of input (may not be in above history):\n')
88 rpt_add(self.app.shell._last_input_line+'\n')
88 rpt_add(self.app.shell._last_input_line+'\n')
89 except:
89 except:
90 pass
90 pass
91
91
92 return ''.join(report)
92 return ''.join(report)
93
93
94 #-----------------------------------------------------------------------------
94 #-----------------------------------------------------------------------------
95 # Aliases and Flags
95 # Aliases and Flags
96 #-----------------------------------------------------------------------------
96 #-----------------------------------------------------------------------------
97 flags = dict(base_flags)
97 flags = dict(base_flags)
98 flags.update(shell_flags)
98 flags.update(shell_flags)
99 frontend_flags = {}
99 frontend_flags = {}
100 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
100 addflag = lambda *args: frontend_flags.update(boolean_flag(*args))
101 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
101 addflag('autoedit-syntax', 'TerminalInteractiveShell.autoedit_syntax',
102 'Turn on auto editing of files with syntax errors.',
102 'Turn on auto editing of files with syntax errors.',
103 'Turn off auto editing of files with syntax errors.'
103 'Turn off auto editing of files with syntax errors.'
104 )
104 )
105 addflag('banner', 'TerminalIPythonApp.display_banner',
105 addflag('banner', 'TerminalIPythonApp.display_banner',
106 "Display a banner upon starting IPython.",
106 "Display a banner upon starting IPython.",
107 "Don't display a banner upon starting IPython."
107 "Don't display a banner upon starting IPython."
108 )
108 )
109 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
109 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
110 """Set to confirm when you try to exit IPython with an EOF (Control-D
110 """Set to confirm when you try to exit IPython with an EOF (Control-D
111 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
111 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
112 you can force a direct exit without any confirmation.""",
112 you can force a direct exit without any confirmation.""",
113 "Don't prompt the user when exiting."
113 "Don't prompt the user when exiting."
114 )
114 )
115 addflag('term-title', 'TerminalInteractiveShell.term_title',
115 addflag('term-title', 'TerminalInteractiveShell.term_title',
116 "Enable auto setting the terminal title.",
116 "Enable auto setting the terminal title.",
117 "Disable auto setting the terminal title."
117 "Disable auto setting the terminal title."
118 )
118 )
119 classic_config = Config()
119 classic_config = Config()
120 classic_config.InteractiveShell.cache_size = 0
120 classic_config.InteractiveShell.cache_size = 0
121 classic_config.PlainTextFormatter.pprint = False
121 classic_config.PlainTextFormatter.pprint = False
122 classic_config.PromptManager.in_template = '>>> '
122 classic_config.PromptManager.in_template = '>>> '
123 classic_config.PromptManager.in2_template = '... '
123 classic_config.PromptManager.in2_template = '... '
124 classic_config.PromptManager.out_template = ''
124 classic_config.PromptManager.out_template = ''
125 classic_config.InteractiveShell.separate_in = ''
125 classic_config.InteractiveShell.separate_in = ''
126 classic_config.InteractiveShell.separate_out = ''
126 classic_config.InteractiveShell.separate_out = ''
127 classic_config.InteractiveShell.separate_out2 = ''
127 classic_config.InteractiveShell.separate_out2 = ''
128 classic_config.InteractiveShell.colors = 'NoColor'
128 classic_config.InteractiveShell.colors = 'NoColor'
129 classic_config.InteractiveShell.xmode = 'Plain'
129 classic_config.InteractiveShell.xmode = 'Plain'
130
130
131 frontend_flags['classic']=(
131 frontend_flags['classic']=(
132 classic_config,
132 classic_config,
133 "Gives IPython a similar feel to the classic Python prompt."
133 "Gives IPython a similar feel to the classic Python prompt."
134 )
134 )
135 # # log doesn't make so much sense this way anymore
135 # # log doesn't make so much sense this way anymore
136 # paa('--log','-l',
136 # paa('--log','-l',
137 # action='store_true', dest='InteractiveShell.logstart',
137 # action='store_true', dest='InteractiveShell.logstart',
138 # help="Start logging to the default log file (./ipython_log.py).")
138 # help="Start logging to the default log file (./ipython_log.py).")
139 #
139 #
140 # # quick is harder to implement
140 # # quick is harder to implement
141 frontend_flags['quick']=(
141 frontend_flags['quick']=(
142 {'TerminalIPythonApp' : {'quick' : True}},
142 {'TerminalIPythonApp' : {'quick' : True}},
143 "Enable quick startup with no config files."
143 "Enable quick startup with no config files."
144 )
144 )
145
145
146 frontend_flags['i'] = (
146 frontend_flags['i'] = (
147 {'TerminalIPythonApp' : {'force_interact' : True}},
147 {'TerminalIPythonApp' : {'force_interact' : True}},
148 """If running code from the command line, become interactive afterwards.
148 """If running code from the command line, become interactive afterwards.
149 It is often useful to follow this with `--` to treat remaining flags as
149 It is often useful to follow this with `--` to treat remaining flags as
150 script arguments.
150 script arguments.
151 """
151 """
152 )
152 )
153 flags.update(frontend_flags)
153 flags.update(frontend_flags)
154
154
155 aliases = dict(base_aliases)
155 aliases = dict(base_aliases)
156 aliases.update(shell_aliases)
156 aliases.update(shell_aliases)
157
157
158 #-----------------------------------------------------------------------------
158 #-----------------------------------------------------------------------------
159 # Main classes and functions
159 # Main classes and functions
160 #-----------------------------------------------------------------------------
160 #-----------------------------------------------------------------------------
161
161
162
162
163 class LocateIPythonApp(BaseIPythonApplication):
163 class LocateIPythonApp(BaseIPythonApplication):
164 description = """print the path to the IPython dir"""
164 description = """print the path to the IPython dir"""
165 subcommands = Dict(dict(
165 subcommands = Dict(dict(
166 profile=('IPython.core.profileapp.ProfileLocate',
166 profile=('IPython.core.profileapp.ProfileLocate',
167 "print the path to an IPython profile directory",
167 "print the path to an IPython profile directory",
168 ),
168 ),
169 ))
169 ))
170 def start(self):
170 def start(self):
171 if self.subapp is not None:
171 if self.subapp is not None:
172 return self.subapp.start()
172 return self.subapp.start()
173 else:
173 else:
174 print(self.ipython_dir)
174 print(self.ipython_dir)
175
175
176
176
177 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
177 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
178 name = u'ipython'
178 name = u'ipython'
179 description = usage.cl_usage
179 description = usage.cl_usage
180 crash_handler_class = IPAppCrashHandler
180 crash_handler_class = IPAppCrashHandler
181 examples = _examples
181 examples = _examples
182
182
183 flags = Dict(flags)
183 flags = Dict(flags)
184 aliases = Dict(aliases)
184 aliases = Dict(aliases)
185 classes = List()
185 classes = List()
186 def _classes_default(self):
186 def _classes_default(self):
187 """This has to be in a method, for TerminalIPythonApp to be available."""
187 """This has to be in a method, for TerminalIPythonApp to be available."""
188 return [
188 return [
189 InteractiveShellApp, # ShellApp comes before TerminalApp, because
189 InteractiveShellApp, # ShellApp comes before TerminalApp, because
190 self.__class__, # it will also affect subclasses (e.g. QtConsole)
190 self.__class__, # it will also affect subclasses (e.g. QtConsole)
191 TerminalInteractiveShell,
191 TerminalInteractiveShell,
192 PromptManager,
192 PromptManager,
193 HistoryManager,
193 HistoryManager,
194 ProfileDir,
194 ProfileDir,
195 PlainTextFormatter,
195 PlainTextFormatter,
196 IPCompleter,
196 IPCompleter,
197 ScriptMagics,
197 ScriptMagics,
198 StoreMagics,
198 StoreMagics,
199 ]
199 ]
200
200
201 deprecated_subcommands = dict(
201 deprecated_subcommands = dict(
202 qtconsole=('qtconsole.qtconsoleapp.JupyterQtConsoleApp',
202 qtconsole=('qtconsole.qtconsoleapp.JupyterQtConsoleApp',
203 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter Qt Console."""
203 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter Qt Console."""
204 ),
204 ),
205 notebook=('notebook.notebookapp.NotebookApp',
205 notebook=('notebook.notebookapp.NotebookApp',
206 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter HTML Notebook Server."""
206 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter HTML Notebook Server."""
207 ),
207 ),
208 console=('jupyter_console.app.ZMQTerminalIPythonApp',
208 console=('jupyter_console.app.ZMQTerminalIPythonApp',
209 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter terminal-based Console."""
209 """DEPRECATED, Will be removed in IPython 6.0 : Launch the Jupyter terminal-based Console."""
210 ),
210 ),
211 nbconvert=('nbconvert.nbconvertapp.NbConvertApp',
211 nbconvert=('nbconvert.nbconvertapp.NbConvertApp',
212 "DEPRECATED, Will be removed in IPython 6.0 : Convert notebooks to/from other formats."
212 "DEPRECATED, Will be removed in IPython 6.0 : Convert notebooks to/from other formats."
213 ),
213 ),
214 trust=('nbformat.sign.TrustNotebookApp',
214 trust=('nbformat.sign.TrustNotebookApp',
215 "DEPRECATED, Will be removed in IPython 6.0 : Sign notebooks to trust their potentially unsafe contents at load."
215 "DEPRECATED, Will be removed in IPython 6.0 : Sign notebooks to trust their potentially unsafe contents at load."
216 ),
216 ),
217 kernelspec=('jupyter_client.kernelspecapp.KernelSpecApp',
217 kernelspec=('jupyter_client.kernelspecapp.KernelSpecApp',
218 "DEPRECATED, Will be removed in IPython 6.0 : Manage Jupyter kernel specifications."
218 "DEPRECATED, Will be removed in IPython 6.0 : Manage Jupyter kernel specifications."
219 ),
219 ),
220 )
220 )
221 subcommands = dict(
221 subcommands = dict(
222 profile = ("IPython.core.profileapp.ProfileApp",
222 profile = ("IPython.core.profileapp.ProfileApp",
223 "Create and manage IPython profiles."
223 "Create and manage IPython profiles."
224 ),
224 ),
225 kernel = ("ipykernel.kernelapp.IPKernelApp",
225 kernel = ("ipykernel.kernelapp.IPKernelApp",
226 "Start a kernel without an attached frontend."
226 "Start a kernel without an attached frontend."
227 ),
227 ),
228 locate=('IPython.terminal.ipapp.LocateIPythonApp',
228 locate=('IPython.terminal.ipapp.LocateIPythonApp',
229 LocateIPythonApp.description
229 LocateIPythonApp.description
230 ),
230 ),
231 history=('IPython.core.historyapp.HistoryApp',
231 history=('IPython.core.historyapp.HistoryApp',
232 "Manage the IPython history database."
232 "Manage the IPython history database."
233 ),
233 ),
234 )
234 )
235 deprecated_subcommands['install-nbextension'] = (
235 deprecated_subcommands['install-nbextension'] = (
236 "notebook.nbextensions.InstallNBExtensionApp",
236 "notebook.nbextensions.InstallNBExtensionApp",
237 "DEPRECATED, Will be removed in IPython 6.0 : Install Jupyter notebook extension files"
237 "DEPRECATED, Will be removed in IPython 6.0 : Install Jupyter notebook extension files"
238 )
238 )
239 subcommands.update(deprecated_subcommands)
239 subcommands.update(deprecated_subcommands)
240
240
241 # *do* autocreate requested profile, but don't create the config file.
241 # *do* autocreate requested profile, but don't create the config file.
242 auto_create=Bool(True)
242 auto_create=Bool(True)
243 # configurables
243 # configurables
244 quick = Bool(False, config=True,
244 quick = Bool(False, config=True,
245 help="""Start IPython quickly by skipping the loading of config files."""
245 help="""Start IPython quickly by skipping the loading of config files."""
246 )
246 )
247 def _quick_changed(self, name, old, new):
247 def _quick_changed(self, name, old, new):
248 if new:
248 if new:
249 self.load_config_file = lambda *a, **kw: None
249 self.load_config_file = lambda *a, **kw: None
250
250
251 display_banner = Bool(True, config=True,
251 display_banner = Bool(True, config=True,
252 help="Whether to display a banner upon starting IPython."
252 help="Whether to display a banner upon starting IPython."
253 )
253 )
254
254
255 # if there is code of files to run from the cmd line, don't interact
255 # if there is code of files to run from the cmd line, don't interact
256 # unless the --i flag (App.force_interact) is true.
256 # unless the --i flag (App.force_interact) is true.
257 force_interact = Bool(False, config=True,
257 force_interact = Bool(False, config=True,
258 help="""If a command or file is given via the command-line,
258 help="""If a command or file is given via the command-line,
259 e.g. 'ipython foo.py', start an interactive shell after executing the
259 e.g. 'ipython foo.py', start an interactive shell after executing the
260 file or command."""
260 file or command."""
261 )
261 )
262 def _force_interact_changed(self, name, old, new):
262 def _force_interact_changed(self, name, old, new):
263 if new:
263 if new:
264 self.interact = True
264 self.interact = True
265
265
266 def _file_to_run_changed(self, name, old, new):
266 def _file_to_run_changed(self, name, old, new):
267 if new:
267 if new:
268 self.something_to_run = True
268 self.something_to_run = True
269 if new and not self.force_interact:
269 if new and not self.force_interact:
270 self.interact = False
270 self.interact = False
271 _code_to_run_changed = _file_to_run_changed
271 _code_to_run_changed = _file_to_run_changed
272 _module_to_run_changed = _file_to_run_changed
272 _module_to_run_changed = _file_to_run_changed
273
273
274 # internal, not-configurable
274 # internal, not-configurable
275 something_to_run=Bool(False)
275 something_to_run=Bool(False)
276
276
277 def parse_command_line(self, argv=None):
277 def parse_command_line(self, argv=None):
278 """override to allow old '-pylab' flag with deprecation warning"""
278 """override to allow old '-pylab' flag with deprecation warning"""
279
279
280 argv = sys.argv[1:] if argv is None else argv
280 argv = sys.argv[1:] if argv is None else argv
281
281
282 if '-pylab' in argv:
282 if '-pylab' in argv:
283 # deprecated `-pylab` given,
283 # deprecated `-pylab` given,
284 # warn and transform into current syntax
284 # warn and transform into current syntax
285 argv = argv[:] # copy, don't clobber
285 argv = argv[:] # copy, don't clobber
286 idx = argv.index('-pylab')
286 idx = argv.index('-pylab')
287 warn.warn("`-pylab` flag has been deprecated.\n"
287 warn.warn("`-pylab` flag has been deprecated.\n"
288 " Use `--matplotlib <backend>` and import pylab manually.")
288 " Use `--matplotlib <backend>` and import pylab manually.")
289 argv[idx] = '--pylab'
289 argv[idx] = '--pylab'
290
290
291 return super(TerminalIPythonApp, self).parse_command_line(argv)
291 return super(TerminalIPythonApp, self).parse_command_line(argv)
292
292
293 @catch_config_error
293 @catch_config_error
294 def initialize(self, argv=None):
294 def initialize(self, argv=None):
295 """Do actions after construct, but before starting the app."""
295 """Do actions after construct, but before starting the app."""
296 super(TerminalIPythonApp, self).initialize(argv)
296 super(TerminalIPythonApp, self).initialize(argv)
297 if self.subapp is not None:
297 if self.subapp is not None:
298 # don't bother initializing further, starting subapp
298 # don't bother initializing further, starting subapp
299 return
299 return
300 # print self.extra_args
300 # print self.extra_args
301 if self.extra_args and not self.something_to_run:
301 if self.extra_args and not self.something_to_run:
302 self.file_to_run = self.extra_args[0]
302 self.file_to_run = self.extra_args[0]
303 self.init_path()
303 self.init_path()
304 # create the shell
304 # create the shell
305 self.init_shell()
305 self.init_shell()
306 # and draw the banner
306 # and draw the banner
307 self.init_banner()
307 self.init_banner()
308 # Now a variety of things that happen after the banner is printed.
308 # Now a variety of things that happen after the banner is printed.
309 self.init_gui_pylab()
309 self.init_gui_pylab()
310 self.init_extensions()
310 self.init_extensions()
311 self.init_code()
311 self.init_code()
312
312
313 def init_shell(self):
313 def init_shell(self):
314 """initialize the InteractiveShell instance"""
314 """initialize the InteractiveShell instance"""
315 # Create an InteractiveShell instance.
315 # Create an InteractiveShell instance.
316 # shell.display_banner should always be False for the terminal
316 # shell.display_banner should always be False for the terminal
317 # based app, because we call shell.show_banner() by hand below
317 # based app, because we call shell.show_banner() by hand below
318 # so the banner shows *before* all extension loading stuff.
318 # so the banner shows *before* all extension loading stuff.
319 self.shell = TerminalInteractiveShell.instance(parent=self,
319 self.shell = TerminalInteractiveShell.instance(parent=self,
320 display_banner=False, profile_dir=self.profile_dir,
320 display_banner=False, profile_dir=self.profile_dir,
321 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
321 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
322 self.shell.configurables.append(self)
322 self.shell.configurables.append(self)
323
323
324 def init_banner(self):
324 def init_banner(self):
325 """optionally display the banner"""
325 """optionally display the banner"""
326 if self.display_banner and self.interact:
326 if self.display_banner and self.interact:
327 self.shell.show_banner()
327 self.shell.show_banner()
328 # Make sure there is a space below the banner.
328 # Make sure there is a space below the banner.
329 if self.log_level <= logging.INFO: print()
329 if self.log_level <= logging.INFO: print()
330
330
331 def _pylab_changed(self, name, old, new):
331 def _pylab_changed(self, name, old, new):
332 """Replace --pylab='inline' with --pylab='auto'"""
332 """Replace --pylab='inline' with --pylab='auto'"""
333 if new == 'inline':
333 if new == 'inline':
334 warn.warn("'inline' not available as pylab backend, "
334 warn.warn("'inline' not available as pylab backend, "
335 "using 'auto' instead.")
335 "using 'auto' instead.")
336 self.pylab = 'auto'
336 self.pylab = 'auto'
337
337
338 def start(self):
338 def start(self):
339 if self.subapp is not None:
339 if self.subapp is not None:
340 return self.subapp.start()
340 return self.subapp.start()
341 # perform any prexec steps:
341 # perform any prexec steps:
342 if self.interact:
342 if self.interact:
343 self.log.debug("Starting IPython's mainloop...")
343 self.log.debug("Starting IPython's mainloop...")
344 self.shell.mainloop()
344 self.shell.mainloop()
345 else:
345 else:
346 self.log.debug("IPython not interactive...")
346 self.log.debug("IPython not interactive...")
347
347
348 def load_default_config(ipython_dir=None):
348 def load_default_config(ipython_dir=None):
349 """Load the default config file from the default ipython_dir.
349 """Load the default config file from the default ipython_dir.
350
350
351 This is useful for embedded shells.
351 This is useful for embedded shells.
352 """
352 """
353 if ipython_dir is None:
353 if ipython_dir is None:
354 ipython_dir = get_ipython_dir()
354 ipython_dir = get_ipython_dir()
355
355
356 profile_dir = os.path.join(ipython_dir, 'profile_default')
356 profile_dir = os.path.join(ipython_dir, 'profile_default')
357
357
358 config = Config()
358 config = Config()
359 for cf in Application._load_config_files("ipython_config", path=profile_dir):
359 for cf in Application._load_config_files("ipython_config", path=profile_dir):
360 config.update(cf)
360 config.update(cf)
361
361
362 return config
362 return config
363
363
364 launch_new_instance = TerminalIPythonApp.launch_instance
364 launch_new_instance = TerminalIPythonApp.launch_instance
365
365
366
366
367 if __name__ == '__main__':
367 if __name__ == '__main__':
368 launch_new_instance()
368 launch_new_instance()
@@ -1,240 +1,240 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 sys
4 import sys
5
5
6 from IPython.core.interactiveshell import InteractiveShell
6 from IPython.core.interactiveshell import InteractiveShell
7 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
7 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
8 from traitlets import Bool, Unicode, Dict
8 from traitlets import Bool, Unicode, Dict
9
9
10 from prompt_toolkit.completion import Completer, Completion
10 from prompt_toolkit.completion import Completer, Completion
11 from prompt_toolkit.enums import DEFAULT_BUFFER
11 from prompt_toolkit.enums import DEFAULT_BUFFER
12 from prompt_toolkit.filters import HasFocus, HasSelection
12 from prompt_toolkit.filters import HasFocus, HasSelection
13 from prompt_toolkit.history import InMemoryHistory
13 from prompt_toolkit.history import InMemoryHistory
14 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
14 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
15 from prompt_toolkit.interface import CommandLineInterface
15 from prompt_toolkit.interface import CommandLineInterface
16 from prompt_toolkit.key_binding.manager import KeyBindingManager
16 from prompt_toolkit.key_binding.manager import KeyBindingManager
17 from prompt_toolkit.key_binding.vi_state import InputMode
17 from prompt_toolkit.key_binding.vi_state import InputMode
18 from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
18 from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
19 from prompt_toolkit.keys import Keys
19 from prompt_toolkit.keys import Keys
20 from prompt_toolkit.layout.lexers import PygmentsLexer
20 from prompt_toolkit.layout.lexers import PygmentsLexer
21 from prompt_toolkit.styles import PygmentsStyle
21 from prompt_toolkit.styles import PygmentsStyle
22
22
23 from pygments.styles import get_style_by_name
23 from pygments.styles import get_style_by_name
24 from pygments.lexers import Python3Lexer, PythonLexer
24 from pygments.lexers import Python3Lexer, PythonLexer
25 from pygments.token import Token
25 from pygments.token import Token
26
26
27 from .pt_inputhooks import get_inputhook_func
27 from .pt_inputhooks import get_inputhook_func
28 from .interactiveshell import get_default_editor
28 from .interactiveshell import get_default_editor
29
29
30
30
31 class IPythonPTCompleter(Completer):
31 class IPythonPTCompleter(Completer):
32 """Adaptor to provide IPython completions to prompt_toolkit"""
32 """Adaptor to provide IPython completions to prompt_toolkit"""
33 def __init__(self, ipy_completer):
33 def __init__(self, ipy_completer):
34 self.ipy_completer = ipy_completer
34 self.ipy_completer = ipy_completer
35
35
36 def get_completions(self, document, complete_event):
36 def get_completions(self, document, complete_event):
37 if not document.current_line.strip():
37 if not document.current_line.strip():
38 return
38 return
39
39
40 used, matches = self.ipy_completer.complete(
40 used, matches = self.ipy_completer.complete(
41 line_buffer=document.current_line,
41 line_buffer=document.current_line,
42 cursor_pos=document.cursor_position_col
42 cursor_pos=document.cursor_position_col
43 )
43 )
44 start_pos = -len(used)
44 start_pos = -len(used)
45 for m in matches:
45 for m in matches:
46 yield Completion(m, start_position=start_pos)
46 yield Completion(m, start_position=start_pos)
47
47
48 class PTInteractiveShell(InteractiveShell):
48 class TerminalInteractiveShell(InteractiveShell):
49 colors_force = True
49 colors_force = True
50
50
51 pt_cli = None
51 pt_cli = None
52
52
53 vi_mode = Bool(False, config=True,
53 vi_mode = Bool(False, config=True,
54 help="Use vi style keybindings at the prompt",
54 help="Use vi style keybindings at the prompt",
55 )
55 )
56
56
57 mouse_support = Bool(False, config=True,
57 mouse_support = Bool(False, config=True,
58 help="Enable mouse support in the prompt"
58 help="Enable mouse support in the prompt"
59 )
59 )
60
60
61 highlighting_style = Unicode('', config=True,
61 highlighting_style = Unicode('', config=True,
62 help="The name of a Pygments style to use for syntax highlighting"
62 help="The name of a Pygments style to use for syntax highlighting"
63 )
63 )
64
64
65 highlighting_style_overrides = Dict(config=True,
65 highlighting_style_overrides = Dict(config=True,
66 help="Override highlighting format for specific tokens"
66 help="Override highlighting format for specific tokens"
67 )
67 )
68
68
69 editor = Unicode(get_default_editor(), config=True,
69 editor = Unicode(get_default_editor(), config=True,
70 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
70 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
71 )
71 )
72
72
73 def get_prompt_tokens(self, cli):
73 def get_prompt_tokens(self, cli):
74 return [
74 return [
75 (Token.Prompt, 'In ['),
75 (Token.Prompt, 'In ['),
76 (Token.PromptNum, str(self.execution_count)),
76 (Token.PromptNum, str(self.execution_count)),
77 (Token.Prompt, ']: '),
77 (Token.Prompt, ']: '),
78 ]
78 ]
79
79
80 def get_continuation_tokens(self, cli, width):
80 def get_continuation_tokens(self, cli, width):
81 return [
81 return [
82 (Token.Prompt, (' ' * (width - 2)) + ': '),
82 (Token.Prompt, (' ' * (width - 2)) + ': '),
83 ]
83 ]
84
84
85 def init_prompt_toolkit_cli(self):
85 def init_prompt_toolkit_cli(self):
86 if not sys.stdin.isatty():
86 if not sys.stdin.isatty():
87 # Piped input - e.g. for tests. Fall back to plain non-interactive
87 # Piped input - e.g. for tests. Fall back to plain non-interactive
88 # output. This is very limited, and only accepts a single line.
88 # output. This is very limited, and only accepts a single line.
89 def prompt():
89 def prompt():
90 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
90 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
91 self.prompt_for_code = prompt
91 self.prompt_for_code = prompt
92 return
92 return
93
93
94 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
94 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
95 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
95 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
96 # Ctrl+J == Enter, seemingly
96 # Ctrl+J == Enter, seemingly
97 @kbmanager.registry.add_binding(Keys.ControlJ,
97 @kbmanager.registry.add_binding(Keys.ControlJ,
98 filter=(HasFocus(DEFAULT_BUFFER)
98 filter=(HasFocus(DEFAULT_BUFFER)
99 & ~HasSelection()
99 & ~HasSelection()
100 & insert_mode
100 & insert_mode
101 ))
101 ))
102 def _(event):
102 def _(event):
103 b = event.current_buffer
103 b = event.current_buffer
104 d = b.document
104 d = b.document
105 if not (d.on_last_line or d.cursor_position_row >= d.line_count
105 if not (d.on_last_line or d.cursor_position_row >= d.line_count
106 - d.empty_line_count_at_the_end()):
106 - d.empty_line_count_at_the_end()):
107 b.newline()
107 b.newline()
108 return
108 return
109
109
110 status, indent = self.input_splitter.check_complete(d.text)
110 status, indent = self.input_splitter.check_complete(d.text)
111
111
112 if (status != 'incomplete') and b.accept_action.is_returnable:
112 if (status != 'incomplete') and b.accept_action.is_returnable:
113 b.accept_action.validate_and_handle(event.cli, b)
113 b.accept_action.validate_and_handle(event.cli, b)
114 else:
114 else:
115 b.insert_text('\n' + (' ' * (indent or 0)))
115 b.insert_text('\n' + (' ' * (indent or 0)))
116
116
117 @kbmanager.registry.add_binding(Keys.ControlC)
117 @kbmanager.registry.add_binding(Keys.ControlC)
118 def _(event):
118 def _(event):
119 event.current_buffer.reset()
119 event.current_buffer.reset()
120
120
121 # Pre-populate history from IPython's history database
121 # Pre-populate history from IPython's history database
122 history = InMemoryHistory()
122 history = InMemoryHistory()
123 last_cell = u""
123 last_cell = u""
124 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
124 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
125 include_latest=True):
125 include_latest=True):
126 # Ignore blank lines and consecutive duplicates
126 # Ignore blank lines and consecutive duplicates
127 cell = cell.rstrip()
127 cell = cell.rstrip()
128 if cell and (cell != last_cell):
128 if cell and (cell != last_cell):
129 history.append(cell)
129 history.append(cell)
130
130
131 style_overrides = {
131 style_overrides = {
132 Token.Prompt: '#009900',
132 Token.Prompt: '#009900',
133 Token.PromptNum: '#00ff00 bold',
133 Token.PromptNum: '#00ff00 bold',
134 }
134 }
135 if self.highlighting_style:
135 if self.highlighting_style:
136 style_cls = get_style_by_name(self.highlighting_style)
136 style_cls = get_style_by_name(self.highlighting_style)
137 else:
137 else:
138 style_cls = get_style_by_name('default')
138 style_cls = get_style_by_name('default')
139 # The default theme needs to be visible on both a dark background
139 # The default theme needs to be visible on both a dark background
140 # and a light background, because we can't tell what the terminal
140 # and a light background, because we can't tell what the terminal
141 # looks like. These tweaks to the default theme help with that.
141 # looks like. These tweaks to the default theme help with that.
142 style_overrides.update({
142 style_overrides.update({
143 Token.Number: '#007700',
143 Token.Number: '#007700',
144 Token.Operator: 'noinherit',
144 Token.Operator: 'noinherit',
145 Token.String: '#BB6622',
145 Token.String: '#BB6622',
146 Token.Name.Function: '#2080D0',
146 Token.Name.Function: '#2080D0',
147 Token.Name.Class: 'bold #2080D0',
147 Token.Name.Class: 'bold #2080D0',
148 Token.Name.Namespace: 'bold #2080D0',
148 Token.Name.Namespace: 'bold #2080D0',
149 })
149 })
150 style_overrides.update(self.highlighting_style_overrides)
150 style_overrides.update(self.highlighting_style_overrides)
151 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
151 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
152 style_dict=style_overrides)
152 style_dict=style_overrides)
153
153
154 app = create_prompt_application(multiline=True,
154 app = create_prompt_application(multiline=True,
155 lexer=PygmentsLexer(Python3Lexer if PY3 else PythonLexer),
155 lexer=PygmentsLexer(Python3Lexer if PY3 else PythonLexer),
156 get_prompt_tokens=self.get_prompt_tokens,
156 get_prompt_tokens=self.get_prompt_tokens,
157 # The line below is waiting for a new release of
157 # The line below is waiting for a new release of
158 # prompt_toolkit (> 0.57)
158 # prompt_toolkit (> 0.57)
159 #get_continuation_tokens=self.get_continuation_tokens,
159 #get_continuation_tokens=self.get_continuation_tokens,
160 key_bindings_registry=kbmanager.registry,
160 key_bindings_registry=kbmanager.registry,
161 history=history,
161 history=history,
162 completer=IPythonPTCompleter(self.Completer),
162 completer=IPythonPTCompleter(self.Completer),
163 enable_history_search=True,
163 enable_history_search=True,
164 style=style,
164 style=style,
165 mouse_support=self.mouse_support,
165 mouse_support=self.mouse_support,
166 )
166 )
167
167
168 self.pt_cli = CommandLineInterface(app,
168 self.pt_cli = CommandLineInterface(app,
169 eventloop=create_eventloop(self.inputhook))
169 eventloop=create_eventloop(self.inputhook))
170
170
171 def prompt_for_code(self):
171 def prompt_for_code(self):
172 document = self.pt_cli.run(pre_run=self.pre_prompt)
172 document = self.pt_cli.run(pre_run=self.pre_prompt)
173 return document.text
173 return document.text
174
174
175 def init_io(self):
175 def init_io(self):
176 if sys.platform not in {'win32', 'cli'}:
176 if sys.platform not in {'win32', 'cli'}:
177 return
177 return
178
178
179 import colorama
179 import colorama
180 colorama.init()
180 colorama.init()
181
181
182 # For some reason we make these wrappers around stdout/stderr.
182 # For some reason we make these wrappers around stdout/stderr.
183 # For now, we need to reset them so all output gets coloured.
183 # For now, we need to reset them so all output gets coloured.
184 # https://github.com/ipython/ipython/issues/8669
184 # https://github.com/ipython/ipython/issues/8669
185 from IPython.utils import io
185 from IPython.utils import io
186 io.stdout = io.IOStream(sys.stdout)
186 io.stdout = io.IOStream(sys.stdout)
187 io.stderr = io.IOStream(sys.stderr)
187 io.stderr = io.IOStream(sys.stderr)
188
188
189 def __init__(self, *args, **kwargs):
189 def __init__(self, *args, **kwargs):
190 super(PTInteractiveShell, self).__init__(*args, **kwargs)
190 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
191 self.init_prompt_toolkit_cli()
191 self.init_prompt_toolkit_cli()
192 self.keep_running = True
192 self.keep_running = True
193
193
194 def ask_exit(self):
194 def ask_exit(self):
195 self.keep_running = False
195 self.keep_running = False
196
196
197 rl_next_input = None
197 rl_next_input = None
198
198
199 def pre_prompt(self):
199 def pre_prompt(self):
200 if self.rl_next_input:
200 if self.rl_next_input:
201 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
201 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
202 self.rl_next_input = None
202 self.rl_next_input = None
203
203
204 def interact(self):
204 def interact(self):
205 while self.keep_running:
205 while self.keep_running:
206 print(self.separate_in, end='')
206 print(self.separate_in, end='')
207
207
208 try:
208 try:
209 code = self.prompt_for_code()
209 code = self.prompt_for_code()
210 except EOFError:
210 except EOFError:
211 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
211 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
212 self.ask_exit()
212 self.ask_exit()
213
213
214 else:
214 else:
215 if code:
215 if code:
216 self.run_cell(code, store_history=True)
216 self.run_cell(code, store_history=True)
217
217
218 def mainloop(self):
218 def mainloop(self):
219 # An extra layer of protection in case someone mashing Ctrl-C breaks
219 # An extra layer of protection in case someone mashing Ctrl-C breaks
220 # out of our internal code.
220 # out of our internal code.
221 while True:
221 while True:
222 try:
222 try:
223 self.interact()
223 self.interact()
224 break
224 break
225 except KeyboardInterrupt:
225 except KeyboardInterrupt:
226 print("\nKeyboardInterrupt escaped interact()\n")
226 print("\nKeyboardInterrupt escaped interact()\n")
227
227
228 _inputhook = None
228 _inputhook = None
229 def inputhook(self, context):
229 def inputhook(self, context):
230 if self._inputhook is not None:
230 if self._inputhook is not None:
231 self._inputhook(context)
231 self._inputhook(context)
232
232
233 def enable_gui(self, gui=None):
233 def enable_gui(self, gui=None):
234 if gui:
234 if gui:
235 self._inputhook = get_inputhook_func(gui)
235 self._inputhook = get_inputhook_func(gui)
236 else:
236 else:
237 self._inputhook = None
237 self._inputhook = None
238
238
239 if __name__ == '__main__':
239 if __name__ == '__main__':
240 PTInteractiveShell.instance().interact()
240 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now