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