##// END OF EJS Templates
prevent atexit handlers from generating crash report...
MinRK -
Show More
@@ -1,317 +1,321 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for IPython.
3 An application for IPython.
4
4
5 All top-level applications should use the classes in this module for
5 All top-level applications should use the classes in this module for
6 handling configuration and creating componenets.
6 handling configuration and creating componenets.
7
7
8 The job of an :class:`Application` is to create the master configuration
8 The job of an :class:`Application` is to create the master configuration
9 object and then create the configurable objects, passing the config to them.
9 object and then create the configurable objects, passing the config to them.
10
10
11 Authors:
11 Authors:
12
12
13 * Brian Granger
13 * Brian Granger
14 * Fernando Perez
14 * Fernando Perez
15 * Min RK
15 * Min RK
16
16
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008-2011 The IPython Development Team
20 # Copyright (C) 2008-2011 The IPython Development Team
21 #
21 #
22 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Imports
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 import atexit
30 import glob
31 import glob
31 import logging
32 import logging
32 import os
33 import os
33 import shutil
34 import shutil
34 import sys
35 import sys
35
36
36 from IPython.config.application import Application
37 from IPython.config.application import Application
37 from IPython.config.configurable import Configurable
38 from IPython.config.configurable import Configurable
38 from IPython.config.loader import Config, ConfigFileNotFound
39 from IPython.config.loader import Config, ConfigFileNotFound
39 from IPython.core import release, crashhandler
40 from IPython.core import release, crashhandler
40 from IPython.core.profiledir import ProfileDir, ProfileDirError
41 from IPython.core.profiledir import ProfileDir, ProfileDirError
41 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
42 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
42 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict
43 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict
43 from IPython.utils import py3compat
44 from IPython.utils import py3compat
44
45
45 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
46 # Classes and functions
47 # Classes and functions
47 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
48
49
49
50
50 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
51 # Base Application Class
52 # Base Application Class
52 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
53
54
54 # aliases and flags
55 # aliases and flags
55
56
56 base_aliases = {
57 base_aliases = {
57 'profile' : 'BaseIPythonApplication.profile',
58 'profile' : 'BaseIPythonApplication.profile',
58 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
59 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
59 'log-level' : 'Application.log_level',
60 'log-level' : 'Application.log_level',
60 }
61 }
61
62
62 base_flags = dict(
63 base_flags = dict(
63 debug = ({'Application' : {'log_level' : logging.DEBUG}},
64 debug = ({'Application' : {'log_level' : logging.DEBUG}},
64 "set log level to logging.DEBUG (maximize logging output)"),
65 "set log level to logging.DEBUG (maximize logging output)"),
65 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
66 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
66 "set log level to logging.CRITICAL (minimize logging output)"),
67 "set log level to logging.CRITICAL (minimize logging output)"),
67 init = ({'BaseIPythonApplication' : {
68 init = ({'BaseIPythonApplication' : {
68 'copy_config_files' : True,
69 'copy_config_files' : True,
69 'auto_create' : True}
70 'auto_create' : True}
70 }, """Initialize profile with default config files. This is equivalent
71 }, """Initialize profile with default config files. This is equivalent
71 to running `ipython profile create <profile>` prior to startup.
72 to running `ipython profile create <profile>` prior to startup.
72 """)
73 """)
73 )
74 )
74
75
75
76
76 class BaseIPythonApplication(Application):
77 class BaseIPythonApplication(Application):
77
78
78 name = Unicode(u'ipython')
79 name = Unicode(u'ipython')
79 description = Unicode(u'IPython: an enhanced interactive Python shell.')
80 description = Unicode(u'IPython: an enhanced interactive Python shell.')
80 version = Unicode(release.version)
81 version = Unicode(release.version)
81
82
82 aliases = Dict(base_aliases)
83 aliases = Dict(base_aliases)
83 flags = Dict(base_flags)
84 flags = Dict(base_flags)
84 classes = List([ProfileDir])
85 classes = List([ProfileDir])
85
86
86 # Track whether the config_file has changed,
87 # Track whether the config_file has changed,
87 # because some logic happens only if we aren't using the default.
88 # because some logic happens only if we aren't using the default.
88 config_file_specified = Bool(False)
89 config_file_specified = Bool(False)
89
90
90 config_file_name = Unicode(u'ipython_config.py')
91 config_file_name = Unicode(u'ipython_config.py')
91 def _config_file_name_default(self):
92 def _config_file_name_default(self):
92 return self.name.replace('-','_') + u'_config.py'
93 return self.name.replace('-','_') + u'_config.py'
93 def _config_file_name_changed(self, name, old, new):
94 def _config_file_name_changed(self, name, old, new):
94 if new != old:
95 if new != old:
95 self.config_file_specified = True
96 self.config_file_specified = True
96
97
97 # The directory that contains IPython's builtin profiles.
98 # The directory that contains IPython's builtin profiles.
98 builtin_profile_dir = Unicode(
99 builtin_profile_dir = Unicode(
99 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
100 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
100 )
101 )
101
102
102 config_file_paths = List(Unicode)
103 config_file_paths = List(Unicode)
103 def _config_file_paths_default(self):
104 def _config_file_paths_default(self):
104 return [os.getcwdu()]
105 return [os.getcwdu()]
105
106
106 profile = Unicode(u'', config=True,
107 profile = Unicode(u'', config=True,
107 help="""The IPython profile to use."""
108 help="""The IPython profile to use."""
108 )
109 )
109 def _profile_default(self):
110 def _profile_default(self):
110 return "python3" if py3compat.PY3 else "default"
111 return "python3" if py3compat.PY3 else "default"
111
112
112 def _profile_changed(self, name, old, new):
113 def _profile_changed(self, name, old, new):
113 self.builtin_profile_dir = os.path.join(
114 self.builtin_profile_dir = os.path.join(
114 get_ipython_package_dir(), u'config', u'profile', new
115 get_ipython_package_dir(), u'config', u'profile', new
115 )
116 )
116
117
117 ipython_dir = Unicode(get_ipython_dir(), config=True,
118 ipython_dir = Unicode(get_ipython_dir(), config=True,
118 help="""
119 help="""
119 The name of the IPython directory. This directory is used for logging
120 The name of the IPython directory. This directory is used for logging
120 configuration (through profiles), history storage, etc. The default
121 configuration (through profiles), history storage, etc. The default
121 is usually $HOME/.ipython. This options can also be specified through
122 is usually $HOME/.ipython. This options can also be specified through
122 the environment variable IPYTHON_DIR.
123 the environment variable IPYTHON_DIR.
123 """
124 """
124 )
125 )
125
126
126 overwrite = Bool(False, config=True,
127 overwrite = Bool(False, config=True,
127 help="""Whether to overwrite existing config files when copying""")
128 help="""Whether to overwrite existing config files when copying""")
128 auto_create = Bool(False, config=True,
129 auto_create = Bool(False, config=True,
129 help="""Whether to create profile dir if it doesn't exist""")
130 help="""Whether to create profile dir if it doesn't exist""")
130
131
131 config_files = List(Unicode)
132 config_files = List(Unicode)
132 def _config_files_default(self):
133 def _config_files_default(self):
133 return [u'ipython_config.py']
134 return [u'ipython_config.py']
134
135
135 copy_config_files = Bool(False, config=True,
136 copy_config_files = Bool(False, config=True,
136 help="""Whether to install the default config files into the profile dir.
137 help="""Whether to install the default config files into the profile dir.
137 If a new profile is being created, and IPython contains config files for that
138 If a new profile is being created, and IPython contains config files for that
138 profile, then they will be staged into the new directory. Otherwise,
139 profile, then they will be staged into the new directory. Otherwise,
139 default config files will be automatically generated.
140 default config files will be automatically generated.
140 """)
141 """)
141
142
142 # The class to use as the crash handler.
143 # The class to use as the crash handler.
143 crash_handler_class = Type(crashhandler.CrashHandler)
144 crash_handler_class = Type(crashhandler.CrashHandler)
144
145
145 def __init__(self, **kwargs):
146 def __init__(self, **kwargs):
146 super(BaseIPythonApplication, self).__init__(**kwargs)
147 super(BaseIPythonApplication, self).__init__(**kwargs)
147 # ensure even default IPYTHON_DIR exists
148 # ensure even default IPYTHON_DIR exists
148 if not os.path.exists(self.ipython_dir):
149 if not os.path.exists(self.ipython_dir):
149 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
150 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
150
151
151 #-------------------------------------------------------------------------
152 #-------------------------------------------------------------------------
152 # Various stages of Application creation
153 # Various stages of Application creation
153 #-------------------------------------------------------------------------
154 #-------------------------------------------------------------------------
154
155
155 def init_crash_handler(self):
156 def init_crash_handler(self):
156 """Create a crash handler, typically setting sys.excepthook to it."""
157 """Create a crash handler, typically setting sys.excepthook to it."""
157 self.crash_handler = self.crash_handler_class(self)
158 self.crash_handler = self.crash_handler_class(self)
158 sys.excepthook = self.crash_handler
159 sys.excepthook = self.crash_handler
160 def unset_crashhandler():
161 sys.excepthook = sys.__excepthook__
162 atexit.register(unset_crashhandler)
159
163
160 def _ipython_dir_changed(self, name, old, new):
164 def _ipython_dir_changed(self, name, old, new):
161 if old in sys.path:
165 if old in sys.path:
162 sys.path.remove(old)
166 sys.path.remove(old)
163 sys.path.append(os.path.abspath(new))
167 sys.path.append(os.path.abspath(new))
164 if not os.path.isdir(new):
168 if not os.path.isdir(new):
165 os.makedirs(new, mode=0777)
169 os.makedirs(new, mode=0777)
166 readme = os.path.join(new, 'README')
170 readme = os.path.join(new, 'README')
167 if not os.path.exists(readme):
171 if not os.path.exists(readme):
168 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
172 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
169 shutil.copy(os.path.join(path, 'README'), readme)
173 shutil.copy(os.path.join(path, 'README'), readme)
170 self.log.debug("IPYTHON_DIR set to: %s" % new)
174 self.log.debug("IPYTHON_DIR set to: %s" % new)
171
175
172 def load_config_file(self, suppress_errors=True):
176 def load_config_file(self, suppress_errors=True):
173 """Load the config file.
177 """Load the config file.
174
178
175 By default, errors in loading config are handled, and a warning
179 By default, errors in loading config are handled, and a warning
176 printed on screen. For testing, the suppress_errors option is set
180 printed on screen. For testing, the suppress_errors option is set
177 to False, so errors will make tests fail.
181 to False, so errors will make tests fail.
178 """
182 """
179 self.log.debug("Searching path %s for config files", self.config_file_paths)
183 self.log.debug("Searching path %s for config files", self.config_file_paths)
180 base_config = 'ipython_config.py'
184 base_config = 'ipython_config.py'
181 self.log.debug("Attempting to load config file: %s" %
185 self.log.debug("Attempting to load config file: %s" %
182 base_config)
186 base_config)
183 try:
187 try:
184 Application.load_config_file(
188 Application.load_config_file(
185 self,
189 self,
186 base_config,
190 base_config,
187 path=self.config_file_paths
191 path=self.config_file_paths
188 )
192 )
189 except ConfigFileNotFound:
193 except ConfigFileNotFound:
190 # ignore errors loading parent
194 # ignore errors loading parent
191 self.log.debug("Config file %s not found", base_config)
195 self.log.debug("Config file %s not found", base_config)
192 pass
196 pass
193 if self.config_file_name == base_config:
197 if self.config_file_name == base_config:
194 # don't load secondary config
198 # don't load secondary config
195 return
199 return
196 self.log.debug("Attempting to load config file: %s" %
200 self.log.debug("Attempting to load config file: %s" %
197 self.config_file_name)
201 self.config_file_name)
198 try:
202 try:
199 Application.load_config_file(
203 Application.load_config_file(
200 self,
204 self,
201 self.config_file_name,
205 self.config_file_name,
202 path=self.config_file_paths
206 path=self.config_file_paths
203 )
207 )
204 except ConfigFileNotFound:
208 except ConfigFileNotFound:
205 # Only warn if the default config file was NOT being used.
209 # Only warn if the default config file was NOT being used.
206 if self.config_file_specified:
210 if self.config_file_specified:
207 msg = self.log.warn
211 msg = self.log.warn
208 else:
212 else:
209 msg = self.log.debug
213 msg = self.log.debug
210 msg("Config file not found, skipping: %s", self.config_file_name)
214 msg("Config file not found, skipping: %s", self.config_file_name)
211 except:
215 except:
212 # For testing purposes.
216 # For testing purposes.
213 if not suppress_errors:
217 if not suppress_errors:
214 raise
218 raise
215 self.log.warn("Error loading config file: %s" %
219 self.log.warn("Error loading config file: %s" %
216 self.config_file_name, exc_info=True)
220 self.config_file_name, exc_info=True)
217
221
218 def init_profile_dir(self):
222 def init_profile_dir(self):
219 """initialize the profile dir"""
223 """initialize the profile dir"""
220 try:
224 try:
221 # location explicitly specified:
225 # location explicitly specified:
222 location = self.config.ProfileDir.location
226 location = self.config.ProfileDir.location
223 except AttributeError:
227 except AttributeError:
224 # location not specified, find by profile name
228 # location not specified, find by profile name
225 try:
229 try:
226 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
230 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
227 except ProfileDirError:
231 except ProfileDirError:
228 # not found, maybe create it (always create default profile)
232 # not found, maybe create it (always create default profile)
229 if self.auto_create or self.profile==self._profile_default():
233 if self.auto_create or self.profile==self._profile_default():
230 try:
234 try:
231 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
235 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
232 except ProfileDirError:
236 except ProfileDirError:
233 self.log.fatal("Could not create profile: %r"%self.profile)
237 self.log.fatal("Could not create profile: %r"%self.profile)
234 self.exit(1)
238 self.exit(1)
235 else:
239 else:
236 self.log.info("Created profile dir: %r"%p.location)
240 self.log.info("Created profile dir: %r"%p.location)
237 else:
241 else:
238 self.log.fatal("Profile %r not found."%self.profile)
242 self.log.fatal("Profile %r not found."%self.profile)
239 self.exit(1)
243 self.exit(1)
240 else:
244 else:
241 self.log.info("Using existing profile dir: %r"%p.location)
245 self.log.info("Using existing profile dir: %r"%p.location)
242 else:
246 else:
243 # location is fully specified
247 # location is fully specified
244 try:
248 try:
245 p = ProfileDir.find_profile_dir(location, self.config)
249 p = ProfileDir.find_profile_dir(location, self.config)
246 except ProfileDirError:
250 except ProfileDirError:
247 # not found, maybe create it
251 # not found, maybe create it
248 if self.auto_create:
252 if self.auto_create:
249 try:
253 try:
250 p = ProfileDir.create_profile_dir(location, self.config)
254 p = ProfileDir.create_profile_dir(location, self.config)
251 except ProfileDirError:
255 except ProfileDirError:
252 self.log.fatal("Could not create profile directory: %r"%location)
256 self.log.fatal("Could not create profile directory: %r"%location)
253 self.exit(1)
257 self.exit(1)
254 else:
258 else:
255 self.log.info("Creating new profile dir: %r"%location)
259 self.log.info("Creating new profile dir: %r"%location)
256 else:
260 else:
257 self.log.fatal("Profile directory %r not found."%location)
261 self.log.fatal("Profile directory %r not found."%location)
258 self.exit(1)
262 self.exit(1)
259 else:
263 else:
260 self.log.info("Using existing profile dir: %r"%location)
264 self.log.info("Using existing profile dir: %r"%location)
261
265
262 self.profile_dir = p
266 self.profile_dir = p
263 self.config_file_paths.append(p.location)
267 self.config_file_paths.append(p.location)
264
268
265 def init_config_files(self):
269 def init_config_files(self):
266 """[optionally] copy default config files into profile dir."""
270 """[optionally] copy default config files into profile dir."""
267 # copy config files
271 # copy config files
268 path = self.builtin_profile_dir
272 path = self.builtin_profile_dir
269 if self.copy_config_files:
273 if self.copy_config_files:
270 src = self.profile
274 src = self.profile
271
275
272 cfg = self.config_file_name
276 cfg = self.config_file_name
273 if path and os.path.exists(os.path.join(path, cfg)):
277 if path and os.path.exists(os.path.join(path, cfg)):
274 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
278 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
275 cfg, src, self.profile_dir.location, self.overwrite)
279 cfg, src, self.profile_dir.location, self.overwrite)
276 )
280 )
277 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
281 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
278 else:
282 else:
279 self.stage_default_config_file()
283 self.stage_default_config_file()
280 else:
284 else:
281 # Still stage *bundled* config files, but not generated ones
285 # Still stage *bundled* config files, but not generated ones
282 # This is necessary for `ipython profile=sympy` to load the profile
286 # This is necessary for `ipython profile=sympy` to load the profile
283 # on the first go
287 # on the first go
284 files = glob.glob(os.path.join(path, '*.py'))
288 files = glob.glob(os.path.join(path, '*.py'))
285 for fullpath in files:
289 for fullpath in files:
286 cfg = os.path.basename(fullpath)
290 cfg = os.path.basename(fullpath)
287 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
291 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
288 # file was copied
292 # file was copied
289 self.log.warn("Staging bundled %s from %s into %r"%(
293 self.log.warn("Staging bundled %s from %s into %r"%(
290 cfg, self.profile, self.profile_dir.location)
294 cfg, self.profile, self.profile_dir.location)
291 )
295 )
292
296
293
297
294 def stage_default_config_file(self):
298 def stage_default_config_file(self):
295 """auto generate default config file, and stage it into the profile."""
299 """auto generate default config file, and stage it into the profile."""
296 s = self.generate_config_file()
300 s = self.generate_config_file()
297 fname = os.path.join(self.profile_dir.location, self.config_file_name)
301 fname = os.path.join(self.profile_dir.location, self.config_file_name)
298 if self.overwrite or not os.path.exists(fname):
302 if self.overwrite or not os.path.exists(fname):
299 self.log.warn("Generating default config file: %r"%(fname))
303 self.log.warn("Generating default config file: %r"%(fname))
300 with open(fname, 'w') as f:
304 with open(fname, 'w') as f:
301 f.write(s)
305 f.write(s)
302
306
303
307
304 def initialize(self, argv=None):
308 def initialize(self, argv=None):
305 # don't hook up crash handler before parsing command-line
309 # don't hook up crash handler before parsing command-line
306 self.parse_command_line(argv)
310 self.parse_command_line(argv)
307 self.init_crash_handler()
311 self.init_crash_handler()
308 if self.subapp is not None:
312 if self.subapp is not None:
309 # stop here if subapp is taking over
313 # stop here if subapp is taking over
310 return
314 return
311 cl_config = self.config
315 cl_config = self.config
312 self.init_profile_dir()
316 self.init_profile_dir()
313 self.init_config_files()
317 self.init_config_files()
314 self.load_config_file()
318 self.load_config_file()
315 # enforce cl-opts override configfile opts:
319 # enforce cl-opts override configfile opts:
316 self.update_config(cl_config)
320 self.update_config(cl_config)
317
321
General Comments 0
You need to be logged in to leave comments. Login now