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