##// END OF EJS Templates
don't run init_profile_dir twice
MinRK -
Show More
@@ -1,369 +1,372
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 30 import atexit
31 31 import glob
32 32 import logging
33 33 import os
34 34 import shutil
35 35 import sys
36 36
37 37 from IPython.config.application import Application, catch_config_error
38 38 from IPython.config.loader import ConfigFileNotFound
39 39 from IPython.core import release, crashhandler
40 40 from IPython.core.profiledir import ProfileDir, ProfileDirError
41 41 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
42 42 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # Classes and functions
46 46 #-----------------------------------------------------------------------------
47 47
48 48
49 49 #-----------------------------------------------------------------------------
50 50 # Base Application Class
51 51 #-----------------------------------------------------------------------------
52 52
53 53 # aliases and flags
54 54
55 55 base_aliases = {
56 56 'profile' : 'BaseIPythonApplication.profile',
57 57 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
58 58 'log-level' : 'Application.log_level',
59 59 'config' : 'BaseIPythonApplication.extra_config_file',
60 60 }
61 61
62 62 base_flags = dict(
63 63 debug = ({'Application' : {'log_level' : logging.DEBUG}},
64 64 "set log level to logging.DEBUG (maximize logging output)"),
65 65 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
66 66 "set log level to logging.CRITICAL (minimize logging output)"),
67 67 init = ({'BaseIPythonApplication' : {
68 68 'copy_config_files' : True,
69 69 'auto_create' : True}
70 70 }, """Initialize profile with default config files. This is equivalent
71 71 to running `ipython profile create <profile>` prior to startup.
72 72 """)
73 73 )
74 74
75 75
76 76 class BaseIPythonApplication(Application):
77 77
78 78 name = Unicode(u'ipython')
79 79 description = Unicode(u'IPython: an enhanced interactive Python shell.')
80 80 version = Unicode(release.version)
81 81
82 82 aliases = Dict(base_aliases)
83 83 flags = Dict(base_flags)
84 84 classes = List([ProfileDir])
85 85
86 86 # Track whether the config_file has changed,
87 87 # because some logic happens only if we aren't using the default.
88 88 config_file_specified = Set()
89 89
90 90 config_file_name = Unicode()
91 91 def _config_file_name_default(self):
92 92 return self.name.replace('-','_') + u'_config.py'
93 93 def _config_file_name_changed(self, name, old, new):
94 94 if new != old:
95 95 self.config_file_specified.add(new)
96 96
97 97 # The directory that contains IPython's builtin profiles.
98 98 builtin_profile_dir = Unicode(
99 99 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
100 100 )
101 101
102 102 config_file_paths = List(Unicode)
103 103 def _config_file_paths_default(self):
104 104 return [os.getcwdu()]
105 105
106 106 extra_config_file = Unicode(config=True,
107 107 help="""Path to an extra config file to load.
108 108
109 109 If specified, load this config file in addition to any other IPython config.
110 110 """)
111 111 def _extra_config_file_changed(self, name, old, new):
112 112 try:
113 113 self.config_files.remove(old)
114 114 except ValueError:
115 115 pass
116 116 self.config_file_specified.add(new)
117 117 self.config_files.append(new)
118 118
119 119 profile = Unicode(u'default', config=True,
120 120 help="""The IPython profile to use."""
121 121 )
122 122
123 123 def _profile_changed(self, name, old, new):
124 124 self.builtin_profile_dir = os.path.join(
125 125 get_ipython_package_dir(), u'config', u'profile', new
126 126 )
127 127
128 128 ipython_dir = Unicode(get_ipython_dir(), config=True,
129 129 help="""
130 130 The name of the IPython directory. This directory is used for logging
131 131 configuration (through profiles), history storage, etc. The default
132 132 is usually $HOME/.ipython. This options can also be specified through
133 133 the environment variable IPYTHONDIR.
134 134 """
135 135 )
136 136 _in_init_profile_dir = False
137 137 profile_dir = Instance(ProfileDir)
138 138 def _profile_dir_default(self):
139 139 # avoid recursion
140 140 if self._in_init_profile_dir:
141 141 return
142 142 # profile_dir requested early, force initialization
143 143 self.init_profile_dir()
144 144 return self.profile_dir
145 145
146 146 overwrite = Bool(False, config=True,
147 147 help="""Whether to overwrite existing config files when copying""")
148 148 auto_create = Bool(False, config=True,
149 149 help="""Whether to create profile dir if it doesn't exist""")
150 150
151 151 config_files = List(Unicode)
152 152 def _config_files_default(self):
153 153 return [self.config_file_name]
154 154
155 155 copy_config_files = Bool(False, config=True,
156 156 help="""Whether to install the default config files into the profile dir.
157 157 If a new profile is being created, and IPython contains config files for that
158 158 profile, then they will be staged into the new directory. Otherwise,
159 159 default config files will be automatically generated.
160 160 """)
161 161
162 162 verbose_crash = Bool(False, config=True,
163 163 help="""Create a massive crash report when IPython encounters what may be an
164 164 internal error. The default is to append a short message to the
165 165 usual traceback""")
166 166
167 167 # The class to use as the crash handler.
168 168 crash_handler_class = Type(crashhandler.CrashHandler)
169 169
170 170 @catch_config_error
171 171 def __init__(self, **kwargs):
172 172 super(BaseIPythonApplication, self).__init__(**kwargs)
173 173 # ensure current working directory exists
174 174 try:
175 175 directory = os.getcwdu()
176 176 except:
177 177 # raise exception
178 178 self.log.error("Current working directory doesn't exist.")
179 179 raise
180 180
181 181 # ensure even default IPYTHONDIR exists
182 182 if not os.path.exists(self.ipython_dir):
183 183 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
184 184
185 185 #-------------------------------------------------------------------------
186 186 # Various stages of Application creation
187 187 #-------------------------------------------------------------------------
188 188
189 189 def init_crash_handler(self):
190 190 """Create a crash handler, typically setting sys.excepthook to it."""
191 191 self.crash_handler = self.crash_handler_class(self)
192 192 sys.excepthook = self.excepthook
193 193 def unset_crashhandler():
194 194 sys.excepthook = sys.__excepthook__
195 195 atexit.register(unset_crashhandler)
196 196
197 197 def excepthook(self, etype, evalue, tb):
198 198 """this is sys.excepthook after init_crashhandler
199 199
200 200 set self.verbose_crash=True to use our full crashhandler, instead of
201 201 a regular traceback with a short message (crash_handler_lite)
202 202 """
203 203
204 204 if self.verbose_crash:
205 205 return self.crash_handler(etype, evalue, tb)
206 206 else:
207 207 return crashhandler.crash_handler_lite(etype, evalue, tb)
208 208
209 209 def _ipython_dir_changed(self, name, old, new):
210 210 if old in sys.path:
211 211 sys.path.remove(old)
212 212 sys.path.append(os.path.abspath(new))
213 213 if not os.path.isdir(new):
214 214 os.makedirs(new, mode=0o777)
215 215 readme = os.path.join(new, 'README')
216 216 if not os.path.exists(readme):
217 217 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
218 218 shutil.copy(os.path.join(path, 'README'), readme)
219 219 self.log.debug("IPYTHONDIR set to: %s" % new)
220 220
221 221 def load_config_file(self, suppress_errors=True):
222 222 """Load the config file.
223 223
224 224 By default, errors in loading config are handled, and a warning
225 225 printed on screen. For testing, the suppress_errors option is set
226 226 to False, so errors will make tests fail.
227 227 """
228 228 self.log.debug("Searching path %s for config files", self.config_file_paths)
229 229 base_config = 'ipython_config.py'
230 230 self.log.debug("Attempting to load config file: %s" %
231 231 base_config)
232 232 try:
233 233 Application.load_config_file(
234 234 self,
235 235 base_config,
236 236 path=self.config_file_paths
237 237 )
238 238 except ConfigFileNotFound:
239 239 # ignore errors loading parent
240 240 self.log.debug("Config file %s not found", base_config)
241 241 pass
242 242
243 243 for config_file_name in self.config_files:
244 244 if not config_file_name or config_file_name == base_config:
245 245 continue
246 246 self.log.debug("Attempting to load config file: %s" %
247 247 self.config_file_name)
248 248 try:
249 249 Application.load_config_file(
250 250 self,
251 251 config_file_name,
252 252 path=self.config_file_paths
253 253 )
254 254 except ConfigFileNotFound:
255 255 # Only warn if the default config file was NOT being used.
256 256 if config_file_name in self.config_file_specified:
257 257 msg = self.log.warn
258 258 else:
259 259 msg = self.log.debug
260 260 msg("Config file not found, skipping: %s", config_file_name)
261 261 except:
262 262 # For testing purposes.
263 263 if not suppress_errors:
264 264 raise
265 265 self.log.warn("Error loading config file: %s" %
266 266 self.config_file_name, exc_info=True)
267 267
268 268 def init_profile_dir(self):
269 269 """initialize the profile dir"""
270 270 self._in_init_profile_dir = True
271 if self.profile_dir is not None:
272 # already ran
273 return
271 274 try:
272 275 # location explicitly specified:
273 276 location = self.config.ProfileDir.location
274 277 except AttributeError:
275 278 # location not specified, find by profile name
276 279 try:
277 280 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
278 281 except ProfileDirError:
279 282 # not found, maybe create it (always create default profile)
280 283 if self.auto_create or self.profile == 'default':
281 284 try:
282 285 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
283 286 except ProfileDirError:
284 287 self.log.fatal("Could not create profile: %r"%self.profile)
285 288 self.exit(1)
286 289 else:
287 290 self.log.info("Created profile dir: %r"%p.location)
288 291 else:
289 292 self.log.fatal("Profile %r not found."%self.profile)
290 293 self.exit(1)
291 294 else:
292 295 self.log.info("Using existing profile dir: %r"%p.location)
293 296 else:
294 297 # location is fully specified
295 298 try:
296 299 p = ProfileDir.find_profile_dir(location, self.config)
297 300 except ProfileDirError:
298 301 # not found, maybe create it
299 302 if self.auto_create:
300 303 try:
301 304 p = ProfileDir.create_profile_dir(location, self.config)
302 305 except ProfileDirError:
303 306 self.log.fatal("Could not create profile directory: %r"%location)
304 307 self.exit(1)
305 308 else:
306 309 self.log.info("Creating new profile dir: %r"%location)
307 310 else:
308 311 self.log.fatal("Profile directory %r not found."%location)
309 312 self.exit(1)
310 313 else:
311 314 self.log.info("Using existing profile dir: %r"%location)
312 315
313 316 self.profile_dir = p
314 317 self.config_file_paths.append(p.location)
315 318 self._in_init_profile_dir = False
316 319
317 320 def init_config_files(self):
318 321 """[optionally] copy default config files into profile dir."""
319 322 # copy config files
320 323 path = self.builtin_profile_dir
321 324 if self.copy_config_files:
322 325 src = self.profile
323 326
324 327 cfg = self.config_file_name
325 328 if path and os.path.exists(os.path.join(path, cfg)):
326 329 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
327 330 cfg, src, self.profile_dir.location, self.overwrite)
328 331 )
329 332 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
330 333 else:
331 334 self.stage_default_config_file()
332 335 else:
333 336 # Still stage *bundled* config files, but not generated ones
334 337 # This is necessary for `ipython profile=sympy` to load the profile
335 338 # on the first go
336 339 files = glob.glob(os.path.join(path, '*.py'))
337 340 for fullpath in files:
338 341 cfg = os.path.basename(fullpath)
339 342 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
340 343 # file was copied
341 344 self.log.warn("Staging bundled %s from %s into %r"%(
342 345 cfg, self.profile, self.profile_dir.location)
343 346 )
344 347
345 348
346 349 def stage_default_config_file(self):
347 350 """auto generate default config file, and stage it into the profile."""
348 351 s = self.generate_config_file()
349 352 fname = os.path.join(self.profile_dir.location, self.config_file_name)
350 353 if self.overwrite or not os.path.exists(fname):
351 354 self.log.warn("Generating default config file: %r"%(fname))
352 355 with open(fname, 'w') as f:
353 356 f.write(s)
354 357
355 358 @catch_config_error
356 359 def initialize(self, argv=None):
357 360 # don't hook up crash handler before parsing command-line
358 361 self.parse_command_line(argv)
359 362 self.init_crash_handler()
360 363 if self.subapp is not None:
361 364 # stop here if subapp is taking over
362 365 return
363 366 cl_config = self.config
364 367 self.init_profile_dir()
365 368 self.init_config_files()
366 369 self.load_config_file()
367 370 # enforce cl-opts override configfile opts:
368 371 self.update_config(cl_config)
369 372
General Comments 0
You need to be logged in to leave comments. Login now