##// END OF EJS Templates
Fix DeprecationWarning with system-wide argparse.
Thomas Kluyver -
Show More
@@ -1,373 +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 self.version = parser_kw.pop("version", None)
328 329 kwargs = dict(argument_default=argparse.SUPPRESS)
329 330 kwargs.update(parser_kw)
330 331 self.parser_kw = kwargs
331 332
332 333 def load_config(self, args=None):
333 334 """Parse command line arguments and return as a Struct.
334 335
335 336 Parameters
336 337 ----------
337 338
338 339 args : optional, list
339 340 If given, a list with the structure of sys.argv[1:] to parse
340 341 arguments from. If not given, the instance's self.argv attribute
341 342 (given at construction time) is used."""
342 343 self.clear()
343 344 if args is None:
344 345 args = self.argv
345 346 self._create_parser()
346 347 self._parse_args(args)
347 348 self._convert_to_config()
348 349 return self.config
349 350
350 351 def get_extra_args(self):
351 352 if hasattr(self, 'extra_args'):
352 353 return self.extra_args
353 354 else:
354 355 return []
355 356
356 357 def _create_parser(self):
357 358 self.parser = ArgumentParser(*self.parser_args, **self.parser_kw)
358 359 self._add_arguments()
359 360
360 361 def _add_arguments(self):
361 362 raise NotImplementedError("subclasses must implement _add_arguments")
362 363
363 364 def _parse_args(self, args):
364 365 """self.parser->self.parsed_data"""
365 366 self.parsed_data, self.extra_args = self.parser.parse_known_args(args)
366 367
367 368 def _convert_to_config(self):
368 369 """self.parsed_data->self.config"""
369 370 for k, v in vars(self.parsed_data).iteritems():
370 371 exec_str = 'self.config.' + k + '= v'
371 372 exec exec_str in locals(), globals()
372 373
373 374
@@ -1,453 +1,459 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
73 def _add_version(self, parser):
74 """Add the --version option to the parser."""
75 parser.add_argument('--version', action="version",
76 version=self.version)
72 77
73 78 def _add_arguments(self):
74 79 self._add_ipython_dir(self.parser)
75 80 self._add_log_level(self.parser)
81 self._add_version(self.parser)
76 82
77 83
78 84 class Application(object):
79 85 """Load a config, construct configurables and set them running.
80 86
81 87 The configuration of an application can be done via three different Config
82 88 objects, which are loaded and ultimately merged into a single one used
83 89 from that point on by the app. These are:
84 90
85 91 1. default_config: internal defaults, implemented in code.
86 92 2. file_config: read from the filesystem.
87 93 3. command_line_config: read from the system's command line flags.
88 94
89 95 During initialization, 3 is actually read before 2, since at the
90 96 command-line one may override the location of the file to be read. But the
91 97 above is the order in which the merge is made.
92 98 """
93 99
94 100 name = u'ipython'
95 101 description = 'IPython: an enhanced interactive Python shell.'
96 102 #: Usage message printed by argparse. If None, auto-generate
97 103 usage = None
98 104 #: The command line config loader. Subclass of ArgParseConfigLoader.
99 105 command_line_loader = BaseAppConfigLoader
100 106 #: The name of the config file to load, determined at runtime
101 107 config_file_name = None
102 108 #: The name of the default config file. Track separately from the actual
103 109 #: name because some logic happens only if we aren't using the default.
104 110 default_config_file_name = u'ipython_config.py'
105 111 default_log_level = logging.WARN
106 112 #: Set by --profile option
107 113 profile_name = None
108 114 #: User's ipython directory, typically ~/.ipython or ~/.config/ipython/
109 115 ipython_dir = None
110 116 #: Internal defaults, implemented in code.
111 117 default_config = None
112 118 #: Read from the filesystem.
113 119 file_config = None
114 120 #: Read from the system's command line flags.
115 121 command_line_config = None
116 122 #: The final config that will be passed to the main object.
117 123 master_config = None
118 124 #: A reference to the argv to be used (typically ends up being sys.argv[1:])
119 125 argv = None
120 126 #: extra arguments computed by the command-line loader
121 127 extra_args = None
122 128 #: The class to use as the crash handler.
123 129 crash_handler_class = crashhandler.CrashHandler
124 130
125 131 # Private attributes
126 132 _exiting = False
127 133 _initialized = False
128 134
129 135 def __init__(self, argv=None):
130 136 self.argv = sys.argv[1:] if argv is None else argv
131 137 self.init_logger()
132 138
133 139 def init_logger(self):
134 140 self.log = logging.getLogger(self.__class__.__name__)
135 141 # This is used as the default until the command line arguments are read.
136 142 self.log.setLevel(self.default_log_level)
137 143 self._log_handler = logging.StreamHandler()
138 144 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
139 145 self._log_handler.setFormatter(self._log_formatter)
140 146 self.log.addHandler(self._log_handler)
141 147
142 148 def _set_log_level(self, level):
143 149 self.log.setLevel(level)
144 150
145 151 def _get_log_level(self):
146 152 return self.log.level
147 153
148 154 log_level = property(_get_log_level, _set_log_level)
149 155
150 156 def initialize(self):
151 157 """Initialize the application.
152 158
153 159 Loads all configuration information and sets all application state, but
154 160 does not start any relevant processing (typically some kind of event
155 161 loop).
156 162
157 163 Once this method has been called, the application is flagged as
158 164 initialized and the method becomes a no-op."""
159 165
160 166 if self._initialized:
161 167 return
162 168
163 169 # The first part is protected with an 'attempt' wrapper, that will log
164 170 # failures with the basic system traceback machinery. Once our crash
165 171 # handler is in place, we can let any subsequent exception propagate,
166 172 # as our handler will log it with much better detail than the default.
167 173 self.attempt(self.create_crash_handler)
168 174
169 175 # Configuration phase
170 176 # Default config (internally hardwired in application code)
171 177 self.create_default_config()
172 178 self.log_default_config()
173 179 self.set_default_config_log_level()
174 180
175 181 # Command-line config
176 182 self.pre_load_command_line_config()
177 183 self.load_command_line_config()
178 184 self.set_command_line_config_log_level()
179 185 self.post_load_command_line_config()
180 186 self.log_command_line_config()
181 187
182 188 # Find resources needed for filesystem access, using information from
183 189 # the above two
184 190 self.find_ipython_dir()
185 191 self.find_resources()
186 192 self.find_config_file_name()
187 193 self.find_config_file_paths()
188 194
189 195 # File-based config
190 196 self.pre_load_file_config()
191 197 self.load_file_config()
192 198 self.set_file_config_log_level()
193 199 self.post_load_file_config()
194 200 self.log_file_config()
195 201
196 202 # Merge all config objects into a single one the app can then use
197 203 self.merge_configs()
198 204 self.log_master_config()
199 205
200 206 # Construction phase
201 207 self.pre_construct()
202 208 self.construct()
203 209 self.post_construct()
204 210
205 211 # Done, flag as such and
206 212 self._initialized = True
207 213
208 214 def start(self):
209 215 """Start the application."""
210 216 self.initialize()
211 217 self.start_app()
212 218
213 219 #-------------------------------------------------------------------------
214 220 # Various stages of Application creation
215 221 #-------------------------------------------------------------------------
216 222
217 223 def create_crash_handler(self):
218 224 """Create a crash handler, typically setting sys.excepthook to it."""
219 225 self.crash_handler = self.crash_handler_class(self)
220 226 sys.excepthook = self.crash_handler
221 227
222 228 def create_default_config(self):
223 229 """Create defaults that can't be set elsewhere.
224 230
225 231 For the most part, we try to set default in the class attributes
226 232 of Configurables. But, defaults the top-level Application (which is
227 233 not a HasTraits or Configurables) are not set in this way. Instead
228 234 we set them here. The Global section is for variables like this that
229 235 don't belong to a particular configurable.
230 236 """
231 237 c = Config()
232 238 c.Global.ipython_dir = get_ipython_dir()
233 239 c.Global.log_level = self.log_level
234 240 self.default_config = c
235 241
236 242 def log_default_config(self):
237 243 self.log.debug('Default config loaded:')
238 244 self.log.debug(repr(self.default_config))
239 245
240 246 def set_default_config_log_level(self):
241 247 try:
242 248 self.log_level = self.default_config.Global.log_level
243 249 except AttributeError:
244 250 # Fallback to the default_log_level class attribute
245 251 pass
246 252
247 253 def create_command_line_config(self):
248 254 """Create and return a command line config loader."""
249 255 return self.command_line_loader(
250 256 self.argv,
251 257 description=self.description,
252 258 version=release.version,
253 259 usage=self.usage
254 260 )
255 261
256 262 def pre_load_command_line_config(self):
257 263 """Do actions just before loading the command line config."""
258 264 pass
259 265
260 266 def load_command_line_config(self):
261 267 """Load the command line config."""
262 268 loader = self.create_command_line_config()
263 269 self.command_line_config = loader.load_config()
264 270 self.extra_args = loader.get_extra_args()
265 271
266 272 def set_command_line_config_log_level(self):
267 273 try:
268 274 self.log_level = self.command_line_config.Global.log_level
269 275 except AttributeError:
270 276 pass
271 277
272 278 def post_load_command_line_config(self):
273 279 """Do actions just after loading the command line config."""
274 280 pass
275 281
276 282 def log_command_line_config(self):
277 283 self.log.debug("Command line config loaded:")
278 284 self.log.debug(repr(self.command_line_config))
279 285
280 286 def find_ipython_dir(self):
281 287 """Set the IPython directory.
282 288
283 289 This sets ``self.ipython_dir``, but the actual value that is passed to
284 290 the application is kept in either ``self.default_config`` or
285 291 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
286 292 ``sys.path`` so config files there can be referenced by other config
287 293 files.
288 294 """
289 295
290 296 try:
291 297 self.ipython_dir = self.command_line_config.Global.ipython_dir
292 298 except AttributeError:
293 299 self.ipython_dir = self.default_config.Global.ipython_dir
294 300 sys.path.append(os.path.abspath(self.ipython_dir))
295 301 if not os.path.isdir(self.ipython_dir):
296 302 os.makedirs(self.ipython_dir, mode=0777)
297 303 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
298 304
299 305 def find_resources(self):
300 306 """Find other resources that need to be in place.
301 307
302 308 Things like cluster directories need to be in place to find the
303 309 config file. These happen right after the IPython directory has
304 310 been set.
305 311 """
306 312 pass
307 313
308 314 def find_config_file_name(self):
309 315 """Find the config file name for this application.
310 316
311 317 This must set ``self.config_file_name`` to the filename of the
312 318 config file to use (just the filename). The search paths for the
313 319 config file are set in :meth:`find_config_file_paths` and then passed
314 320 to the config file loader where they are resolved to an absolute path.
315 321
316 322 If a profile has been set at the command line, this will resolve it.
317 323 """
318 324 try:
319 325 self.config_file_name = self.command_line_config.Global.config_file
320 326 except AttributeError:
321 327 pass
322 328 else:
323 329 return
324 330
325 331 try:
326 332 self.profile_name = self.command_line_config.Global.profile
327 333 except AttributeError:
328 334 # Just use the default as there is no profile
329 335 self.config_file_name = self.default_config_file_name
330 336 else:
331 337 # Use the default config file name and profile name if set
332 338 # to determine the used config file name.
333 339 name_parts = self.default_config_file_name.split('.')
334 340 name_parts.insert(1, u'_' + self.profile_name + u'.')
335 341 self.config_file_name = ''.join(name_parts)
336 342
337 343 def find_config_file_paths(self):
338 344 """Set the search paths for resolving the config file.
339 345
340 346 This must set ``self.config_file_paths`` to a sequence of search
341 347 paths to pass to the config file loader.
342 348 """
343 349 # Include our own profiles directory last, so that users can still find
344 350 # our shipped copies of builtin profiles even if they don't have them
345 351 # in their local ipython directory.
346 352 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
347 353 self.config_file_paths = (os.getcwd(), self.ipython_dir, prof_dir)
348 354
349 355 def pre_load_file_config(self):
350 356 """Do actions before the config file is loaded."""
351 357 pass
352 358
353 359 def load_file_config(self):
354 360 """Load the config file.
355 361
356 362 This tries to load the config file from disk. If successful, the
357 363 ``CONFIG_FILE`` config variable is set to the resolved config file
358 364 location. If not successful, an empty config is used.
359 365 """
360 366 self.log.debug("Attempting to load config file: %s" %
361 367 self.config_file_name)
362 368 loader = PyFileConfigLoader(self.config_file_name,
363 369 path=self.config_file_paths)
364 370 try:
365 371 self.file_config = loader.load_config()
366 372 self.file_config.Global.config_file = loader.full_filename
367 373 except IOError:
368 374 # Only warn if the default config file was NOT being used.
369 375 if not self.config_file_name==self.default_config_file_name:
370 376 self.log.warn("Config file not found, skipping: %s" %
371 377 self.config_file_name, exc_info=True)
372 378 self.file_config = Config()
373 379 except:
374 380 self.log.warn("Error loading config file: %s" %
375 381 self.config_file_name, exc_info=True)
376 382 self.file_config = Config()
377 383
378 384 def set_file_config_log_level(self):
379 385 # We need to keeep self.log_level updated. But we only use the value
380 386 # of the file_config if a value was not specified at the command
381 387 # line, because the command line overrides everything.
382 388 if not hasattr(self.command_line_config.Global, 'log_level'):
383 389 try:
384 390 self.log_level = self.file_config.Global.log_level
385 391 except AttributeError:
386 392 pass # Use existing value
387 393
388 394 def post_load_file_config(self):
389 395 """Do actions after the config file is loaded."""
390 396 pass
391 397
392 398 def log_file_config(self):
393 399 if hasattr(self.file_config.Global, 'config_file'):
394 400 self.log.debug("Config file loaded: %s" %
395 401 self.file_config.Global.config_file)
396 402 self.log.debug(repr(self.file_config))
397 403
398 404 def merge_configs(self):
399 405 """Merge the default, command line and file config objects."""
400 406 config = Config()
401 407 config._merge(self.default_config)
402 408 config._merge(self.file_config)
403 409 config._merge(self.command_line_config)
404 410
405 411 # XXX fperez - propose to Brian we rename master_config to simply
406 412 # config, I think this is going to be heavily used in examples and
407 413 # application code and the name is shorter/easier to find/remember.
408 414 # For now, just alias it...
409 415 self.master_config = config
410 416 self.config = config
411 417
412 418 def log_master_config(self):
413 419 self.log.debug("Master config created:")
414 420 self.log.debug(repr(self.master_config))
415 421
416 422 def pre_construct(self):
417 423 """Do actions after the config has been built, but before construct."""
418 424 pass
419 425
420 426 def construct(self):
421 427 """Construct the main objects that make up this app."""
422 428 self.log.debug("Constructing main objects for application")
423 429
424 430 def post_construct(self):
425 431 """Do actions after construct, but before starting the app."""
426 432 pass
427 433
428 434 def start_app(self):
429 435 """Actually start the app."""
430 436 self.log.debug("Starting application")
431 437
432 438 #-------------------------------------------------------------------------
433 439 # Utility methods
434 440 #-------------------------------------------------------------------------
435 441
436 442 def exit(self, exit_status=0):
437 443 if self._exiting:
438 444 pass
439 445 else:
440 446 self.log.debug("Exiting application: %s" % self.name)
441 447 self._exiting = True
442 448 sys.exit(exit_status)
443 449
444 450 def attempt(self, func):
445 451 try:
446 452 func()
447 453 except SystemExit:
448 454 raise
449 455 except:
450 456 self.log.critical("Aborting application: %s" % self.name,
451 457 exc_info=True)
452 458 self.exit(0)
453 459
General Comments 0
You need to be logged in to leave comments. Login now