##// END OF EJS Templates
fix error in doc (arg->kwarg) and pep-8
Matthias BUSSONNIER -
Show More
@@ -1,356 +1,356 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A base class for objects that are configurable.
3 A base class for objects that are configurable.
4
4
5 Inheritance diagram:
5 Inheritance diagram:
6
6
7 .. inheritance-diagram:: IPython.config.configurable
7 .. inheritance-diagram:: IPython.config.configurable
8 :parts: 3
8 :parts: 3
9
9
10 Authors:
10 Authors:
11
11
12 * Brian Granger
12 * Brian Granger
13 * Fernando Perez
13 * Fernando Perez
14 * Min RK
14 * Min RK
15 """
15 """
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2008-2011 The IPython Development Team
18 # Copyright (C) 2008-2011 The IPython Development Team
19 #
19 #
20 # Distributed under the terms of the BSD License. The full license is in
20 # Distributed under the terms of the BSD License. The full license is in
21 # the file COPYING, distributed as part of this software.
21 # the file COPYING, distributed as part of this software.
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Imports
25 # Imports
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 import datetime
28 import datetime
29 from copy import deepcopy
29 from copy import deepcopy
30
30
31 from loader import Config
31 from loader import Config
32 from IPython.utils.traitlets import HasTraits, Instance
32 from IPython.utils.traitlets import HasTraits, Instance
33 from IPython.utils.text import indent, wrap_paragraphs
33 from IPython.utils.text import indent, wrap_paragraphs
34
34
35
35
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37 # Helper classes for Configurables
37 # Helper classes for Configurables
38 #-----------------------------------------------------------------------------
38 #-----------------------------------------------------------------------------
39
39
40
40
41 class ConfigurableError(Exception):
41 class ConfigurableError(Exception):
42 pass
42 pass
43
43
44
44
45 class MultipleInstanceError(ConfigurableError):
45 class MultipleInstanceError(ConfigurableError):
46 pass
46 pass
47
47
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49 # Configurable implementation
49 # Configurable implementation
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51
51
52 class Configurable(HasTraits):
52 class Configurable(HasTraits):
53
53
54 config = Instance(Config,(),{})
54 config = Instance(Config, (), {})
55 created = None
55 created = None
56
56
57 def __init__(self, **kwargs):
57 def __init__(self, **kwargs):
58 """Create a configurable given a config config.
58 """Create a configurable given a config config.
59
59
60 Parameters
60 Parameters
61 ----------
61 ----------
62 config : Config
62 config : Config
63 If this is empty, default values are used. If config is a
63 If this is empty, default values are used. If config is a
64 :class:`Config` instance, it will be used to configure the
64 :class:`Config` instance, it will be used to configure the
65 instance.
65 instance.
66
66
67 Notes
67 Notes
68 -----
68 -----
69 Subclasses of Configurable must call the :meth:`__init__` method of
69 Subclasses of Configurable must call the :meth:`__init__` method of
70 :class:`Configurable` *before* doing anything else and using
70 :class:`Configurable` *before* doing anything else and using
71 :func:`super`::
71 :func:`super`::
72
72
73 class MyConfigurable(Configurable):
73 class MyConfigurable(Configurable):
74 def __init__(self, config=None):
74 def __init__(self, config=None):
75 super(MyConfigurable, self).__init__(config)
75 super(MyConfigurable, self).__init__(config=config)
76 # Then any other code you need to finish initialization.
76 # Then any other code you need to finish initialization.
77
77
78 This ensures that instances will be configured properly.
78 This ensures that instances will be configured properly.
79 """
79 """
80 config = kwargs.pop('config', None)
80 config = kwargs.pop('config', None)
81 if config is not None:
81 if config is not None:
82 # We used to deepcopy, but for now we are trying to just save
82 # We used to deepcopy, but for now we are trying to just save
83 # by reference. This *could* have side effects as all components
83 # by reference. This *could* have side effects as all components
84 # will share config. In fact, I did find such a side effect in
84 # will share config. In fact, I did find such a side effect in
85 # _config_changed below. If a config attribute value was a mutable type
85 # _config_changed below. If a config attribute value was a mutable type
86 # all instances of a component were getting the same copy, effectively
86 # all instances of a component were getting the same copy, effectively
87 # making that a class attribute.
87 # making that a class attribute.
88 # self.config = deepcopy(config)
88 # self.config = deepcopy(config)
89 self.config = config
89 self.config = config
90 # This should go second so individual keyword arguments override
90 # This should go second so individual keyword arguments override
91 # the values in config.
91 # the values in config.
92 super(Configurable, self).__init__(**kwargs)
92 super(Configurable, self).__init__(**kwargs)
93 self.created = datetime.datetime.now()
93 self.created = datetime.datetime.now()
94
94
95 #-------------------------------------------------------------------------
95 #-------------------------------------------------------------------------
96 # Static trait notifiations
96 # Static trait notifiations
97 #-------------------------------------------------------------------------
97 #-------------------------------------------------------------------------
98
98
99 def _config_changed(self, name, old, new):
99 def _config_changed(self, name, old, new):
100 """Update all the class traits having ``config=True`` as metadata.
100 """Update all the class traits having ``config=True`` as metadata.
101
101
102 For any class trait with a ``config`` metadata attribute that is
102 For any class trait with a ``config`` metadata attribute that is
103 ``True``, we update the trait with the value of the corresponding
103 ``True``, we update the trait with the value of the corresponding
104 config entry.
104 config entry.
105 """
105 """
106 # Get all traits with a config metadata entry that is True
106 # Get all traits with a config metadata entry that is True
107 traits = self.traits(config=True)
107 traits = self.traits(config=True)
108
108
109 # We auto-load config section for this class as well as any parent
109 # We auto-load config section for this class as well as any parent
110 # classes that are Configurable subclasses. This starts with Configurable
110 # classes that are Configurable subclasses. This starts with Configurable
111 # and works down the mro loading the config for each section.
111 # and works down the mro loading the config for each section.
112 section_names = [cls.__name__ for cls in \
112 section_names = [cls.__name__ for cls in \
113 reversed(self.__class__.__mro__) if
113 reversed(self.__class__.__mro__) if
114 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
114 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
115
115
116 for sname in section_names:
116 for sname in section_names:
117 # Don't do a blind getattr as that would cause the config to
117 # Don't do a blind getattr as that would cause the config to
118 # dynamically create the section with name self.__class__.__name__.
118 # dynamically create the section with name self.__class__.__name__.
119 if new._has_section(sname):
119 if new._has_section(sname):
120 my_config = new[sname]
120 my_config = new[sname]
121 for k, v in traits.iteritems():
121 for k, v in traits.iteritems():
122 # Don't allow traitlets with config=True to start with
122 # Don't allow traitlets with config=True to start with
123 # uppercase. Otherwise, they are confused with Config
123 # uppercase. Otherwise, they are confused with Config
124 # subsections. But, developers shouldn't have uppercase
124 # subsections. But, developers shouldn't have uppercase
125 # attributes anyways! (PEP 6)
125 # attributes anyways! (PEP 6)
126 if k[0].upper()==k[0] and not k.startswith('_'):
126 if k[0].upper()==k[0] and not k.startswith('_'):
127 raise ConfigurableError('Configurable traitlets with '
127 raise ConfigurableError('Configurable traitlets with '
128 'config=True must start with a lowercase so they are '
128 'config=True must start with a lowercase so they are '
129 'not confused with Config subsections: %s.%s' % \
129 'not confused with Config subsections: %s.%s' % \
130 (self.__class__.__name__, k))
130 (self.__class__.__name__, k))
131 try:
131 try:
132 # Here we grab the value from the config
132 # Here we grab the value from the config
133 # If k has the naming convention of a config
133 # If k has the naming convention of a config
134 # section, it will be auto created.
134 # section, it will be auto created.
135 config_value = my_config[k]
135 config_value = my_config[k]
136 except KeyError:
136 except KeyError:
137 pass
137 pass
138 else:
138 else:
139 # print "Setting %s.%s from %s.%s=%r" % \
139 # print "Setting %s.%s from %s.%s=%r" % \
140 # (self.__class__.__name__,k,sname,k,config_value)
140 # (self.__class__.__name__,k,sname,k,config_value)
141 # We have to do a deepcopy here if we don't deepcopy the entire
141 # We have to do a deepcopy here if we don't deepcopy the entire
142 # config object. If we don't, a mutable config_value will be
142 # config object. If we don't, a mutable config_value will be
143 # shared by all instances, effectively making it a class attribute.
143 # shared by all instances, effectively making it a class attribute.
144 setattr(self, k, deepcopy(config_value))
144 setattr(self, k, deepcopy(config_value))
145
145
146 def update_config(self, config):
146 def update_config(self, config):
147 """Fire the traits events when the config is updated."""
147 """Fire the traits events when the config is updated."""
148 # Save a copy of the current config.
148 # Save a copy of the current config.
149 newconfig = deepcopy(self.config)
149 newconfig = deepcopy(self.config)
150 # Merge the new config into the current one.
150 # Merge the new config into the current one.
151 newconfig._merge(config)
151 newconfig._merge(config)
152 # Save the combined config as self.config, which triggers the traits
152 # Save the combined config as self.config, which triggers the traits
153 # events.
153 # events.
154 self.config = newconfig
154 self.config = newconfig
155
155
156 @classmethod
156 @classmethod
157 def class_get_help(cls, inst=None):
157 def class_get_help(cls, inst=None):
158 """Get the help string for this class in ReST format.
158 """Get the help string for this class in ReST format.
159
159
160 If `inst` is given, it's current trait values will be used in place of
160 If `inst` is given, it's current trait values will be used in place of
161 class defaults.
161 class defaults.
162 """
162 """
163 assert inst is None or isinstance(inst, cls)
163 assert inst is None or isinstance(inst, cls)
164 cls_traits = cls.class_traits(config=True)
164 cls_traits = cls.class_traits(config=True)
165 final_help = []
165 final_help = []
166 final_help.append(u'%s options' % cls.__name__)
166 final_help.append(u'%s options' % cls.__name__)
167 final_help.append(len(final_help[0])*u'-')
167 final_help.append(len(final_help[0])*u'-')
168 for k,v in sorted(cls.class_traits(config=True).iteritems()):
168 for k, v in sorted(cls.class_traits(config=True).iteritems()):
169 help = cls.class_get_trait_help(v, inst)
169 help = cls.class_get_trait_help(v, inst)
170 final_help.append(help)
170 final_help.append(help)
171 return '\n'.join(final_help)
171 return '\n'.join(final_help)
172
172
173 @classmethod
173 @classmethod
174 def class_get_trait_help(cls, trait, inst=None):
174 def class_get_trait_help(cls, trait, inst=None):
175 """Get the help string for a single trait.
175 """Get the help string for a single trait.
176
176
177 If `inst` is given, it's current trait values will be used in place of
177 If `inst` is given, it's current trait values will be used in place of
178 the class default.
178 the class default.
179 """
179 """
180 assert inst is None or isinstance(inst, cls)
180 assert inst is None or isinstance(inst, cls)
181 lines = []
181 lines = []
182 header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
182 header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
183 lines.append(header)
183 lines.append(header)
184 if inst is not None:
184 if inst is not None:
185 lines.append(indent('Current: %r' % getattr(inst, trait.name), 4))
185 lines.append(indent('Current: %r' % getattr(inst, trait.name), 4))
186 else:
186 else:
187 try:
187 try:
188 dvr = repr(trait.get_default_value())
188 dvr = repr(trait.get_default_value())
189 except Exception:
189 except Exception:
190 dvr = None # ignore defaults we can't construct
190 dvr = None # ignore defaults we can't construct
191 if dvr is not None:
191 if dvr is not None:
192 if len(dvr) > 64:
192 if len(dvr) > 64:
193 dvr = dvr[:61]+'...'
193 dvr = dvr[:61]+'...'
194 lines.append(indent('Default: %s' % dvr, 4))
194 lines.append(indent('Default: %s' % dvr, 4))
195 if 'Enum' in trait.__class__.__name__:
195 if 'Enum' in trait.__class__.__name__:
196 # include Enum choices
196 # include Enum choices
197 lines.append(indent('Choices: %r' % (trait.values,)))
197 lines.append(indent('Choices: %r' % (trait.values,)))
198
198
199 help = trait.get_metadata('help')
199 help = trait.get_metadata('help')
200 if help is not None:
200 if help is not None:
201 help = '\n'.join(wrap_paragraphs(help, 76))
201 help = '\n'.join(wrap_paragraphs(help, 76))
202 lines.append(indent(help, 4))
202 lines.append(indent(help, 4))
203 return '\n'.join(lines)
203 return '\n'.join(lines)
204
204
205 @classmethod
205 @classmethod
206 def class_print_help(cls, inst=None):
206 def class_print_help(cls, inst=None):
207 """Get the help string for a single trait and print it."""
207 """Get the help string for a single trait and print it."""
208 print cls.class_get_help(inst)
208 print cls.class_get_help(inst)
209
209
210 @classmethod
210 @classmethod
211 def class_config_section(cls):
211 def class_config_section(cls):
212 """Get the config class config section"""
212 """Get the config class config section"""
213 def c(s):
213 def c(s):
214 """return a commented, wrapped block."""
214 """return a commented, wrapped block."""
215 s = '\n\n'.join(wrap_paragraphs(s, 78))
215 s = '\n\n'.join(wrap_paragraphs(s, 78))
216
216
217 return '# ' + s.replace('\n', '\n# ')
217 return '# ' + s.replace('\n', '\n# ')
218
218
219 # section header
219 # section header
220 breaker = '#' + '-'*78
220 breaker = '#' + '-'*78
221 s = "# %s configuration"%cls.__name__
221 s = "# %s configuration" % cls.__name__
222 lines = [breaker, s, breaker, '']
222 lines = [breaker, s, breaker, '']
223 # get the description trait
223 # get the description trait
224 desc = cls.class_traits().get('description')
224 desc = cls.class_traits().get('description')
225 if desc:
225 if desc:
226 desc = desc.default_value
226 desc = desc.default_value
227 else:
227 else:
228 # no description trait, use __doc__
228 # no description trait, use __doc__
229 desc = getattr(cls, '__doc__', '')
229 desc = getattr(cls, '__doc__', '')
230 if desc:
230 if desc:
231 lines.append(c(desc))
231 lines.append(c(desc))
232 lines.append('')
232 lines.append('')
233
233
234 parents = []
234 parents = []
235 for parent in cls.mro():
235 for parent in cls.mro():
236 # only include parents that are not base classes
236 # only include parents that are not base classes
237 # and are not the class itself
237 # and are not the class itself
238 # and have some configurable traits to inherit
238 # and have some configurable traits to inherit
239 if parent is not cls and issubclass(parent, Configurable) and \
239 if parent is not cls and issubclass(parent, Configurable) and \
240 parent.class_traits(config=True):
240 parent.class_traits(config=True):
241 parents.append(parent)
241 parents.append(parent)
242
242
243 if parents:
243 if parents:
244 pstr = ', '.join([ p.__name__ for p in parents ])
244 pstr = ', '.join([ p.__name__ for p in parents ])
245 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
245 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
246 lines.append('')
246 lines.append('')
247
247
248 for name,trait in cls.class_traits(config=True).iteritems():
248 for name, trait in cls.class_traits(config=True).iteritems():
249 help = trait.get_metadata('help') or ''
249 help = trait.get_metadata('help') or ''
250 lines.append(c(help))
250 lines.append(c(help))
251 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
251 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
252 lines.append('')
252 lines.append('')
253 return '\n'.join(lines)
253 return '\n'.join(lines)
254
254
255
255
256
256
257 class SingletonConfigurable(Configurable):
257 class SingletonConfigurable(Configurable):
258 """A configurable that only allows one instance.
258 """A configurable that only allows one instance.
259
259
260 This class is for classes that should only have one instance of itself
260 This class is for classes that should only have one instance of itself
261 or *any* subclass. To create and retrieve such a class use the
261 or *any* subclass. To create and retrieve such a class use the
262 :meth:`SingletonConfigurable.instance` method.
262 :meth:`SingletonConfigurable.instance` method.
263 """
263 """
264
264
265 _instance = None
265 _instance = None
266
266
267 @classmethod
267 @classmethod
268 def _walk_mro(cls):
268 def _walk_mro(cls):
269 """Walk the cls.mro() for parent classes that are also singletons
269 """Walk the cls.mro() for parent classes that are also singletons
270
270
271 For use in instance()
271 For use in instance()
272 """
272 """
273
273
274 for subclass in cls.mro():
274 for subclass in cls.mro():
275 if issubclass(cls, subclass) and \
275 if issubclass(cls, subclass) and \
276 issubclass(subclass, SingletonConfigurable) and \
276 issubclass(subclass, SingletonConfigurable) and \
277 subclass != SingletonConfigurable:
277 subclass != SingletonConfigurable:
278 yield subclass
278 yield subclass
279
279
280 @classmethod
280 @classmethod
281 def clear_instance(cls):
281 def clear_instance(cls):
282 """unset _instance for this class and singleton parents.
282 """unset _instance for this class and singleton parents.
283 """
283 """
284 if not cls.initialized():
284 if not cls.initialized():
285 return
285 return
286 for subclass in cls._walk_mro():
286 for subclass in cls._walk_mro():
287 if isinstance(subclass._instance, cls):
287 if isinstance(subclass._instance, cls):
288 # only clear instances that are instances
288 # only clear instances that are instances
289 # of the calling class
289 # of the calling class
290 subclass._instance = None
290 subclass._instance = None
291
291
292 @classmethod
292 @classmethod
293 def instance(cls, *args, **kwargs):
293 def instance(cls, *args, **kwargs):
294 """Returns a global instance of this class.
294 """Returns a global instance of this class.
295
295
296 This method create a new instance if none have previously been created
296 This method create a new instance if none have previously been created
297 and returns a previously created instance is one already exists.
297 and returns a previously created instance is one already exists.
298
298
299 The arguments and keyword arguments passed to this method are passed
299 The arguments and keyword arguments passed to this method are passed
300 on to the :meth:`__init__` method of the class upon instantiation.
300 on to the :meth:`__init__` method of the class upon instantiation.
301
301
302 Examples
302 Examples
303 --------
303 --------
304
304
305 Create a singleton class using instance, and retrieve it::
305 Create a singleton class using instance, and retrieve it::
306
306
307 >>> from IPython.config.configurable import SingletonConfigurable
307 >>> from IPython.config.configurable import SingletonConfigurable
308 >>> class Foo(SingletonConfigurable): pass
308 >>> class Foo(SingletonConfigurable): pass
309 >>> foo = Foo.instance()
309 >>> foo = Foo.instance()
310 >>> foo == Foo.instance()
310 >>> foo == Foo.instance()
311 True
311 True
312
312
313 Create a subclass that is retrived using the base class instance::
313 Create a subclass that is retrived using the base class instance::
314
314
315 >>> class Bar(SingletonConfigurable): pass
315 >>> class Bar(SingletonConfigurable): pass
316 >>> class Bam(Bar): pass
316 >>> class Bam(Bar): pass
317 >>> bam = Bam.instance()
317 >>> bam = Bam.instance()
318 >>> bam == Bar.instance()
318 >>> bam == Bar.instance()
319 True
319 True
320 """
320 """
321 # Create and save the instance
321 # Create and save the instance
322 if cls._instance is None:
322 if cls._instance is None:
323 inst = cls(*args, **kwargs)
323 inst = cls(*args, **kwargs)
324 # Now make sure that the instance will also be returned by
324 # Now make sure that the instance will also be returned by
325 # parent classes' _instance attribute.
325 # parent classes' _instance attribute.
326 for subclass in cls._walk_mro():
326 for subclass in cls._walk_mro():
327 subclass._instance = inst
327 subclass._instance = inst
328
328
329 if isinstance(cls._instance, cls):
329 if isinstance(cls._instance, cls):
330 return cls._instance
330 return cls._instance
331 else:
331 else:
332 raise MultipleInstanceError(
332 raise MultipleInstanceError(
333 'Multiple incompatible subclass instances of '
333 'Multiple incompatible subclass instances of '
334 '%s are being created.' % cls.__name__
334 '%s are being created.' % cls.__name__
335 )
335 )
336
336
337 @classmethod
337 @classmethod
338 def initialized(cls):
338 def initialized(cls):
339 """Has an instance been created?"""
339 """Has an instance been created?"""
340 return hasattr(cls, "_instance") and cls._instance is not None
340 return hasattr(cls, "_instance") and cls._instance is not None
341
341
342
342
343 class LoggingConfigurable(Configurable):
343 class LoggingConfigurable(Configurable):
344 """A parent class for Configurables that log.
344 """A parent class for Configurables that log.
345
345
346 Subclasses have a log trait, and the default behavior
346 Subclasses have a log trait, and the default behavior
347 is to get the logger from the currently running Application
347 is to get the logger from the currently running Application
348 via Application.instance().log.
348 via Application.instance().log.
349 """
349 """
350
350
351 log = Instance('logging.Logger')
351 log = Instance('logging.Logger')
352 def _log_default(self):
352 def _log_default(self):
353 from IPython.config.application import Application
353 from IPython.config.application import Application
354 return Application.instance().log
354 return Application.instance().log
355
355
356
356
@@ -1,336 +1,334 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 * Min RK
15 * Min RK
16
16
17 """
17 """
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Copyright (C) 2008-2011 The IPython Development Team
20 # Copyright (C) 2008-2011 The IPython Development Team
21 #
21 #
22 # Distributed under the terms of the BSD License. The full license is in
22 # Distributed under the terms of the BSD License. The full license is in
23 # the file COPYING, distributed as part of this software.
23 # the file COPYING, distributed as part of this software.
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27 # Imports
27 # Imports
28 #-----------------------------------------------------------------------------
28 #-----------------------------------------------------------------------------
29
29
30 import atexit
30 import atexit
31 import glob
31 import glob
32 import logging
32 import logging
33 import os
33 import os
34 import shutil
34 import shutil
35 import sys
35 import sys
36
36
37 from IPython.config.application import Application, catch_config_error
37 from IPython.config.application import Application, catch_config_error
38 from IPython.config.configurable import Configurable
38 from IPython.config.loader import ConfigFileNotFound
39 from IPython.config.loader import Config, ConfigFileNotFound
40 from IPython.core import release, crashhandler
39 from IPython.core import release, crashhandler
41 from IPython.core.profiledir import ProfileDir, ProfileDirError
40 from IPython.core.profiledir import ProfileDir, ProfileDirError
42 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
41 from IPython.utils.path import get_ipython_dir, get_ipython_package_dir
43 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict
42 from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict
44 from IPython.utils import py3compat
45
43
46 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
47 # Classes and functions
45 # Classes and functions
48 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
49
47
50
48
51 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
52 # Base Application Class
50 # Base Application Class
53 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
54
52
55 # aliases and flags
53 # aliases and flags
56
54
57 base_aliases = {
55 base_aliases = {
58 'profile' : 'BaseIPythonApplication.profile',
56 'profile' : 'BaseIPythonApplication.profile',
59 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
57 'ipython-dir' : 'BaseIPythonApplication.ipython_dir',
60 'log-level' : 'Application.log_level',
58 'log-level' : 'Application.log_level',
61 }
59 }
62
60
63 base_flags = dict(
61 base_flags = dict(
64 debug = ({'Application' : {'log_level' : logging.DEBUG}},
62 debug = ({'Application' : {'log_level' : logging.DEBUG}},
65 "set log level to logging.DEBUG (maximize logging output)"),
63 "set log level to logging.DEBUG (maximize logging output)"),
66 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
64 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
67 "set log level to logging.CRITICAL (minimize logging output)"),
65 "set log level to logging.CRITICAL (minimize logging output)"),
68 init = ({'BaseIPythonApplication' : {
66 init = ({'BaseIPythonApplication' : {
69 'copy_config_files' : True,
67 'copy_config_files' : True,
70 'auto_create' : True}
68 'auto_create' : True}
71 }, """Initialize profile with default config files. This is equivalent
69 }, """Initialize profile with default config files. This is equivalent
72 to running `ipython profile create <profile>` prior to startup.
70 to running `ipython profile create <profile>` prior to startup.
73 """)
71 """)
74 )
72 )
75
73
76
74
77 class BaseIPythonApplication(Application):
75 class BaseIPythonApplication(Application):
78
76
79 name = Unicode(u'ipython')
77 name = Unicode(u'ipython')
80 description = Unicode(u'IPython: an enhanced interactive Python shell.')
78 description = Unicode(u'IPython: an enhanced interactive Python shell.')
81 version = Unicode(release.version)
79 version = Unicode(release.version)
82
80
83 aliases = Dict(base_aliases)
81 aliases = Dict(base_aliases)
84 flags = Dict(base_flags)
82 flags = Dict(base_flags)
85 classes = List([ProfileDir])
83 classes = List([ProfileDir])
86
84
87 # Track whether the config_file has changed,
85 # Track whether the config_file has changed,
88 # because some logic happens only if we aren't using the default.
86 # because some logic happens only if we aren't using the default.
89 config_file_specified = Bool(False)
87 config_file_specified = Bool(False)
90
88
91 config_file_name = Unicode(u'ipython_config.py')
89 config_file_name = Unicode(u'ipython_config.py')
92 def _config_file_name_default(self):
90 def _config_file_name_default(self):
93 return self.name.replace('-','_') + u'_config.py'
91 return self.name.replace('-','_') + u'_config.py'
94 def _config_file_name_changed(self, name, old, new):
92 def _config_file_name_changed(self, name, old, new):
95 if new != old:
93 if new != old:
96 self.config_file_specified = True
94 self.config_file_specified = True
97
95
98 # The directory that contains IPython's builtin profiles.
96 # The directory that contains IPython's builtin profiles.
99 builtin_profile_dir = Unicode(
97 builtin_profile_dir = Unicode(
100 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
98 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
101 )
99 )
102
100
103 config_file_paths = List(Unicode)
101 config_file_paths = List(Unicode)
104 def _config_file_paths_default(self):
102 def _config_file_paths_default(self):
105 return [os.getcwdu()]
103 return [os.getcwdu()]
106
104
107 profile = Unicode(u'default', config=True,
105 profile = Unicode(u'default', config=True,
108 help="""The IPython profile to use."""
106 help="""The IPython profile to use."""
109 )
107 )
110
108
111 def _profile_changed(self, name, old, new):
109 def _profile_changed(self, name, old, new):
112 self.builtin_profile_dir = os.path.join(
110 self.builtin_profile_dir = os.path.join(
113 get_ipython_package_dir(), u'config', u'profile', new
111 get_ipython_package_dir(), u'config', u'profile', new
114 )
112 )
115
113
116 ipython_dir = Unicode(get_ipython_dir(), config=True,
114 ipython_dir = Unicode(get_ipython_dir(), config=True,
117 help="""
115 help="""
118 The name of the IPython directory. This directory is used for logging
116 The name of the IPython directory. This directory is used for logging
119 configuration (through profiles), history storage, etc. The default
117 configuration (through profiles), history storage, etc. The default
120 is usually $HOME/.ipython. This options can also be specified through
118 is usually $HOME/.ipython. This options can also be specified through
121 the environment variable IPYTHONDIR.
119 the environment variable IPYTHONDIR.
122 """
120 """
123 )
121 )
124
122
125 overwrite = Bool(False, config=True,
123 overwrite = Bool(False, config=True,
126 help="""Whether to overwrite existing config files when copying""")
124 help="""Whether to overwrite existing config files when copying""")
127 auto_create = Bool(False, config=True,
125 auto_create = Bool(False, config=True,
128 help="""Whether to create profile dir if it doesn't exist""")
126 help="""Whether to create profile dir if it doesn't exist""")
129
127
130 config_files = List(Unicode)
128 config_files = List(Unicode)
131 def _config_files_default(self):
129 def _config_files_default(self):
132 return [u'ipython_config.py']
130 return [u'ipython_config.py']
133
131
134 copy_config_files = Bool(False, config=True,
132 copy_config_files = Bool(False, config=True,
135 help="""Whether to install the default config files into the profile dir.
133 help="""Whether to install the default config files into the profile dir.
136 If a new profile is being created, and IPython contains config files for that
134 If a new profile is being created, and IPython contains config files for that
137 profile, then they will be staged into the new directory. Otherwise,
135 profile, then they will be staged into the new directory. Otherwise,
138 default config files will be automatically generated.
136 default config files will be automatically generated.
139 """)
137 """)
140
138
141 verbose_crash = Bool(False, config=True,
139 verbose_crash = Bool(False, config=True,
142 help="""Create a massive crash report when IPython encounters what may be an
140 help="""Create a massive crash report when IPython encounters what may be an
143 internal error. The default is to append a short message to the
141 internal error. The default is to append a short message to the
144 usual traceback""")
142 usual traceback""")
145
143
146 # The class to use as the crash handler.
144 # The class to use as the crash handler.
147 crash_handler_class = Type(crashhandler.CrashHandler)
145 crash_handler_class = Type(crashhandler.CrashHandler)
148
146
149 def __init__(self, **kwargs):
147 def __init__(self, **kwargs):
150 super(BaseIPythonApplication, self).__init__(**kwargs)
148 super(BaseIPythonApplication, self).__init__(**kwargs)
151 # ensure even default IPYTHONDIR exists
149 # ensure even default IPYTHONDIR exists
152 if not os.path.exists(self.ipython_dir):
150 if not os.path.exists(self.ipython_dir):
153 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
151 self._ipython_dir_changed('ipython_dir', self.ipython_dir, self.ipython_dir)
154
152
155 #-------------------------------------------------------------------------
153 #-------------------------------------------------------------------------
156 # Various stages of Application creation
154 # Various stages of Application creation
157 #-------------------------------------------------------------------------
155 #-------------------------------------------------------------------------
158
156
159 def init_crash_handler(self):
157 def init_crash_handler(self):
160 """Create a crash handler, typically setting sys.excepthook to it."""
158 """Create a crash handler, typically setting sys.excepthook to it."""
161 self.crash_handler = self.crash_handler_class(self)
159 self.crash_handler = self.crash_handler_class(self)
162 sys.excepthook = self.excepthook
160 sys.excepthook = self.excepthook
163 def unset_crashhandler():
161 def unset_crashhandler():
164 sys.excepthook = sys.__excepthook__
162 sys.excepthook = sys.__excepthook__
165 atexit.register(unset_crashhandler)
163 atexit.register(unset_crashhandler)
166
164
167 def excepthook(self, etype, evalue, tb):
165 def excepthook(self, etype, evalue, tb):
168 """this is sys.excepthook after init_crashhandler
166 """this is sys.excepthook after init_crashhandler
169
167
170 set self.verbose_crash=True to use our full crashhandler, instead of
168 set self.verbose_crash=True to use our full crashhandler, instead of
171 a regular traceback with a short message (crash_handler_lite)
169 a regular traceback with a short message (crash_handler_lite)
172 """
170 """
173
171
174 if self.verbose_crash:
172 if self.verbose_crash:
175 return self.crash_handler(etype, evalue, tb)
173 return self.crash_handler(etype, evalue, tb)
176 else:
174 else:
177 return crashhandler.crash_handler_lite(etype, evalue, tb)
175 return crashhandler.crash_handler_lite(etype, evalue, tb)
178
176
179 def _ipython_dir_changed(self, name, old, new):
177 def _ipython_dir_changed(self, name, old, new):
180 if old in sys.path:
178 if old in sys.path:
181 sys.path.remove(old)
179 sys.path.remove(old)
182 sys.path.append(os.path.abspath(new))
180 sys.path.append(os.path.abspath(new))
183 if not os.path.isdir(new):
181 if not os.path.isdir(new):
184 os.makedirs(new, mode=0o777)
182 os.makedirs(new, mode=0o777)
185 readme = os.path.join(new, 'README')
183 readme = os.path.join(new, 'README')
186 if not os.path.exists(readme):
184 if not os.path.exists(readme):
187 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
185 path = os.path.join(get_ipython_package_dir(), u'config', u'profile')
188 shutil.copy(os.path.join(path, 'README'), readme)
186 shutil.copy(os.path.join(path, 'README'), readme)
189 self.log.debug("IPYTHONDIR set to: %s" % new)
187 self.log.debug("IPYTHONDIR set to: %s" % new)
190
188
191 def load_config_file(self, suppress_errors=True):
189 def load_config_file(self, suppress_errors=True):
192 """Load the config file.
190 """Load the config file.
193
191
194 By default, errors in loading config are handled, and a warning
192 By default, errors in loading config are handled, and a warning
195 printed on screen. For testing, the suppress_errors option is set
193 printed on screen. For testing, the suppress_errors option is set
196 to False, so errors will make tests fail.
194 to False, so errors will make tests fail.
197 """
195 """
198 self.log.debug("Searching path %s for config files", self.config_file_paths)
196 self.log.debug("Searching path %s for config files", self.config_file_paths)
199 base_config = 'ipython_config.py'
197 base_config = 'ipython_config.py'
200 self.log.debug("Attempting to load config file: %s" %
198 self.log.debug("Attempting to load config file: %s" %
201 base_config)
199 base_config)
202 try:
200 try:
203 Application.load_config_file(
201 Application.load_config_file(
204 self,
202 self,
205 base_config,
203 base_config,
206 path=self.config_file_paths
204 path=self.config_file_paths
207 )
205 )
208 except ConfigFileNotFound:
206 except ConfigFileNotFound:
209 # ignore errors loading parent
207 # ignore errors loading parent
210 self.log.debug("Config file %s not found", base_config)
208 self.log.debug("Config file %s not found", base_config)
211 pass
209 pass
212 if self.config_file_name == base_config:
210 if self.config_file_name == base_config:
213 # don't load secondary config
211 # don't load secondary config
214 return
212 return
215 self.log.debug("Attempting to load config file: %s" %
213 self.log.debug("Attempting to load config file: %s" %
216 self.config_file_name)
214 self.config_file_name)
217 try:
215 try:
218 Application.load_config_file(
216 Application.load_config_file(
219 self,
217 self,
220 self.config_file_name,
218 self.config_file_name,
221 path=self.config_file_paths
219 path=self.config_file_paths
222 )
220 )
223 except ConfigFileNotFound:
221 except ConfigFileNotFound:
224 # Only warn if the default config file was NOT being used.
222 # Only warn if the default config file was NOT being used.
225 if self.config_file_specified:
223 if self.config_file_specified:
226 msg = self.log.warn
224 msg = self.log.warn
227 else:
225 else:
228 msg = self.log.debug
226 msg = self.log.debug
229 msg("Config file not found, skipping: %s", self.config_file_name)
227 msg("Config file not found, skipping: %s", self.config_file_name)
230 except:
228 except:
231 # For testing purposes.
229 # For testing purposes.
232 if not suppress_errors:
230 if not suppress_errors:
233 raise
231 raise
234 self.log.warn("Error loading config file: %s" %
232 self.log.warn("Error loading config file: %s" %
235 self.config_file_name, exc_info=True)
233 self.config_file_name, exc_info=True)
236
234
237 def init_profile_dir(self):
235 def init_profile_dir(self):
238 """initialize the profile dir"""
236 """initialize the profile dir"""
239 try:
237 try:
240 # location explicitly specified:
238 # location explicitly specified:
241 location = self.config.ProfileDir.location
239 location = self.config.ProfileDir.location
242 except AttributeError:
240 except AttributeError:
243 # location not specified, find by profile name
241 # location not specified, find by profile name
244 try:
242 try:
245 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
243 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
246 except ProfileDirError:
244 except ProfileDirError:
247 # not found, maybe create it (always create default profile)
245 # not found, maybe create it (always create default profile)
248 if self.auto_create or self.profile=='default':
246 if self.auto_create or self.profile == 'default':
249 try:
247 try:
250 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
248 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
251 except ProfileDirError:
249 except ProfileDirError:
252 self.log.fatal("Could not create profile: %r"%self.profile)
250 self.log.fatal("Could not create profile: %r"%self.profile)
253 self.exit(1)
251 self.exit(1)
254 else:
252 else:
255 self.log.info("Created profile dir: %r"%p.location)
253 self.log.info("Created profile dir: %r"%p.location)
256 else:
254 else:
257 self.log.fatal("Profile %r not found."%self.profile)
255 self.log.fatal("Profile %r not found."%self.profile)
258 self.exit(1)
256 self.exit(1)
259 else:
257 else:
260 self.log.info("Using existing profile dir: %r"%p.location)
258 self.log.info("Using existing profile dir: %r"%p.location)
261 else:
259 else:
262 # location is fully specified
260 # location is fully specified
263 try:
261 try:
264 p = ProfileDir.find_profile_dir(location, self.config)
262 p = ProfileDir.find_profile_dir(location, self.config)
265 except ProfileDirError:
263 except ProfileDirError:
266 # not found, maybe create it
264 # not found, maybe create it
267 if self.auto_create:
265 if self.auto_create:
268 try:
266 try:
269 p = ProfileDir.create_profile_dir(location, self.config)
267 p = ProfileDir.create_profile_dir(location, self.config)
270 except ProfileDirError:
268 except ProfileDirError:
271 self.log.fatal("Could not create profile directory: %r"%location)
269 self.log.fatal("Could not create profile directory: %r"%location)
272 self.exit(1)
270 self.exit(1)
273 else:
271 else:
274 self.log.info("Creating new profile dir: %r"%location)
272 self.log.info("Creating new profile dir: %r"%location)
275 else:
273 else:
276 self.log.fatal("Profile directory %r not found."%location)
274 self.log.fatal("Profile directory %r not found."%location)
277 self.exit(1)
275 self.exit(1)
278 else:
276 else:
279 self.log.info("Using existing profile dir: %r"%location)
277 self.log.info("Using existing profile dir: %r"%location)
280
278
281 self.profile_dir = p
279 self.profile_dir = p
282 self.config_file_paths.append(p.location)
280 self.config_file_paths.append(p.location)
283
281
284 def init_config_files(self):
282 def init_config_files(self):
285 """[optionally] copy default config files into profile dir."""
283 """[optionally] copy default config files into profile dir."""
286 # copy config files
284 # copy config files
287 path = self.builtin_profile_dir
285 path = self.builtin_profile_dir
288 if self.copy_config_files:
286 if self.copy_config_files:
289 src = self.profile
287 src = self.profile
290
288
291 cfg = self.config_file_name
289 cfg = self.config_file_name
292 if path and os.path.exists(os.path.join(path, cfg)):
290 if path and os.path.exists(os.path.join(path, cfg)):
293 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
291 self.log.warn("Staging %r from %s into %r [overwrite=%s]"%(
294 cfg, src, self.profile_dir.location, self.overwrite)
292 cfg, src, self.profile_dir.location, self.overwrite)
295 )
293 )
296 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
294 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
297 else:
295 else:
298 self.stage_default_config_file()
296 self.stage_default_config_file()
299 else:
297 else:
300 # Still stage *bundled* config files, but not generated ones
298 # Still stage *bundled* config files, but not generated ones
301 # This is necessary for `ipython profile=sympy` to load the profile
299 # This is necessary for `ipython profile=sympy` to load the profile
302 # on the first go
300 # on the first go
303 files = glob.glob(os.path.join(path, '*.py'))
301 files = glob.glob(os.path.join(path, '*.py'))
304 for fullpath in files:
302 for fullpath in files:
305 cfg = os.path.basename(fullpath)
303 cfg = os.path.basename(fullpath)
306 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
304 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
307 # file was copied
305 # file was copied
308 self.log.warn("Staging bundled %s from %s into %r"%(
306 self.log.warn("Staging bundled %s from %s into %r"%(
309 cfg, self.profile, self.profile_dir.location)
307 cfg, self.profile, self.profile_dir.location)
310 )
308 )
311
309
312
310
313 def stage_default_config_file(self):
311 def stage_default_config_file(self):
314 """auto generate default config file, and stage it into the profile."""
312 """auto generate default config file, and stage it into the profile."""
315 s = self.generate_config_file()
313 s = self.generate_config_file()
316 fname = os.path.join(self.profile_dir.location, self.config_file_name)
314 fname = os.path.join(self.profile_dir.location, self.config_file_name)
317 if self.overwrite or not os.path.exists(fname):
315 if self.overwrite or not os.path.exists(fname):
318 self.log.warn("Generating default config file: %r"%(fname))
316 self.log.warn("Generating default config file: %r"%(fname))
319 with open(fname, 'w') as f:
317 with open(fname, 'w') as f:
320 f.write(s)
318 f.write(s)
321
319
322 @catch_config_error
320 @catch_config_error
323 def initialize(self, argv=None):
321 def initialize(self, argv=None):
324 # don't hook up crash handler before parsing command-line
322 # don't hook up crash handler before parsing command-line
325 self.parse_command_line(argv)
323 self.parse_command_line(argv)
326 self.init_crash_handler()
324 self.init_crash_handler()
327 if self.subapp is not None:
325 if self.subapp is not None:
328 # stop here if subapp is taking over
326 # stop here if subapp is taking over
329 return
327 return
330 cl_config = self.config
328 cl_config = self.config
331 self.init_profile_dir()
329 self.init_profile_dir()
332 self.init_config_files()
330 self.init_config_files()
333 self.load_config_file()
331 self.load_config_file()
334 # enforce cl-opts override configfile opts:
332 # enforce cl-opts override configfile opts:
335 self.update_config(cl_config)
333 self.update_config(cl_config)
336
334
General Comments 0
You need to be logged in to leave comments. Login now