##// END OF EJS Templates
Testing '#:' attribute docstrings for sphinx....
Fernando Perez -
Show More
@@ -1,378 +1,378
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 An application for IPython.
4 An application for IPython.
5
5
6 All top-level applications should use the classes in this module for
6 All top-level applications should use the classes in this module for
7 handling configuration and creating componenets.
7 handling configuration and creating componenets.
8
8
9 The job of an :class:`Application` is to create the master configuration
9 The job of an :class:`Application` is to create the master configuration
10 object and then create the components, passing the config to them.
10 object and then create the components, passing the config to them.
11
11
12 Authors:
12 Authors:
13
13
14 * Brian Granger
14 * Brian Granger
15 * Fernando Perez
15 * Fernando Perez
16
16
17 Notes
17 Notes
18 -----
18 -----
19 """
19 """
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Copyright (C) 2008-2009 The IPython Development Team
22 # Copyright (C) 2008-2009 The IPython Development Team
23 #
23 #
24 # Distributed under the terms of the BSD License. The full license is in
24 # Distributed under the terms of the BSD License. The full license is in
25 # the file COPYING, distributed as part of this software.
25 # the file COPYING, distributed as part of this software.
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29 # Imports
29 # Imports
30 #-----------------------------------------------------------------------------
30 #-----------------------------------------------------------------------------
31
31
32 import logging
32 import logging
33 import os
33 import os
34 import sys
34 import sys
35
35
36 from IPython.core import release
36 from IPython.core import release
37 from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir
37 from IPython.utils.genutils import get_ipython_dir, get_ipython_package_dir
38 from IPython.config.loader import (
38 from IPython.config.loader import (
39 PyFileConfigLoader,
39 PyFileConfigLoader,
40 ArgParseConfigLoader,
40 ArgParseConfigLoader,
41 Config,
41 Config,
42 NoConfigDefault
42 NoConfigDefault
43 )
43 )
44
44
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46 # Classes and functions
46 # Classes and functions
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48
48
49
49
50 class BaseAppArgParseConfigLoader(ArgParseConfigLoader):
50 class BaseAppArgParseConfigLoader(ArgParseConfigLoader):
51 """Default command line options for IPython based applications."""
51 """Default command line options for IPython based applications."""
52
52
53 def _add_other_arguments(self):
53 def _add_other_arguments(self):
54 self.parser.add_argument('--ipython-dir',
54 self.parser.add_argument('--ipython-dir',
55 dest='Global.ipython_dir',type=unicode,
55 dest='Global.ipython_dir',type=unicode,
56 help='Set to override default location of Global.ipython_dir.',
56 help='Set to override default location of Global.ipython_dir.',
57 default=NoConfigDefault,
57 default=NoConfigDefault,
58 metavar='Global.ipython_dir')
58 metavar='Global.ipython_dir')
59 self.parser.add_argument('-p', '--profile',
59 self.parser.add_argument('-p', '--profile',
60 dest='Global.profile',type=unicode,
60 dest='Global.profile',type=unicode,
61 help='The string name of the ipython profile to be used.',
61 help='The string name of the ipython profile to be used.',
62 default=NoConfigDefault,
62 default=NoConfigDefault,
63 metavar='Global.profile')
63 metavar='Global.profile')
64 self.parser.add_argument('--log-level',
64 self.parser.add_argument('--log-level',
65 dest="Global.log_level",type=int,
65 dest="Global.log_level",type=int,
66 help='Set the log level (0,10,20,30,40,50). Default is 30.',
66 help='Set the log level (0,10,20,30,40,50). Default is 30.',
67 default=NoConfigDefault,
67 default=NoConfigDefault,
68 metavar='Global.log_level')
68 metavar='Global.log_level')
69 self.parser.add_argument('--config-file',
69 self.parser.add_argument('--config-file',
70 dest='Global.config_file',type=unicode,
70 dest='Global.config_file',type=unicode,
71 help='Set the config file name to override default.',
71 help='Set the config file name to override default.',
72 default=NoConfigDefault,
72 default=NoConfigDefault,
73 metavar='Global.config_file')
73 metavar='Global.config_file')
74
74
75
75
76 class ApplicationError(Exception):
76 class ApplicationError(Exception):
77 pass
77 pass
78
78
79
79
80 class Application(object):
80 class Application(object):
81 """Load a config, construct components and set them running."""
81 """Load a config, construct components and set them running."""
82
82
83 name = u'ipython'
83 name = u'ipython'
84 description = 'IPython: an enhanced interactive Python shell.'
84 description = 'IPython: an enhanced interactive Python shell.'
85
85
86 config_file_name = u'ipython_config.py'
86 config_file_name = u'ipython_config.py'
87 # Track the default and actual separately because some messages are
87 # Track the default and actual separately because some messages are
88 # only printed if we aren't using the default.
88 # only printed if we aren't using the default.
89 default_config_file_name = config_file_name
89 default_config_file_name = config_file_name
90 default_log_level = logging.WARN
90 default_log_level = logging.WARN
91 # Set by --profile option
91 # Set by --profile option
92 profile_name = None
92 profile_name = None
93 # User's ipython directory, typically ~/.ipython/
93 #: User's ipython directory, typically ~/.ipython/
94 ipython_dir = None
94 ipython_dir = None
95
95
96 # Private attributes
96 # Private attributes
97 _exiting = False
97 _exiting = False
98
98
99 def __init__(self):
99 def __init__(self):
100 self.init_logger()
100 self.init_logger()
101
101
102 def init_logger(self):
102 def init_logger(self):
103 self.log = logging.getLogger(self.__class__.__name__)
103 self.log = logging.getLogger(self.__class__.__name__)
104 # This is used as the default until the command line arguments are read.
104 # This is used as the default until the command line arguments are read.
105 self.log.setLevel(self.default_log_level)
105 self.log.setLevel(self.default_log_level)
106 self._log_handler = logging.StreamHandler()
106 self._log_handler = logging.StreamHandler()
107 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
107 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
108 self._log_handler.setFormatter(self._log_formatter)
108 self._log_handler.setFormatter(self._log_formatter)
109 self.log.addHandler(self._log_handler)
109 self.log.addHandler(self._log_handler)
110
110
111 def _set_log_level(self, level):
111 def _set_log_level(self, level):
112 self.log.setLevel(level)
112 self.log.setLevel(level)
113
113
114 def _get_log_level(self):
114 def _get_log_level(self):
115 return self.log.level
115 return self.log.level
116
116
117 log_level = property(_get_log_level, _set_log_level)
117 log_level = property(_get_log_level, _set_log_level)
118
118
119 def start(self):
119 def start(self):
120 """Start the application."""
120 """Start the application."""
121 self.attempt(self.create_default_config)
121 self.attempt(self.create_default_config)
122 self.log_default_config()
122 self.log_default_config()
123 self.set_default_config_log_level()
123 self.set_default_config_log_level()
124 self.attempt(self.pre_load_command_line_config)
124 self.attempt(self.pre_load_command_line_config)
125 self.attempt(self.load_command_line_config, action='abort')
125 self.attempt(self.load_command_line_config, action='abort')
126 self.set_command_line_config_log_level()
126 self.set_command_line_config_log_level()
127 self.attempt(self.post_load_command_line_config)
127 self.attempt(self.post_load_command_line_config)
128 self.log_command_line_config()
128 self.log_command_line_config()
129 self.attempt(self.find_ipython_dir)
129 self.attempt(self.find_ipython_dir)
130 self.attempt(self.find_resources)
130 self.attempt(self.find_resources)
131 self.attempt(self.find_config_file_name)
131 self.attempt(self.find_config_file_name)
132 self.attempt(self.find_config_file_paths)
132 self.attempt(self.find_config_file_paths)
133 self.attempt(self.pre_load_file_config)
133 self.attempt(self.pre_load_file_config)
134 self.attempt(self.load_file_config)
134 self.attempt(self.load_file_config)
135 self.set_file_config_log_level()
135 self.set_file_config_log_level()
136 self.attempt(self.post_load_file_config)
136 self.attempt(self.post_load_file_config)
137 self.log_file_config()
137 self.log_file_config()
138 self.attempt(self.merge_configs)
138 self.attempt(self.merge_configs)
139 self.log_master_config()
139 self.log_master_config()
140 self.attempt(self.pre_construct)
140 self.attempt(self.pre_construct)
141 self.attempt(self.construct)
141 self.attempt(self.construct)
142 self.attempt(self.post_construct)
142 self.attempt(self.post_construct)
143 self.attempt(self.start_app)
143 self.attempt(self.start_app)
144
144
145 #-------------------------------------------------------------------------
145 #-------------------------------------------------------------------------
146 # Various stages of Application creation
146 # Various stages of Application creation
147 #-------------------------------------------------------------------------
147 #-------------------------------------------------------------------------
148
148
149 def create_default_config(self):
149 def create_default_config(self):
150 """Create defaults that can't be set elsewhere.
150 """Create defaults that can't be set elsewhere.
151
151
152 For the most part, we try to set default in the class attributes
152 For the most part, we try to set default in the class attributes
153 of Components. But, defaults the top-level Application (which is
153 of Components. But, defaults the top-level Application (which is
154 not a HasTraitlets or Component) are not set in this way. Instead
154 not a HasTraitlets or Component) are not set in this way. Instead
155 we set them here. The Global section is for variables like this that
155 we set them here. The Global section is for variables like this that
156 don't belong to a particular component.
156 don't belong to a particular component.
157 """
157 """
158 c = Config()
158 c = Config()
159 c.Global.ipython_dir = get_ipython_dir()
159 c.Global.ipython_dir = get_ipython_dir()
160 c.Global.log_level = self.log_level
160 c.Global.log_level = self.log_level
161 self.default_config = c
161 self.default_config = c
162
162
163 def log_default_config(self):
163 def log_default_config(self):
164 self.log.debug('Default config loaded:')
164 self.log.debug('Default config loaded:')
165 self.log.debug(repr(self.default_config))
165 self.log.debug(repr(self.default_config))
166
166
167 def set_default_config_log_level(self):
167 def set_default_config_log_level(self):
168 try:
168 try:
169 self.log_level = self.default_config.Global.log_level
169 self.log_level = self.default_config.Global.log_level
170 except AttributeError:
170 except AttributeError:
171 # Fallback to the default_log_level class attribute
171 # Fallback to the default_log_level class attribute
172 pass
172 pass
173
173
174 def create_command_line_config(self):
174 def create_command_line_config(self):
175 """Create and return a command line config loader."""
175 """Create and return a command line config loader."""
176 return BaseAppArgParseConfigLoader(
176 return BaseAppArgParseConfigLoader(
177 description=self.description,
177 description=self.description,
178 version=release.version
178 version=release.version
179 )
179 )
180
180
181 def pre_load_command_line_config(self):
181 def pre_load_command_line_config(self):
182 """Do actions just before loading the command line config."""
182 """Do actions just before loading the command line config."""
183 pass
183 pass
184
184
185 def load_command_line_config(self):
185 def load_command_line_config(self):
186 """Load the command line config."""
186 """Load the command line config."""
187 loader = self.create_command_line_config()
187 loader = self.create_command_line_config()
188 self.command_line_config = loader.load_config()
188 self.command_line_config = loader.load_config()
189 self.extra_args = loader.get_extra_args()
189 self.extra_args = loader.get_extra_args()
190
190
191 def set_command_line_config_log_level(self):
191 def set_command_line_config_log_level(self):
192 try:
192 try:
193 self.log_level = self.command_line_config.Global.log_level
193 self.log_level = self.command_line_config.Global.log_level
194 except AttributeError:
194 except AttributeError:
195 pass
195 pass
196
196
197 def post_load_command_line_config(self):
197 def post_load_command_line_config(self):
198 """Do actions just after loading the command line config."""
198 """Do actions just after loading the command line config."""
199 pass
199 pass
200
200
201 def log_command_line_config(self):
201 def log_command_line_config(self):
202 self.log.debug("Command line config loaded:")
202 self.log.debug("Command line config loaded:")
203 self.log.debug(repr(self.command_line_config))
203 self.log.debug(repr(self.command_line_config))
204
204
205 def find_ipython_dir(self):
205 def find_ipython_dir(self):
206 """Set the IPython directory.
206 """Set the IPython directory.
207
207
208 This sets ``self.ipython_dir``, but the actual value that is passed to
208 This sets ``self.ipython_dir``, but the actual value that is passed to
209 the application is kept in either ``self.default_config`` or
209 the application is kept in either ``self.default_config`` or
210 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
210 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
211 ``sys.path`` so config files there can be referenced by other config
211 ``sys.path`` so config files there can be referenced by other config
212 files.
212 files.
213 """
213 """
214
214
215 try:
215 try:
216 self.ipython_dir = self.command_line_config.Global.ipython_dir
216 self.ipython_dir = self.command_line_config.Global.ipython_dir
217 except AttributeError:
217 except AttributeError:
218 self.ipython_dir = self.default_config.Global.ipython_dir
218 self.ipython_dir = self.default_config.Global.ipython_dir
219 sys.path.append(os.path.abspath(self.ipython_dir))
219 sys.path.append(os.path.abspath(self.ipython_dir))
220 if not os.path.isdir(self.ipython_dir):
220 if not os.path.isdir(self.ipython_dir):
221 os.makedirs(self.ipython_dir, mode=0777)
221 os.makedirs(self.ipython_dir, mode=0777)
222 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
222 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
223
223
224 def find_resources(self):
224 def find_resources(self):
225 """Find other resources that need to be in place.
225 """Find other resources that need to be in place.
226
226
227 Things like cluster directories need to be in place to find the
227 Things like cluster directories need to be in place to find the
228 config file. These happen right after the IPython directory has
228 config file. These happen right after the IPython directory has
229 been set.
229 been set.
230 """
230 """
231 pass
231 pass
232
232
233 def find_config_file_name(self):
233 def find_config_file_name(self):
234 """Find the config file name for this application.
234 """Find the config file name for this application.
235
235
236 This must set ``self.config_file_name`` to the filename of the
236 This must set ``self.config_file_name`` to the filename of the
237 config file to use (just the filename). The search paths for the
237 config file to use (just the filename). The search paths for the
238 config file are set in :meth:`find_config_file_paths` and then passed
238 config file are set in :meth:`find_config_file_paths` and then passed
239 to the config file loader where they are resolved to an absolute path.
239 to the config file loader where they are resolved to an absolute path.
240
240
241 If a profile has been set at the command line, this will resolve it.
241 If a profile has been set at the command line, this will resolve it.
242 """
242 """
243
243
244 try:
244 try:
245 self.config_file_name = self.command_line_config.Global.config_file
245 self.config_file_name = self.command_line_config.Global.config_file
246 except AttributeError:
246 except AttributeError:
247 pass
247 pass
248
248
249 try:
249 try:
250 self.profile_name = self.command_line_config.Global.profile
250 self.profile_name = self.command_line_config.Global.profile
251 except AttributeError:
251 except AttributeError:
252 pass
252 pass
253 else:
253 else:
254 name_parts = self.config_file_name.split('.')
254 name_parts = self.config_file_name.split('.')
255 name_parts.insert(1, u'_' + self.profile_name + u'.')
255 name_parts.insert(1, u'_' + self.profile_name + u'.')
256 self.config_file_name = ''.join(name_parts)
256 self.config_file_name = ''.join(name_parts)
257
257
258 def find_config_file_paths(self):
258 def find_config_file_paths(self):
259 """Set the search paths for resolving the config file.
259 """Set the search paths for resolving the config file.
260
260
261 This must set ``self.config_file_paths`` to a sequence of search
261 This must set ``self.config_file_paths`` to a sequence of search
262 paths to pass to the config file loader.
262 paths to pass to the config file loader.
263 """
263 """
264 # Include our own profiles directory last, so that users can still find
264 # Include our own profiles directory last, so that users can still find
265 # our shipped copies of builtin profiles even if they don't have them
265 # our shipped copies of builtin profiles even if they don't have them
266 # in their local ipython directory.
266 # in their local ipython directory.
267 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
267 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
268 self.config_file_paths = (os.getcwd(), self.ipython_dir, prof_dir)
268 self.config_file_paths = (os.getcwd(), self.ipython_dir, prof_dir)
269
269
270 def pre_load_file_config(self):
270 def pre_load_file_config(self):
271 """Do actions before the config file is loaded."""
271 """Do actions before the config file is loaded."""
272 pass
272 pass
273
273
274 def load_file_config(self):
274 def load_file_config(self):
275 """Load the config file.
275 """Load the config file.
276
276
277 This tries to load the config file from disk. If successful, the
277 This tries to load the config file from disk. If successful, the
278 ``CONFIG_FILE`` config variable is set to the resolved config file
278 ``CONFIG_FILE`` config variable is set to the resolved config file
279 location. If not successful, an empty config is used.
279 location. If not successful, an empty config is used.
280 """
280 """
281 self.log.debug("Attempting to load config file: %s" %
281 self.log.debug("Attempting to load config file: %s" %
282 self.config_file_name)
282 self.config_file_name)
283 loader = PyFileConfigLoader(self.config_file_name,
283 loader = PyFileConfigLoader(self.config_file_name,
284 path=self.config_file_paths)
284 path=self.config_file_paths)
285 try:
285 try:
286 self.file_config = loader.load_config()
286 self.file_config = loader.load_config()
287 self.file_config.Global.config_file = loader.full_filename
287 self.file_config.Global.config_file = loader.full_filename
288 except IOError:
288 except IOError:
289 # Only warn if the default config file was NOT being used.
289 # Only warn if the default config file was NOT being used.
290 if not self.config_file_name==self.default_config_file_name:
290 if not self.config_file_name==self.default_config_file_name:
291 self.log.warn("Config file not found, skipping: %s" %
291 self.log.warn("Config file not found, skipping: %s" %
292 self.config_file_name, exc_info=True)
292 self.config_file_name, exc_info=True)
293 self.file_config = Config()
293 self.file_config = Config()
294 except:
294 except:
295 self.log.warn("Error loading config file: %s" %
295 self.log.warn("Error loading config file: %s" %
296 self.config_file_name, exc_info=True)
296 self.config_file_name, exc_info=True)
297 self.file_config = Config()
297 self.file_config = Config()
298
298
299 def set_file_config_log_level(self):
299 def set_file_config_log_level(self):
300 # We need to keeep self.log_level updated. But we only use the value
300 # We need to keeep self.log_level updated. But we only use the value
301 # of the file_config if a value was not specified at the command
301 # of the file_config if a value was not specified at the command
302 # line, because the command line overrides everything.
302 # line, because the command line overrides everything.
303 if not hasattr(self.command_line_config.Global, 'log_level'):
303 if not hasattr(self.command_line_config.Global, 'log_level'):
304 try:
304 try:
305 self.log_level = self.file_config.Global.log_level
305 self.log_level = self.file_config.Global.log_level
306 except AttributeError:
306 except AttributeError:
307 pass # Use existing value
307 pass # Use existing value
308
308
309 def post_load_file_config(self):
309 def post_load_file_config(self):
310 """Do actions after the config file is loaded."""
310 """Do actions after the config file is loaded."""
311 pass
311 pass
312
312
313 def log_file_config(self):
313 def log_file_config(self):
314 if hasattr(self.file_config.Global, 'config_file'):
314 if hasattr(self.file_config.Global, 'config_file'):
315 self.log.debug("Config file loaded: %s" %
315 self.log.debug("Config file loaded: %s" %
316 self.file_config.Global.config_file)
316 self.file_config.Global.config_file)
317 self.log.debug(repr(self.file_config))
317 self.log.debug(repr(self.file_config))
318
318
319 def merge_configs(self):
319 def merge_configs(self):
320 """Merge the default, command line and file config objects."""
320 """Merge the default, command line and file config objects."""
321 config = Config()
321 config = Config()
322 config._merge(self.default_config)
322 config._merge(self.default_config)
323 config._merge(self.file_config)
323 config._merge(self.file_config)
324 config._merge(self.command_line_config)
324 config._merge(self.command_line_config)
325 self.master_config = config
325 self.master_config = config
326
326
327 def log_master_config(self):
327 def log_master_config(self):
328 self.log.debug("Master config created:")
328 self.log.debug("Master config created:")
329 self.log.debug(repr(self.master_config))
329 self.log.debug(repr(self.master_config))
330
330
331 def pre_construct(self):
331 def pre_construct(self):
332 """Do actions after the config has been built, but before construct."""
332 """Do actions after the config has been built, but before construct."""
333 pass
333 pass
334
334
335 def construct(self):
335 def construct(self):
336 """Construct the main components that make up this app."""
336 """Construct the main components that make up this app."""
337 self.log.debug("Constructing components for application")
337 self.log.debug("Constructing components for application")
338
338
339 def post_construct(self):
339 def post_construct(self):
340 """Do actions after construct, but before starting the app."""
340 """Do actions after construct, but before starting the app."""
341 pass
341 pass
342
342
343 def start_app(self):
343 def start_app(self):
344 """Actually start the app."""
344 """Actually start the app."""
345 self.log.debug("Starting application")
345 self.log.debug("Starting application")
346
346
347 #-------------------------------------------------------------------------
347 #-------------------------------------------------------------------------
348 # Utility methods
348 # Utility methods
349 #-------------------------------------------------------------------------
349 #-------------------------------------------------------------------------
350
350
351 def abort(self):
351 def abort(self):
352 """Abort the starting of the application."""
352 """Abort the starting of the application."""
353 if self._exiting:
353 if self._exiting:
354 pass
354 pass
355 else:
355 else:
356 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
356 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
357 self._exiting = True
357 self._exiting = True
358 sys.exit(1)
358 sys.exit(1)
359
359
360 def exit(self, exit_status=0):
360 def exit(self, exit_status=0):
361 if self._exiting:
361 if self._exiting:
362 pass
362 pass
363 else:
363 else:
364 self.log.debug("Exiting application: %s" % self.name)
364 self.log.debug("Exiting application: %s" % self.name)
365 self._exiting = True
365 self._exiting = True
366 sys.exit(exit_status)
366 sys.exit(exit_status)
367
367
368 def attempt(self, func, action='abort'):
368 def attempt(self, func, action='abort'):
369 try:
369 try:
370 func()
370 func()
371 except SystemExit:
371 except SystemExit:
372 raise
372 raise
373 except:
373 except:
374 if action == 'abort':
374 if action == 'abort':
375 self.abort()
375 self.abort()
376 elif action == 'exit':
376 elif action == 'exit':
377 self.exit(0)
377 self.exit(0)
378
378
General Comments 0
You need to be logged in to leave comments. Login now