##// END OF EJS Templates
Handle errors from older versions of argparse.
Thomas Kluyver -
Show More
@@ -1,374 +1,374 b''
1 1 # -*- coding: utf-8 -*-
2 2 # coding: utf-8
3 3 """A simple configuration system.
4 4
5 5 Authors
6 6 -------
7 7 * Brian Granger
8 8 * Fernando Perez
9 9 """
10 10
11 11 #-----------------------------------------------------------------------------
12 12 # Copyright (C) 2008-2009 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is in
15 15 # the file COPYING, distributed as part of this software.
16 16 #-----------------------------------------------------------------------------
17 17
18 18 #-----------------------------------------------------------------------------
19 19 # Imports
20 20 #-----------------------------------------------------------------------------
21 21
22 22 import __builtin__
23 23 import os
24 24 import sys
25 25
26 26 from IPython.external import argparse
27 27 from IPython.utils.path import filefind
28 28
29 29 #-----------------------------------------------------------------------------
30 30 # Exceptions
31 31 #-----------------------------------------------------------------------------
32 32
33 33
34 34 class ConfigError(Exception):
35 35 pass
36 36
37 37
38 38 class ConfigLoaderError(ConfigError):
39 39 pass
40 40
41 41 #-----------------------------------------------------------------------------
42 42 # Argparse fix
43 43 #-----------------------------------------------------------------------------
44 44
45 45 # Unfortunately argparse by default prints help messages to stderr instead of
46 46 # stdout. This makes it annoying to capture long help screens at the command
47 47 # line, since one must know how to pipe stderr, which many users don't know how
48 48 # to do. So we override the print_help method with one that defaults to
49 49 # stdout and use our class instead.
50 50
51 51 class ArgumentParser(argparse.ArgumentParser):
52 52 """Simple argparse subclass that prints help to stdout by default."""
53 53
54 54 def print_help(self, file=None):
55 55 if file is None:
56 56 file = sys.stdout
57 57 return super(ArgumentParser, self).print_help(file)
58 58
59 59 print_help.__doc__ = argparse.ArgumentParser.print_help.__doc__
60 60
61 61 #-----------------------------------------------------------------------------
62 62 # Config class for holding config information
63 63 #-----------------------------------------------------------------------------
64 64
65 65
66 66 class Config(dict):
67 67 """An attribute based dict that can do smart merges."""
68 68
69 69 def __init__(self, *args, **kwds):
70 70 dict.__init__(self, *args, **kwds)
71 71 # This sets self.__dict__ = self, but it has to be done this way
72 72 # because we are also overriding __setattr__.
73 73 dict.__setattr__(self, '__dict__', self)
74 74
75 75 def _merge(self, other):
76 76 to_update = {}
77 77 for k, v in other.iteritems():
78 78 if not self.has_key(k):
79 79 to_update[k] = v
80 80 else: # I have this key
81 81 if isinstance(v, Config):
82 82 # Recursively merge common sub Configs
83 83 self[k]._merge(v)
84 84 else:
85 85 # Plain updates for non-Configs
86 86 to_update[k] = v
87 87
88 88 self.update(to_update)
89 89
90 90 def _is_section_key(self, key):
91 91 if key[0].upper()==key[0] and not key.startswith('_'):
92 92 return True
93 93 else:
94 94 return False
95 95
96 96 def __contains__(self, key):
97 97 if self._is_section_key(key):
98 98 return True
99 99 else:
100 100 return super(Config, self).__contains__(key)
101 101 # .has_key is deprecated for dictionaries.
102 102 has_key = __contains__
103 103
104 104 def _has_section(self, key):
105 105 if self._is_section_key(key):
106 106 if super(Config, self).__contains__(key):
107 107 return True
108 108 return False
109 109
110 110 def copy(self):
111 111 return type(self)(dict.copy(self))
112 112
113 113 def __copy__(self):
114 114 return self.copy()
115 115
116 116 def __deepcopy__(self, memo):
117 117 import copy
118 118 return type(self)(copy.deepcopy(self.items()))
119 119
120 120 def __getitem__(self, key):
121 121 # Because we use this for an exec namespace, we need to delegate
122 122 # the lookup of names in __builtin__ to itself. This means
123 123 # that you can't have section or attribute names that are
124 124 # builtins.
125 125 try:
126 126 return getattr(__builtin__, key)
127 127 except AttributeError:
128 128 pass
129 129 if self._is_section_key(key):
130 130 try:
131 131 return dict.__getitem__(self, key)
132 132 except KeyError:
133 133 c = Config()
134 134 dict.__setitem__(self, key, c)
135 135 return c
136 136 else:
137 137 return dict.__getitem__(self, key)
138 138
139 139 def __setitem__(self, key, value):
140 140 # Don't allow names in __builtin__ to be modified.
141 141 if hasattr(__builtin__, key):
142 142 raise ConfigError('Config variable names cannot have the same name '
143 143 'as a Python builtin: %s' % key)
144 144 if self._is_section_key(key):
145 145 if not isinstance(value, Config):
146 146 raise ValueError('values whose keys begin with an uppercase '
147 147 'char must be Config instances: %r, %r' % (key, value))
148 148 else:
149 149 dict.__setitem__(self, key, value)
150 150
151 151 def __getattr__(self, key):
152 152 try:
153 153 return self.__getitem__(key)
154 154 except KeyError, e:
155 155 raise AttributeError(e)
156 156
157 157 def __setattr__(self, key, value):
158 158 try:
159 159 self.__setitem__(key, value)
160 160 except KeyError, e:
161 161 raise AttributeError(e)
162 162
163 163 def __delattr__(self, key):
164 164 try:
165 165 dict.__delitem__(self, key)
166 166 except KeyError, e:
167 167 raise AttributeError(e)
168 168
169 169
170 170 #-----------------------------------------------------------------------------
171 171 # Config loading classes
172 172 #-----------------------------------------------------------------------------
173 173
174 174
175 175 class ConfigLoader(object):
176 176 """A object for loading configurations from just about anywhere.
177 177
178 178 The resulting configuration is packaged as a :class:`Struct`.
179 179
180 180 Notes
181 181 -----
182 182 A :class:`ConfigLoader` does one thing: load a config from a source
183 183 (file, command line arguments) and returns the data as a :class:`Struct`.
184 184 There are lots of things that :class:`ConfigLoader` does not do. It does
185 185 not implement complex logic for finding config files. It does not handle
186 186 default values or merge multiple configs. These things need to be
187 187 handled elsewhere.
188 188 """
189 189
190 190 def __init__(self):
191 191 """A base class for config loaders.
192 192
193 193 Examples
194 194 --------
195 195
196 196 >>> cl = ConfigLoader()
197 197 >>> config = cl.load_config()
198 198 >>> config
199 199 {}
200 200 """
201 201 self.clear()
202 202
203 203 def clear(self):
204 204 self.config = Config()
205 205
206 206 def load_config(self):
207 207 """Load a config from somewhere, return a :class:`Config` instance.
208 208
209 209 Usually, this will cause self.config to be set and then returned.
210 210 However, in most cases, :meth:`ConfigLoader.clear` should be called
211 211 to erase any previous state.
212 212 """
213 213 self.clear()
214 214 return self.config
215 215
216 216
217 217 class FileConfigLoader(ConfigLoader):
218 218 """A base class for file based configurations.
219 219
220 220 As we add more file based config loaders, the common logic should go
221 221 here.
222 222 """
223 223 pass
224 224
225 225
226 226 class PyFileConfigLoader(FileConfigLoader):
227 227 """A config loader for pure python files.
228 228
229 229 This calls execfile on a plain python file and looks for attributes
230 230 that are all caps. These attribute are added to the config Struct.
231 231 """
232 232
233 233 def __init__(self, filename, path=None):
234 234 """Build a config loader for a filename and path.
235 235
236 236 Parameters
237 237 ----------
238 238 filename : str
239 239 The file name of the config file.
240 240 path : str, list, tuple
241 241 The path to search for the config file on, or a sequence of
242 242 paths to try in order.
243 243 """
244 244 super(PyFileConfigLoader, self).__init__()
245 245 self.filename = filename
246 246 self.path = path
247 247 self.full_filename = ''
248 248 self.data = None
249 249
250 250 def load_config(self):
251 251 """Load the config from a file and return it as a Struct."""
252 252 self.clear()
253 253 self._find_file()
254 254 self._read_file_as_dict()
255 255 self._convert_to_config()
256 256 return self.config
257 257
258 258 def _find_file(self):
259 259 """Try to find the file by searching the paths."""
260 260 self.full_filename = filefind(self.filename, self.path)
261 261
262 262 def _read_file_as_dict(self):
263 263 """Load the config file into self.config, with recursive loading."""
264 264 # This closure is made available in the namespace that is used
265 265 # to exec the config file. This allows users to call
266 266 # load_subconfig('myconfig.py') to load config files recursively.
267 267 # It needs to be a closure because it has references to self.path
268 268 # and self.config. The sub-config is loaded with the same path
269 269 # as the parent, but it uses an empty config which is then merged
270 270 # with the parents.
271 271 def load_subconfig(fname):
272 272 loader = PyFileConfigLoader(fname, self.path)
273 273 try:
274 274 sub_config = loader.load_config()
275 275 except IOError:
276 276 # Pass silently if the sub config is not there. This happens
277 277 # when a user us using a profile, but not the default config.
278 278 pass
279 279 else:
280 280 self.config._merge(sub_config)
281 281
282 282 # Again, this needs to be a closure and should be used in config
283 283 # files to get the config being loaded.
284 284 def get_config():
285 285 return self.config
286 286
287 287 namespace = dict(load_subconfig=load_subconfig, get_config=get_config)
288 288 execfile(self.full_filename, namespace)
289 289
290 290 def _convert_to_config(self):
291 291 if self.data is None:
292 292 ConfigLoaderError('self.data does not exist')
293 293
294 294
295 295 class CommandLineConfigLoader(ConfigLoader):
296 296 """A config loader for command line arguments.
297 297
298 298 As we add more command line based loaders, the common logic should go
299 299 here.
300 300 """
301 301
302 302
303 303 class ArgParseConfigLoader(CommandLineConfigLoader):
304 304
305 305 def __init__(self, argv=None, *parser_args, **parser_kw):
306 306 """Create a config loader for use with argparse.
307 307
308 308 Parameters
309 309 ----------
310 310
311 311 argv : optional, list
312 312 If given, used to read command-line arguments from, otherwise
313 313 sys.argv[1:] is used.
314 314
315 315 parser_args : tuple
316 316 A tuple of positional arguments that will be passed to the
317 317 constructor of :class:`argparse.ArgumentParser`.
318 318
319 319 parser_kw : dict
320 320 A tuple of keyword arguments that will be passed to the
321 321 constructor of :class:`argparse.ArgumentParser`.
322 322 """
323 323 super(CommandLineConfigLoader, self).__init__()
324 324 if argv == None:
325 325 argv = sys.argv[1:]
326 326 self.argv = argv
327 327 self.parser_args = parser_args
328 328 self.version = parser_kw.pop("version", None)
329 329 kwargs = dict(argument_default=argparse.SUPPRESS)
330 330 kwargs.update(parser_kw)
331 331 self.parser_kw = kwargs
332 332
333 333 def load_config(self, args=None):
334 334 """Parse command line arguments and return as a Struct.
335 335
336 336 Parameters
337 337 ----------
338 338
339 339 args : optional, list
340 340 If given, a list with the structure of sys.argv[1:] to parse
341 341 arguments from. If not given, the instance's self.argv attribute
342 342 (given at construction time) is used."""
343 343 self.clear()
344 344 if args is None:
345 345 args = self.argv
346 346 self._create_parser()
347 347 self._parse_args(args)
348 348 self._convert_to_config()
349 349 return self.config
350 350
351 351 def get_extra_args(self):
352 352 if hasattr(self, 'extra_args'):
353 353 return self.extra_args
354 354 else:
355 355 return []
356 356
357 357 def _create_parser(self):
358 358 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
359 359 self._add_arguments()
360 360
361 361 def _add_arguments(self):
362 362 raise NotImplementedError("subclasses must implement _add_arguments")
363 363
364 364 def _parse_args(self, args):
365 """self.parser->self.parsed_data"""
365 """self.parser->self.parsed_data"""
366 366 self.parsed_data, self.extra_args = self.parser.parse_known_args(args)
367 367
368 368 def _convert_to_config(self):
369 369 """self.parsed_data->self.config"""
370 370 for k, v in vars(self.parsed_data).iteritems():
371 371 exec_str = 'self.config.' + k + '= v'
372 372 exec exec_str in locals(), globals()
373 373
374 374
@@ -1,459 +1,462 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
16 16 Notes
17 17 -----
18 18 """
19 19
20 20 #-----------------------------------------------------------------------------
21 21 # Copyright (C) 2008-2009 The IPython Development Team
22 22 #
23 23 # Distributed under the terms of the BSD License. The full license is in
24 24 # the file COPYING, distributed as part of this software.
25 25 #-----------------------------------------------------------------------------
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Imports
29 29 #-----------------------------------------------------------------------------
30 30
31 31 import logging
32 32 import os
33 33 import sys
34 34
35 35 from IPython.core import release, crashhandler
36 36 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
37 37 from IPython.config.loader import (
38 38 PyFileConfigLoader,
39 39 ArgParseConfigLoader,
40 40 Config,
41 41 )
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Classes and functions
45 45 #-----------------------------------------------------------------------------
46 46
47 47 class ApplicationError(Exception):
48 48 pass
49 49
50 50
51 51 class BaseAppConfigLoader(ArgParseConfigLoader):
52 52 """Default command line options for IPython based applications."""
53 53
54 54 def _add_ipython_dir(self, parser):
55 55 """Add the --ipython-dir option to the parser."""
56 56 paa = parser.add_argument
57 57 paa('--ipython-dir',
58 58 dest='Global.ipython_dir',type=unicode,
59 59 help=
60 60 """Set to override default location of the IPython directory
61 61 IPYTHON_DIR, stored as Global.ipython_dir. This can also be
62 62 specified through the environment variable IPYTHON_DIR.""",
63 63 metavar='Global.ipython_dir')
64 64
65 65 def _add_log_level(self, parser):
66 66 """Add the --log-level option to the parser."""
67 67 paa = parser.add_argument
68 68 paa('--log-level',
69 69 dest="Global.log_level",type=int,
70 70 help='Set the log level (0,10,20,30,40,50). Default is 30.',
71 71 metavar='Global.log_level')
72 72
73 73 def _add_version(self, parser):
74 74 """Add the --version option to the parser."""
75 75 parser.add_argument('--version', action="version",
76 76 version=self.version)
77 77
78 78 def _add_arguments(self):
79 79 self._add_ipython_dir(self.parser)
80 80 self._add_log_level(self.parser)
81 self._add_version(self.parser)
81 try: # Old versions of argparse don't have a version action
82 self._add_version(self.parser)
83 except Exception:
84 pass
82 85
83 86
84 87 class Application(object):
85 88 """Load a config, construct configurables and set them running.
86 89
87 90 The configuration of an application can be done via three different Config
88 91 objects, which are loaded and ultimately merged into a single one used
89 92 from that point on by the app. These are:
90 93
91 94 1. default_config: internal defaults, implemented in code.
92 95 2. file_config: read from the filesystem.
93 96 3. command_line_config: read from the system's command line flags.
94 97
95 98 During initialization, 3 is actually read before 2, since at the
96 99 command-line one may override the location of the file to be read. But the
97 100 above is the order in which the merge is made.
98 101 """
99 102
100 103 name = u'ipython'
101 104 description = 'IPython: an enhanced interactive Python shell.'
102 105 #: Usage message printed by argparse. If None, auto-generate
103 106 usage = None
104 107 #: The command line config loader. Subclass of ArgParseConfigLoader.
105 108 command_line_loader = BaseAppConfigLoader
106 109 #: The name of the config file to load, determined at runtime
107 110 config_file_name = None
108 111 #: The name of the default config file. Track separately from the actual
109 112 #: name because some logic happens only if we aren't using the default.
110 113 default_config_file_name = u'ipython_config.py'
111 114 default_log_level = logging.WARN
112 115 #: Set by --profile option
113 116 profile_name = None
114 117 #: User's ipython directory, typically ~/.ipython or ~/.config/ipython/
115 118 ipython_dir = None
116 119 #: Internal defaults, implemented in code.
117 120 default_config = None
118 121 #: Read from the filesystem.
119 122 file_config = None
120 123 #: Read from the system's command line flags.
121 124 command_line_config = None
122 125 #: The final config that will be passed to the main object.
123 126 master_config = None
124 127 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
125 128 argv = None
126 129 #: extra arguments computed by the command-line loader
127 130 extra_args = None
128 131 #: The class to use as the crash handler.
129 132 crash_handler_class = crashhandler.CrashHandler
130 133
131 134 # Private attributes
132 135 _exiting = False
133 136 _initialized = False
134 137
135 138 def __init__(self, argv=None):
136 139 self.argv = sys.argv[1:] if argv is None else argv
137 140 self.init_logger()
138 141
139 142 def init_logger(self):
140 143 self.log = logging.getLogger(self.__class__.__name__)
141 144 # This is used as the default until the command line arguments are read.
142 145 self.log.setLevel(self.default_log_level)
143 146 self._log_handler = logging.StreamHandler()
144 147 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
145 148 self._log_handler.setFormatter(self._log_formatter)
146 149 self.log.addHandler(self._log_handler)
147 150
148 151 def _set_log_level(self, level):
149 152 self.log.setLevel(level)
150 153
151 154 def _get_log_level(self):
152 155 return self.log.level
153 156
154 157 log_level = property(_get_log_level, _set_log_level)
155 158
156 159 def initialize(self):
157 160 """Initialize the application.
158 161
159 162 Loads all configuration information and sets all application state, but
160 163 does not start any relevant processing (typically some kind of event
161 164 loop).
162 165
163 166 Once this method has been called, the application is flagged as
164 167 initialized and the method becomes a no-op."""
165 168
166 169 if self._initialized:
167 170 return
168 171
169 172 # The first part is protected with an 'attempt' wrapper, that will log
170 173 # failures with the basic system traceback machinery. Once our crash
171 174 # handler is in place, we can let any subsequent exception propagate,
172 175 # as our handler will log it with much better detail than the default.
173 176 self.attempt(self.create_crash_handler)
174 177
175 178 # Configuration phase
176 179 # Default config (internally hardwired in application code)
177 180 self.create_default_config()
178 181 self.log_default_config()
179 182 self.set_default_config_log_level()
180 183
181 184 # Command-line config
182 185 self.pre_load_command_line_config()
183 186 self.load_command_line_config()
184 187 self.set_command_line_config_log_level()
185 188 self.post_load_command_line_config()
186 189 self.log_command_line_config()
187 190
188 191 # Find resources needed for filesystem access, using information from
189 192 # the above two
190 193 self.find_ipython_dir()
191 194 self.find_resources()
192 195 self.find_config_file_name()
193 196 self.find_config_file_paths()
194 197
195 198 # File-based config
196 199 self.pre_load_file_config()
197 200 self.load_file_config()
198 201 self.set_file_config_log_level()
199 202 self.post_load_file_config()
200 203 self.log_file_config()
201 204
202 205 # Merge all config objects into a single one the app can then use
203 206 self.merge_configs()
204 207 self.log_master_config()
205 208
206 209 # Construction phase
207 210 self.pre_construct()
208 211 self.construct()
209 212 self.post_construct()
210 213
211 214 # Done, flag as such and
212 215 self._initialized = True
213 216
214 217 def start(self):
215 218 """Start the application."""
216 219 self.initialize()
217 220 self.start_app()
218 221
219 222 #-------------------------------------------------------------------------
220 223 # Various stages of Application creation
221 224 #-------------------------------------------------------------------------
222 225
223 226 def create_crash_handler(self):
224 227 """Create a crash handler, typically setting sys.excepthook to it."""
225 228 self.crash_handler = self.crash_handler_class(self)
226 229 sys.excepthook = self.crash_handler
227 230
228 231 def create_default_config(self):
229 232 """Create defaults that can't be set elsewhere.
230 233
231 234 For the most part, we try to set default in the class attributes
232 235 of Configurables. But, defaults the top-level Application (which is
233 236 not a HasTraits or Configurables) are not set in this way. Instead
234 237 we set them here. The Global section is for variables like this that
235 238 don't belong to a particular configurable.
236 239 """
237 240 c = Config()
238 241 c.Global.ipython_dir = get_ipython_dir()
239 242 c.Global.log_level = self.log_level
240 243 self.default_config = c
241 244
242 245 def log_default_config(self):
243 246 self.log.debug('Default config loaded:')
244 247 self.log.debug(repr(self.default_config))
245 248
246 249 def set_default_config_log_level(self):
247 250 try:
248 251 self.log_level = self.default_config.Global.log_level
249 252 except AttributeError:
250 253 # Fallback to the default_log_level class attribute
251 254 pass
252 255
253 256 def create_command_line_config(self):
254 257 """Create and return a command line config loader."""
255 258 return self.command_line_loader(
256 259 self.argv,
257 260 description=self.description,
258 261 version=release.version,
259 262 usage=self.usage
260 263 )
261 264
262 265 def pre_load_command_line_config(self):
263 266 """Do actions just before loading the command line config."""
264 267 pass
265 268
266 269 def load_command_line_config(self):
267 270 """Load the command line config."""
268 271 loader = self.create_command_line_config()
269 272 self.command_line_config = loader.load_config()
270 273 self.extra_args = loader.get_extra_args()
271 274
272 275 def set_command_line_config_log_level(self):
273 276 try:
274 277 self.log_level = self.command_line_config.Global.log_level
275 278 except AttributeError:
276 279 pass
277 280
278 281 def post_load_command_line_config(self):
279 282 """Do actions just after loading the command line config."""
280 283 pass
281 284
282 285 def log_command_line_config(self):
283 286 self.log.debug("Command line config loaded:")
284 287 self.log.debug(repr(self.command_line_config))
285 288
286 289 def find_ipython_dir(self):
287 290 """Set the IPython directory.
288 291
289 292 This sets ``self.ipython_dir``, but the actual value that is passed to
290 293 the application is kept in either ``self.default_config`` or
291 294 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
292 295 ``sys.path`` so config files there can be referenced by other config
293 296 files.
294 297 """
295 298
296 299 try:
297 300 self.ipython_dir = self.command_line_config.Global.ipython_dir
298 301 except AttributeError:
299 302 self.ipython_dir = self.default_config.Global.ipython_dir
300 303 sys.path.append(os.path.abspath(self.ipython_dir))
301 304 if not os.path.isdir(self.ipython_dir):
302 305 os.makedirs(self.ipython_dir, mode=0777)
303 306 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
304 307
305 308 def find_resources(self):
306 309 """Find other resources that need to be in place.
307 310
308 311 Things like cluster directories need to be in place to find the
309 312 config file. These happen right after the IPython directory has
310 313 been set.
311 314 """
312 315 pass
313 316
314 317 def find_config_file_name(self):
315 318 """Find the config file name for this application.
316 319
317 320 This must set ``self.config_file_name`` to the filename of the
318 321 config file to use (just the filename). The search paths for the
319 322 config file are set in :meth:`find_config_file_paths` and then passed
320 323 to the config file loader where they are resolved to an absolute path.
321 324
322 325 If a profile has been set at the command line, this will resolve it.
323 326 """
324 327 try:
325 328 self.config_file_name = self.command_line_config.Global.config_file
326 329 except AttributeError:
327 330 pass
328 331 else:
329 332 return
330 333
331 334 try:
332 335 self.profile_name = self.command_line_config.Global.profile
333 336 except AttributeError:
334 337 # Just use the default as there is no profile
335 338 self.config_file_name = self.default_config_file_name
336 339 else:
337 340 # Use the default config file name and profile name if set
338 341 # to determine the used config file name.
339 342 name_parts = self.default_config_file_name.split('.')
340 343 name_parts.insert(1, u'_' + self.profile_name + u'.')
341 344 self.config_file_name = ''.join(name_parts)
342 345
343 346 def find_config_file_paths(self):
344 347 """Set the search paths for resolving the config file.
345 348
346 349 This must set ``self.config_file_paths`` to a sequence of search
347 350 paths to pass to the config file loader.
348 351 """
349 352 # Include our own profiles directory last, so that users can still find
350 353 # our shipped copies of builtin profiles even if they don't have them
351 354 # in their local ipython directory.
352 355 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
353 356 self.config_file_paths = (os.getcwd(), self.ipython_dir, prof_dir)
354 357
355 358 def pre_load_file_config(self):
356 359 """Do actions before the config file is loaded."""
357 360 pass
358 361
359 362 def load_file_config(self):
360 363 """Load the config file.
361 364
362 365 This tries to load the config file from disk. If successful, the
363 366 ``CONFIG_FILE`` config variable is set to the resolved config file
364 367 location. If not successful, an empty config is used.
365 368 """
366 369 self.log.debug("Attempting to load config file: %s" %
367 370 self.config_file_name)
368 371 loader = PyFileConfigLoader(self.config_file_name,
369 372 path=self.config_file_paths)
370 373 try:
371 374 self.file_config = loader.load_config()
372 375 self.file_config.Global.config_file = loader.full_filename
373 376 except IOError:
374 377 # Only warn if the default config file was NOT being used.
375 378 if not self.config_file_name==self.default_config_file_name:
376 379 self.log.warn("Config file not found, skipping: %s" %
377 380 self.config_file_name, exc_info=True)
378 381 self.file_config = Config()
379 382 except:
380 383 self.log.warn("Error loading config file: %s" %
381 384 self.config_file_name, exc_info=True)
382 385 self.file_config = Config()
383 386
384 387 def set_file_config_log_level(self):
385 388 # We need to keeep self.log_level updated. But we only use the value
386 389 # of the file_config if a value was not specified at the command
387 390 # line, because the command line overrides everything.
388 391 if not hasattr(self.command_line_config.Global, 'log_level'):
389 392 try:
390 393 self.log_level = self.file_config.Global.log_level
391 394 except AttributeError:
392 395 pass # Use existing value
393 396
394 397 def post_load_file_config(self):
395 398 """Do actions after the config file is loaded."""
396 399 pass
397 400
398 401 def log_file_config(self):
399 402 if hasattr(self.file_config.Global, 'config_file'):
400 403 self.log.debug("Config file loaded: %s" %
401 404 self.file_config.Global.config_file)
402 405 self.log.debug(repr(self.file_config))
403 406
404 407 def merge_configs(self):
405 408 """Merge the default, command line and file config objects."""
406 409 config = Config()
407 410 config._merge(self.default_config)
408 411 config._merge(self.file_config)
409 412 config._merge(self.command_line_config)
410 413
411 414 # XXX fperez - propose to Brian we rename master_config to simply
412 415 # config, I think this is going to be heavily used in examples and
413 416 # application code and the name is shorter/easier to find/remember.
414 417 # For now, just alias it...
415 418 self.master_config = config
416 419 self.config = config
417 420
418 421 def log_master_config(self):
419 422 self.log.debug("Master config created:")
420 423 self.log.debug(repr(self.master_config))
421 424
422 425 def pre_construct(self):
423 426 """Do actions after the config has been built, but before construct."""
424 427 pass
425 428
426 429 def construct(self):
427 430 """Construct the main objects that make up this app."""
428 431 self.log.debug("Constructing main objects for application")
429 432
430 433 def post_construct(self):
431 434 """Do actions after construct, but before starting the app."""
432 435 pass
433 436
434 437 def start_app(self):
435 438 """Actually start the app."""
436 439 self.log.debug("Starting application")
437 440
438 441 #-------------------------------------------------------------------------
439 442 # Utility methods
440 443 #-------------------------------------------------------------------------
441 444
442 445 def exit(self, exit_status=0):
443 446 if self._exiting:
444 447 pass
445 448 else:
446 449 self.log.debug("Exiting application: %s" % self.name)
447 450 self._exiting = True
448 451 sys.exit(exit_status)
449 452
450 453 def attempt(self, func):
451 454 try:
452 455 func()
453 456 except SystemExit:
454 457 raise
455 458 except:
456 459 self.log.critical("Aborting application: %s" % self.name,
457 460 exc_info=True)
458 461 self.exit(0)
459 462
General Comments 0
You need to be logged in to leave comments. Login now