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