##// END OF EJS Templates
Initial support for %pylab magic to load pylab at runtime....
Fernando Perez -
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,377 +1,377 b''
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 self.default_config = Config()
158 self.default_config = Config()
159 self.default_config.Global.ipython_dir = get_ipython_dir()
159 self.default_config.Global.ipython_dir = get_ipython_dir()
160 self.default_config.Global.log_level = self.log_level
160 self.default_config.Global.log_level = self.log_level
161
161
162 def log_default_config(self):
162 def log_default_config(self):
163 self.log.debug('Default config loaded:')
163 self.log.debug('Default config loaded:')
164 self.log.debug(repr(self.default_config))
164 self.log.debug(repr(self.default_config))
165
165
166 def set_default_config_log_level(self):
166 def set_default_config_log_level(self):
167 try:
167 try:
168 self.log_level = self.default_config.Global.log_level
168 self.log_level = self.default_config.Global.log_level
169 except AttributeError:
169 except AttributeError:
170 # Fallback to the default_log_level class attribute
170 # Fallback to the default_log_level class attribute
171 pass
171 pass
172
172
173 def create_command_line_config(self):
173 def create_command_line_config(self):
174 """Create and return a command line config loader."""
174 """Create and return a command line config loader."""
175 return BaseAppArgParseConfigLoader(
175 return BaseAppArgParseConfigLoader(
176 description=self.description,
176 description=self.description,
177 version=release.version
177 version=release.version
178 )
178 )
179
179
180 def pre_load_command_line_config(self):
180 def pre_load_command_line_config(self):
181 """Do actions just before loading the command line config."""
181 """Do actions just before loading the command line config."""
182 pass
182 pass
183
183
184 def load_command_line_config(self):
184 def load_command_line_config(self):
185 """Load the command line config."""
185 """Load the command line config."""
186 loader = self.create_command_line_config()
186 loader = self.create_command_line_config()
187 self.command_line_config = loader.load_config()
187 self.command_line_config = loader.load_config()
188 self.extra_args = loader.get_extra_args()
188 self.extra_args = loader.get_extra_args()
189
189
190 def set_command_line_config_log_level(self):
190 def set_command_line_config_log_level(self):
191 try:
191 try:
192 self.log_level = self.command_line_config.Global.log_level
192 self.log_level = self.command_line_config.Global.log_level
193 except AttributeError:
193 except AttributeError:
194 pass
194 pass
195
195
196 def post_load_command_line_config(self):
196 def post_load_command_line_config(self):
197 """Do actions just after loading the command line config."""
197 """Do actions just after loading the command line config."""
198 pass
198 pass
199
199
200 def log_command_line_config(self):
200 def log_command_line_config(self):
201 self.log.debug("Command line config loaded:")
201 self.log.debug("Command line config loaded:")
202 self.log.debug(repr(self.command_line_config))
202 self.log.debug(repr(self.command_line_config))
203
203
204 def find_ipython_dir(self):
204 def find_ipython_dir(self):
205 """Set the IPython directory.
205 """Set the IPython directory.
206
206
207 This sets ``self.ipython_dir``, but the actual value that is passed to
207 This sets ``self.ipython_dir``, but the actual value that is passed to
208 the application is kept in either ``self.default_config`` or
208 the application is kept in either ``self.default_config`` or
209 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
209 ``self.command_line_config``. This also adds ``self.ipython_dir`` to
210 ``sys.path`` so config files there can be referenced by other config
210 ``sys.path`` so config files there can be referenced by other config
211 files.
211 files.
212 """
212 """
213
213
214 try:
214 try:
215 self.ipython_dir = self.command_line_config.Global.ipython_dir
215 self.ipython_dir = self.command_line_config.Global.ipython_dir
216 except AttributeError:
216 except AttributeError:
217 self.ipython_dir = self.default_config.Global.ipython_dir
217 self.ipython_dir = self.default_config.Global.ipython_dir
218 sys.path.append(os.path.abspath(self.ipython_dir))
218 sys.path.append(os.path.abspath(self.ipython_dir))
219 if not os.path.isdir(self.ipython_dir):
219 if not os.path.isdir(self.ipython_dir):
220 os.makedirs(self.ipython_dir, mode=0777)
220 os.makedirs(self.ipython_dir, mode=0777)
221 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
221 self.log.debug("IPYTHON_DIR set to: %s" % self.ipython_dir)
222
222
223 def find_resources(self):
223 def find_resources(self):
224 """Find other resources that need to be in place.
224 """Find other resources that need to be in place.
225
225
226 Things like cluster directories need to be in place to find the
226 Things like cluster directories need to be in place to find the
227 config file. These happen right after the IPython directory has
227 config file. These happen right after the IPython directory has
228 been set.
228 been set.
229 """
229 """
230 pass
230 pass
231
231
232 def find_config_file_name(self):
232 def find_config_file_name(self):
233 """Find the config file name for this application.
233 """Find the config file name for this application.
234
234
235 This must set ``self.config_file_name`` to the filename of the
235 This must set ``self.config_file_name`` to the filename of the
236 config file to use (just the filename). The search paths for the
236 config file to use (just the filename). The search paths for the
237 config file are set in :meth:`find_config_file_paths` and then passed
237 config file are set in :meth:`find_config_file_paths` and then passed
238 to the config file loader where they are resolved to an absolute path.
238 to the config file loader where they are resolved to an absolute path.
239
239
240 If a profile has been set at the command line, this will resolve it.
240 If a profile has been set at the command line, this will resolve it.
241 """
241 """
242
242
243 try:
243 try:
244 self.config_file_name = self.command_line_config.Global.config_file
244 self.config_file_name = self.command_line_config.Global.config_file
245 except AttributeError:
245 except AttributeError:
246 pass
246 pass
247
247
248 try:
248 try:
249 self.profile_name = self.command_line_config.Global.profile
249 self.profile_name = self.command_line_config.Global.profile
250 except AttributeError:
250 except AttributeError:
251 pass
251 pass
252 else:
252 else:
253 name_parts = self.config_file_name.split('.')
253 name_parts = self.config_file_name.split('.')
254 name_parts.insert(1, u'_' + self.profile_name + u'.')
254 name_parts.insert(1, u'_' + self.profile_name + u'.')
255 self.config_file_name = ''.join(name_parts)
255 self.config_file_name = ''.join(name_parts)
256
256
257 def find_config_file_paths(self):
257 def find_config_file_paths(self):
258 """Set the search paths for resolving the config file.
258 """Set the search paths for resolving the config file.
259
259
260 This must set ``self.config_file_paths`` to a sequence of search
260 This must set ``self.config_file_paths`` to a sequence of search
261 paths to pass to the config file loader.
261 paths to pass to the config file loader.
262 """
262 """
263 # Include our own profiles directory last, so that users can still find
263 # Include our own profiles directory last, so that users can still find
264 # our shipped copies of builtin profiles even if they don't have them
264 # our shipped copies of builtin profiles even if they don't have them
265 # in their local ipython directory.
265 # in their local ipython directory.
266 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
266 prof_dir = os.path.join(get_ipython_package_dir(), 'config', 'profile')
267 self.config_file_paths = (os.getcwd(), self.ipython_dir,prof_dir)
267 self.config_file_paths = (os.getcwd(), self.ipython_dir, prof_dir)
268
268
269 def pre_load_file_config(self):
269 def pre_load_file_config(self):
270 """Do actions before the config file is loaded."""
270 """Do actions before the config file is loaded."""
271 pass
271 pass
272
272
273 def load_file_config(self):
273 def load_file_config(self):
274 """Load the config file.
274 """Load the config file.
275
275
276 This tries to load the config file from disk. If successful, the
276 This tries to load the config file from disk. If successful, the
277 ``CONFIG_FILE`` config variable is set to the resolved config file
277 ``CONFIG_FILE`` config variable is set to the resolved config file
278 location. If not successful, an empty config is used.
278 location. If not successful, an empty config is used.
279 """
279 """
280 self.log.debug("Attempting to load config file: %s" %
280 self.log.debug("Attempting to load config file: %s" %
281 self.config_file_name)
281 self.config_file_name)
282 loader = PyFileConfigLoader(self.config_file_name,
282 loader = PyFileConfigLoader(self.config_file_name,
283 path=self.config_file_paths)
283 path=self.config_file_paths)
284 try:
284 try:
285 self.file_config = loader.load_config()
285 self.file_config = loader.load_config()
286 self.file_config.Global.config_file = loader.full_filename
286 self.file_config.Global.config_file = loader.full_filename
287 except IOError:
287 except IOError:
288 # Only warn if the default config file was NOT being used.
288 # Only warn if the default config file was NOT being used.
289 if not self.config_file_name==self.default_config_file_name:
289 if not self.config_file_name==self.default_config_file_name:
290 self.log.warn("Config file not found, skipping: %s" %
290 self.log.warn("Config file not found, skipping: %s" %
291 self.config_file_name, exc_info=True)
291 self.config_file_name, exc_info=True)
292 self.file_config = Config()
292 self.file_config = Config()
293 except:
293 except:
294 self.log.warn("Error loading config file: %s" %
294 self.log.warn("Error loading config file: %s" %
295 self.config_file_name, exc_info=True)
295 self.config_file_name, exc_info=True)
296 self.file_config = Config()
296 self.file_config = Config()
297
297
298 def set_file_config_log_level(self):
298 def set_file_config_log_level(self):
299 # We need to keeep self.log_level updated. But we only use the value
299 # We need to keeep self.log_level updated. But we only use the value
300 # of the file_config if a value was not specified at the command
300 # of the file_config if a value was not specified at the command
301 # line, because the command line overrides everything.
301 # line, because the command line overrides everything.
302 if not hasattr(self.command_line_config.Global, 'log_level'):
302 if not hasattr(self.command_line_config.Global, 'log_level'):
303 try:
303 try:
304 self.log_level = self.file_config.Global.log_level
304 self.log_level = self.file_config.Global.log_level
305 except AttributeError:
305 except AttributeError:
306 pass # Use existing value
306 pass # Use existing value
307
307
308 def post_load_file_config(self):
308 def post_load_file_config(self):
309 """Do actions after the config file is loaded."""
309 """Do actions after the config file is loaded."""
310 pass
310 pass
311
311
312 def log_file_config(self):
312 def log_file_config(self):
313 if hasattr(self.file_config.Global, 'config_file'):
313 if hasattr(self.file_config.Global, 'config_file'):
314 self.log.debug("Config file loaded: %s" %
314 self.log.debug("Config file loaded: %s" %
315 self.file_config.Global.config_file)
315 self.file_config.Global.config_file)
316 self.log.debug(repr(self.file_config))
316 self.log.debug(repr(self.file_config))
317
317
318 def merge_configs(self):
318 def merge_configs(self):
319 """Merge the default, command line and file config objects."""
319 """Merge the default, command line and file config objects."""
320 config = Config()
320 config = Config()
321 config._merge(self.default_config)
321 config._merge(self.default_config)
322 config._merge(self.file_config)
322 config._merge(self.file_config)
323 config._merge(self.command_line_config)
323 config._merge(self.command_line_config)
324 self.master_config = config
324 self.master_config = config
325
325
326 def log_master_config(self):
326 def log_master_config(self):
327 self.log.debug("Master config created:")
327 self.log.debug("Master config created:")
328 self.log.debug(repr(self.master_config))
328 self.log.debug(repr(self.master_config))
329
329
330 def pre_construct(self):
330 def pre_construct(self):
331 """Do actions after the config has been built, but before construct."""
331 """Do actions after the config has been built, but before construct."""
332 pass
332 pass
333
333
334 def construct(self):
334 def construct(self):
335 """Construct the main components that make up this app."""
335 """Construct the main components that make up this app."""
336 self.log.debug("Constructing components for application")
336 self.log.debug("Constructing components for application")
337
337
338 def post_construct(self):
338 def post_construct(self):
339 """Do actions after construct, but before starting the app."""
339 """Do actions after construct, but before starting the app."""
340 pass
340 pass
341
341
342 def start_app(self):
342 def start_app(self):
343 """Actually start the app."""
343 """Actually start the app."""
344 self.log.debug("Starting application")
344 self.log.debug("Starting application")
345
345
346 #-------------------------------------------------------------------------
346 #-------------------------------------------------------------------------
347 # Utility methods
347 # Utility methods
348 #-------------------------------------------------------------------------
348 #-------------------------------------------------------------------------
349
349
350 def abort(self):
350 def abort(self):
351 """Abort the starting of the application."""
351 """Abort the starting of the application."""
352 if self._exiting:
352 if self._exiting:
353 pass
353 pass
354 else:
354 else:
355 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
355 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
356 self._exiting = True
356 self._exiting = True
357 sys.exit(1)
357 sys.exit(1)
358
358
359 def exit(self, exit_status=0):
359 def exit(self, exit_status=0):
360 if self._exiting:
360 if self._exiting:
361 pass
361 pass
362 else:
362 else:
363 self.log.debug("Exiting application: %s" % self.name)
363 self.log.debug("Exiting application: %s" % self.name)
364 self._exiting = True
364 self._exiting = True
365 sys.exit(exit_status)
365 sys.exit(exit_status)
366
366
367 def attempt(self, func, action='abort'):
367 def attempt(self, func, action='abort'):
368 try:
368 try:
369 func()
369 func()
370 except SystemExit:
370 except SystemExit:
371 raise
371 raise
372 except:
372 except:
373 if action == 'abort':
373 if action == 'abort':
374 self.abort()
374 self.abort()
375 elif action == 'exit':
375 elif action == 'exit':
376 self.exit(0)
376 self.exit(0)
377
377
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now