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