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