##// END OF EJS Templates
Restore terminal title when not running non-interactively
Maciej Goszczycki -
Show More
@@ -1,342 +1,343
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The :class:`~traitlets.config.application.Application` object for the command
4 The :class:`~traitlets.config.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
11
12 import logging
12 import logging
13 import os
13 import os
14 import sys
14 import sys
15 import warnings
15 import warnings
16
16
17 from traitlets.config.loader import Config
17 from traitlets.config.loader import Config
18 from traitlets.config.application import boolean_flag, catch_config_error
18 from traitlets.config.application import boolean_flag, catch_config_error
19 from IPython.core import release
19 from IPython.core import release
20 from IPython.core import usage
20 from IPython.core import usage
21 from IPython.core.completer import IPCompleter
21 from IPython.core.completer import IPCompleter
22 from IPython.core.crashhandler import CrashHandler
22 from IPython.core.crashhandler import CrashHandler
23 from IPython.core.formatters import PlainTextFormatter
23 from IPython.core.formatters import PlainTextFormatter
24 from IPython.core.history import HistoryManager
24 from IPython.core.history import HistoryManager
25 from IPython.core.application import (
25 from IPython.core.application import (
26 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
26 ProfileDir, BaseIPythonApplication, base_flags, base_aliases
27 )
27 )
28 from IPython.core.magic import MagicsManager
28 from IPython.core.magic import MagicsManager
29 from IPython.core.magics import (
29 from IPython.core.magics import (
30 ScriptMagics, LoggingMagics
30 ScriptMagics, LoggingMagics
31 )
31 )
32 from IPython.core.shellapp import (
32 from IPython.core.shellapp import (
33 InteractiveShellApp, shell_flags, shell_aliases
33 InteractiveShellApp, shell_flags, shell_aliases
34 )
34 )
35 from IPython.extensions.storemagic import StoreMagics
35 from IPython.extensions.storemagic import StoreMagics
36 from .interactiveshell import TerminalInteractiveShell
36 from .interactiveshell import TerminalInteractiveShell
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, default, observe, Type
39 Bool, List, default, observe, Type
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('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
105 addflag('simple-prompt', 'TerminalInteractiveShell.simple_prompt',
106 "Force simple minimal prompt using `raw_input`",
106 "Force simple minimal prompt using `raw_input`",
107 "Use a rich interactive prompt with prompt_toolkit",
107 "Use a rich interactive prompt with prompt_toolkit",
108 )
108 )
109
109
110 addflag('banner', 'TerminalIPythonApp.display_banner',
110 addflag('banner', 'TerminalIPythonApp.display_banner',
111 "Display a banner upon starting IPython.",
111 "Display a banner upon starting IPython.",
112 "Don't display a banner upon starting IPython."
112 "Don't display a banner upon starting IPython."
113 )
113 )
114 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
114 addflag('confirm-exit', 'TerminalInteractiveShell.confirm_exit',
115 """Set to confirm when you try to exit IPython with an EOF (Control-D
115 """Set to confirm when you try to exit IPython with an EOF (Control-D
116 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
117 you can force a direct exit without any confirmation.""",
117 you can force a direct exit without any confirmation.""",
118 "Don't prompt the user when exiting."
118 "Don't prompt the user when exiting."
119 )
119 )
120 addflag('term-title', 'TerminalInteractiveShell.term_title',
120 addflag('term-title', 'TerminalInteractiveShell.term_title',
121 "Enable auto setting the terminal title.",
121 "Enable auto setting the terminal title.",
122 "Disable auto setting the terminal title."
122 "Disable auto setting the terminal title."
123 )
123 )
124 classic_config = Config()
124 classic_config = Config()
125 classic_config.InteractiveShell.cache_size = 0
125 classic_config.InteractiveShell.cache_size = 0
126 classic_config.PlainTextFormatter.pprint = False
126 classic_config.PlainTextFormatter.pprint = False
127 classic_config.TerminalInteractiveShell.prompts_class='IPython.terminal.prompts.ClassicPrompts'
127 classic_config.TerminalInteractiveShell.prompts_class='IPython.terminal.prompts.ClassicPrompts'
128 classic_config.InteractiveShell.separate_in = ''
128 classic_config.InteractiveShell.separate_in = ''
129 classic_config.InteractiveShell.separate_out = ''
129 classic_config.InteractiveShell.separate_out = ''
130 classic_config.InteractiveShell.separate_out2 = ''
130 classic_config.InteractiveShell.separate_out2 = ''
131 classic_config.InteractiveShell.colors = 'NoColor'
131 classic_config.InteractiveShell.colors = 'NoColor'
132 classic_config.InteractiveShell.xmode = 'Plain'
132 classic_config.InteractiveShell.xmode = 'Plain'
133
133
134 frontend_flags['classic']=(
134 frontend_flags['classic']=(
135 classic_config,
135 classic_config,
136 "Gives IPython a similar feel to the classic Python prompt."
136 "Gives IPython a similar feel to the classic Python prompt."
137 )
137 )
138 # # log doesn't make so much sense this way anymore
138 # # log doesn't make so much sense this way anymore
139 # paa('--log','-l',
139 # paa('--log','-l',
140 # action='store_true', dest='InteractiveShell.logstart',
140 # action='store_true', dest='InteractiveShell.logstart',
141 # help="Start logging to the default log file (./ipython_log.py).")
141 # help="Start logging to the default log file (./ipython_log.py).")
142 #
142 #
143 # # quick is harder to implement
143 # # quick is harder to implement
144 frontend_flags['quick']=(
144 frontend_flags['quick']=(
145 {'TerminalIPythonApp' : {'quick' : True}},
145 {'TerminalIPythonApp' : {'quick' : True}},
146 "Enable quick startup with no config files."
146 "Enable quick startup with no config files."
147 )
147 )
148
148
149 frontend_flags['i'] = (
149 frontend_flags['i'] = (
150 {'TerminalIPythonApp' : {'force_interact' : True}},
150 {'TerminalIPythonApp' : {'force_interact' : True}},
151 """If running code from the command line, become interactive afterwards.
151 """If running code from the command line, become interactive afterwards.
152 It is often useful to follow this with `--` to treat remaining flags as
152 It is often useful to follow this with `--` to treat remaining flags as
153 script arguments.
153 script arguments.
154 """
154 """
155 )
155 )
156 flags.update(frontend_flags)
156 flags.update(frontend_flags)
157
157
158 aliases = dict(base_aliases)
158 aliases = dict(base_aliases)
159 aliases.update(shell_aliases)
159 aliases.update(shell_aliases)
160
160
161 #-----------------------------------------------------------------------------
161 #-----------------------------------------------------------------------------
162 # Main classes and functions
162 # Main classes and functions
163 #-----------------------------------------------------------------------------
163 #-----------------------------------------------------------------------------
164
164
165
165
166 class LocateIPythonApp(BaseIPythonApplication):
166 class LocateIPythonApp(BaseIPythonApplication):
167 description = """print the path to the IPython dir"""
167 description = """print the path to the IPython dir"""
168 subcommands = dict(
168 subcommands = dict(
169 profile=('IPython.core.profileapp.ProfileLocate',
169 profile=('IPython.core.profileapp.ProfileLocate',
170 "print the path to an IPython profile directory",
170 "print the path to an IPython profile directory",
171 ),
171 ),
172 )
172 )
173 def start(self):
173 def start(self):
174 if self.subapp is not None:
174 if self.subapp is not None:
175 return self.subapp.start()
175 return self.subapp.start()
176 else:
176 else:
177 print(self.ipython_dir)
177 print(self.ipython_dir)
178
178
179
179
180 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
180 class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp):
181 name = u'ipython'
181 name = u'ipython'
182 description = usage.cl_usage
182 description = usage.cl_usage
183 crash_handler_class = IPAppCrashHandler
183 crash_handler_class = IPAppCrashHandler
184 examples = _examples
184 examples = _examples
185
185
186 flags = flags
186 flags = flags
187 aliases = aliases
187 aliases = aliases
188 classes = List()
188 classes = List()
189
189
190 interactive_shell_class = Type(
190 interactive_shell_class = Type(
191 klass=object, # use default_value otherwise which only allow subclasses.
191 klass=object, # use default_value otherwise which only allow subclasses.
192 default_value=TerminalInteractiveShell,
192 default_value=TerminalInteractiveShell,
193 help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends"
193 help="Class to use to instantiate the TerminalInteractiveShell object. Useful for custom Frontends"
194 ).tag(config=True)
194 ).tag(config=True)
195
195
196 @default('classes')
196 @default('classes')
197 def _classes_default(self):
197 def _classes_default(self):
198 """This has to be in a method, for TerminalIPythonApp to be available."""
198 """This has to be in a method, for TerminalIPythonApp to be available."""
199 return [
199 return [
200 InteractiveShellApp, # ShellApp comes before TerminalApp, because
200 InteractiveShellApp, # ShellApp comes before TerminalApp, because
201 self.__class__, # it will also affect subclasses (e.g. QtConsole)
201 self.__class__, # it will also affect subclasses (e.g. QtConsole)
202 TerminalInteractiveShell,
202 TerminalInteractiveShell,
203 HistoryManager,
203 HistoryManager,
204 MagicsManager,
204 MagicsManager,
205 ProfileDir,
205 ProfileDir,
206 PlainTextFormatter,
206 PlainTextFormatter,
207 IPCompleter,
207 IPCompleter,
208 ScriptMagics,
208 ScriptMagics,
209 LoggingMagics,
209 LoggingMagics,
210 StoreMagics,
210 StoreMagics,
211 ]
211 ]
212
212
213 subcommands = dict(
213 subcommands = dict(
214 profile = ("IPython.core.profileapp.ProfileApp",
214 profile = ("IPython.core.profileapp.ProfileApp",
215 "Create and manage IPython profiles."
215 "Create and manage IPython profiles."
216 ),
216 ),
217 kernel = ("ipykernel.kernelapp.IPKernelApp",
217 kernel = ("ipykernel.kernelapp.IPKernelApp",
218 "Start a kernel without an attached frontend."
218 "Start a kernel without an attached frontend."
219 ),
219 ),
220 locate=('IPython.terminal.ipapp.LocateIPythonApp',
220 locate=('IPython.terminal.ipapp.LocateIPythonApp',
221 LocateIPythonApp.description
221 LocateIPythonApp.description
222 ),
222 ),
223 history=('IPython.core.historyapp.HistoryApp',
223 history=('IPython.core.historyapp.HistoryApp',
224 "Manage the IPython history database."
224 "Manage the IPython history database."
225 ),
225 ),
226 )
226 )
227
227
228
228
229 # *do* autocreate requested profile, but don't create the config file.
229 # *do* autocreate requested profile, but don't create the config file.
230 auto_create=Bool(True)
230 auto_create=Bool(True)
231 # configurables
231 # configurables
232 quick = Bool(False,
232 quick = Bool(False,
233 help="""Start IPython quickly by skipping the loading of config files."""
233 help="""Start IPython quickly by skipping the loading of config files."""
234 ).tag(config=True)
234 ).tag(config=True)
235 @observe('quick')
235 @observe('quick')
236 def _quick_changed(self, change):
236 def _quick_changed(self, change):
237 if change['new']:
237 if change['new']:
238 self.load_config_file = lambda *a, **kw: None
238 self.load_config_file = lambda *a, **kw: None
239
239
240 display_banner = Bool(True,
240 display_banner = Bool(True,
241 help="Whether to display a banner upon starting IPython."
241 help="Whether to display a banner upon starting IPython."
242 ).tag(config=True)
242 ).tag(config=True)
243
243
244 # if there is code of files to run from the cmd line, don't interact
244 # if there is code of files to run from the cmd line, don't interact
245 # unless the --i flag (App.force_interact) is true.
245 # unless the --i flag (App.force_interact) is true.
246 force_interact = Bool(False,
246 force_interact = Bool(False,
247 help="""If a command or file is given via the command-line,
247 help="""If a command or file is given via the command-line,
248 e.g. 'ipython foo.py', start an interactive shell after executing the
248 e.g. 'ipython foo.py', start an interactive shell after executing the
249 file or command."""
249 file or command."""
250 ).tag(config=True)
250 ).tag(config=True)
251 @observe('force_interact')
251 @observe('force_interact')
252 def _force_interact_changed(self, change):
252 def _force_interact_changed(self, change):
253 if change['new']:
253 if change['new']:
254 self.interact = True
254 self.interact = True
255
255
256 @observe('file_to_run', 'code_to_run', 'module_to_run')
256 @observe('file_to_run', 'code_to_run', 'module_to_run')
257 def _file_to_run_changed(self, change):
257 def _file_to_run_changed(self, change):
258 new = change['new']
258 new = change['new']
259 if new:
259 if new:
260 self.something_to_run = True
260 self.something_to_run = True
261 if new and not self.force_interact:
261 if new and not self.force_interact:
262 self.interact = False
262 self.interact = False
263
263
264 # internal, not-configurable
264 # internal, not-configurable
265 something_to_run=Bool(False)
265 something_to_run=Bool(False)
266
266
267 @catch_config_error
267 @catch_config_error
268 def initialize(self, argv=None):
268 def initialize(self, argv=None):
269 """Do actions after construct, but before starting the app."""
269 """Do actions after construct, but before starting the app."""
270 super(TerminalIPythonApp, self).initialize(argv)
270 super(TerminalIPythonApp, self).initialize(argv)
271 if self.subapp is not None:
271 if self.subapp is not None:
272 # don't bother initializing further, starting subapp
272 # don't bother initializing further, starting subapp
273 return
273 return
274 # print self.extra_args
274 # print self.extra_args
275 if self.extra_args and not self.something_to_run:
275 if self.extra_args and not self.something_to_run:
276 self.file_to_run = self.extra_args[0]
276 self.file_to_run = self.extra_args[0]
277 self.init_path()
277 self.init_path()
278 # create the shell
278 # create the shell
279 self.init_shell()
279 self.init_shell()
280 # and draw the banner
280 # and draw the banner
281 self.init_banner()
281 self.init_banner()
282 # Now a variety of things that happen after the banner is printed.
282 # Now a variety of things that happen after the banner is printed.
283 self.init_gui_pylab()
283 self.init_gui_pylab()
284 self.init_extensions()
284 self.init_extensions()
285 self.init_code()
285 self.init_code()
286
286
287 def init_shell(self):
287 def init_shell(self):
288 """initialize the InteractiveShell instance"""
288 """initialize the InteractiveShell instance"""
289 # Create an InteractiveShell instance.
289 # Create an InteractiveShell instance.
290 # shell.display_banner should always be False for the terminal
290 # shell.display_banner should always be False for the terminal
291 # based app, because we call shell.show_banner() by hand below
291 # based app, because we call shell.show_banner() by hand below
292 # so the banner shows *before* all extension loading stuff.
292 # so the banner shows *before* all extension loading stuff.
293 self.shell = self.interactive_shell_class.instance(parent=self,
293 self.shell = self.interactive_shell_class.instance(parent=self,
294 profile_dir=self.profile_dir,
294 profile_dir=self.profile_dir,
295 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
295 ipython_dir=self.ipython_dir, user_ns=self.user_ns)
296 self.shell.configurables.append(self)
296 self.shell.configurables.append(self)
297
297
298 def init_banner(self):
298 def init_banner(self):
299 """optionally display the banner"""
299 """optionally display the banner"""
300 if self.display_banner and self.interact:
300 if self.display_banner and self.interact:
301 self.shell.show_banner()
301 self.shell.show_banner()
302 # Make sure there is a space below the banner.
302 # Make sure there is a space below the banner.
303 if self.log_level <= logging.INFO: print()
303 if self.log_level <= logging.INFO: print()
304
304
305 def _pylab_changed(self, name, old, new):
305 def _pylab_changed(self, name, old, new):
306 """Replace --pylab='inline' with --pylab='auto'"""
306 """Replace --pylab='inline' with --pylab='auto'"""
307 if new == 'inline':
307 if new == 'inline':
308 warnings.warn("'inline' not available as pylab backend, "
308 warnings.warn("'inline' not available as pylab backend, "
309 "using 'auto' instead.")
309 "using 'auto' instead.")
310 self.pylab = 'auto'
310 self.pylab = 'auto'
311
311
312 def start(self):
312 def start(self):
313 if self.subapp is not None:
313 if self.subapp is not None:
314 return self.subapp.start()
314 return self.subapp.start()
315 # perform any prexec steps:
315 # perform any prexec steps:
316 if self.interact:
316 if self.interact:
317 self.log.debug("Starting IPython's mainloop...")
317 self.log.debug("Starting IPython's mainloop...")
318 self.shell.mainloop()
318 self.shell.mainloop()
319 else:
319 else:
320 self.log.debug("IPython not interactive...")
320 self.log.debug("IPython not interactive...")
321 self.shell.restore_term_title()
321 if not self.shell.last_execution_succeeded:
322 if not self.shell.last_execution_succeeded:
322 sys.exit(1)
323 sys.exit(1)
323
324
324 def load_default_config(ipython_dir=None):
325 def load_default_config(ipython_dir=None):
325 """Load the default config file from the default ipython_dir.
326 """Load the default config file from the default ipython_dir.
326
327
327 This is useful for embedded shells.
328 This is useful for embedded shells.
328 """
329 """
329 if ipython_dir is None:
330 if ipython_dir is None:
330 ipython_dir = get_ipython_dir()
331 ipython_dir = get_ipython_dir()
331
332
332 profile_dir = os.path.join(ipython_dir, 'profile_default')
333 profile_dir = os.path.join(ipython_dir, 'profile_default')
333 app = TerminalIPythonApp()
334 app = TerminalIPythonApp()
334 app.config_file_paths.append(profile_dir)
335 app.config_file_paths.append(profile_dir)
335 app.load_config_file()
336 app.load_config_file()
336 return app.config
337 return app.config
337
338
338 launch_new_instance = TerminalIPythonApp.launch_instance
339 launch_new_instance = TerminalIPythonApp.launch_instance
339
340
340
341
341 if __name__ == '__main__':
342 if __name__ == '__main__':
342 launch_new_instance()
343 launch_new_instance()
General Comments 0
You need to be logged in to leave comments. Login now