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