##// END OF EJS Templates
move useful update_config method to Configurable from Application...
MinRK -
Show More
@@ -1,328 +1,338 b''
1 1 # encoding: utf-8
2 2 """
3 3 A base class for objects that are configurable.
4 4
5 5 Authors:
6 6
7 7 * Brian Granger
8 8 * Fernando Perez
9 9 * Min RK
10 10 """
11 11
12 12 #-----------------------------------------------------------------------------
13 13 # Copyright (C) 2008-2011 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-----------------------------------------------------------------------------
18 18
19 19 #-----------------------------------------------------------------------------
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23 23 import datetime
24 24 from copy import deepcopy
25 25
26 26 from loader import Config
27 27 from IPython.utils.traitlets import HasTraits, Instance
28 28 from IPython.utils.text import indent, wrap_paragraphs
29 29
30 30
31 31 #-----------------------------------------------------------------------------
32 32 # Helper classes for Configurables
33 33 #-----------------------------------------------------------------------------
34 34
35 35
36 36 class ConfigurableError(Exception):
37 37 pass
38 38
39 39
40 40 class MultipleInstanceError(ConfigurableError):
41 41 pass
42 42
43 43 #-----------------------------------------------------------------------------
44 44 # Configurable implementation
45 45 #-----------------------------------------------------------------------------
46 46
47 47 class Configurable(HasTraits):
48 48
49 49 config = Instance(Config,(),{})
50 50 created = None
51 51
52 52 def __init__(self, **kwargs):
53 53 """Create a configurable given a config config.
54 54
55 55 Parameters
56 56 ----------
57 57 config : Config
58 58 If this is empty, default values are used. If config is a
59 59 :class:`Config` instance, it will be used to configure the
60 60 instance.
61 61
62 62 Notes
63 63 -----
64 64 Subclasses of Configurable must call the :meth:`__init__` method of
65 65 :class:`Configurable` *before* doing anything else and using
66 66 :func:`super`::
67 67
68 68 class MyConfigurable(Configurable):
69 69 def __init__(self, config=None):
70 70 super(MyConfigurable, self).__init__(config)
71 71 # Then any other code you need to finish initialization.
72 72
73 73 This ensures that instances will be configured properly.
74 74 """
75 75 config = kwargs.pop('config', None)
76 76 if config is not None:
77 77 # We used to deepcopy, but for now we are trying to just save
78 78 # by reference. This *could* have side effects as all components
79 79 # will share config. In fact, I did find such a side effect in
80 80 # _config_changed below. If a config attribute value was a mutable type
81 81 # all instances of a component were getting the same copy, effectively
82 82 # making that a class attribute.
83 83 # self.config = deepcopy(config)
84 84 self.config = config
85 85 # This should go second so individual keyword arguments override
86 86 # the values in config.
87 87 super(Configurable, self).__init__(**kwargs)
88 88 self.created = datetime.datetime.now()
89 89
90 90 #-------------------------------------------------------------------------
91 91 # Static trait notifiations
92 92 #-------------------------------------------------------------------------
93 93
94 94 def _config_changed(self, name, old, new):
95 95 """Update all the class traits having ``config=True`` as metadata.
96 96
97 97 For any class trait with a ``config`` metadata attribute that is
98 98 ``True``, we update the trait with the value of the corresponding
99 99 config entry.
100 100 """
101 101 # Get all traits with a config metadata entry that is True
102 102 traits = self.traits(config=True)
103 103
104 104 # We auto-load config section for this class as well as any parent
105 105 # classes that are Configurable subclasses. This starts with Configurable
106 106 # and works down the mro loading the config for each section.
107 107 section_names = [cls.__name__ for cls in \
108 108 reversed(self.__class__.__mro__) if
109 109 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
110 110
111 111 for sname in section_names:
112 112 # Don't do a blind getattr as that would cause the config to
113 113 # dynamically create the section with name self.__class__.__name__.
114 114 if new._has_section(sname):
115 115 my_config = new[sname]
116 116 for k, v in traits.iteritems():
117 117 # Don't allow traitlets with config=True to start with
118 118 # uppercase. Otherwise, they are confused with Config
119 119 # subsections. But, developers shouldn't have uppercase
120 120 # attributes anyways! (PEP 6)
121 121 if k[0].upper()==k[0] and not k.startswith('_'):
122 122 raise ConfigurableError('Configurable traitlets with '
123 123 'config=True must start with a lowercase so they are '
124 124 'not confused with Config subsections: %s.%s' % \
125 125 (self.__class__.__name__, k))
126 126 try:
127 127 # Here we grab the value from the config
128 128 # If k has the naming convention of a config
129 129 # section, it will be auto created.
130 130 config_value = my_config[k]
131 131 except KeyError:
132 132 pass
133 133 else:
134 134 # print "Setting %s.%s from %s.%s=%r" % \
135 135 # (self.__class__.__name__,k,sname,k,config_value)
136 136 # We have to do a deepcopy here if we don't deepcopy the entire
137 137 # config object. If we don't, a mutable config_value will be
138 138 # shared by all instances, effectively making it a class attribute.
139 139 setattr(self, k, deepcopy(config_value))
140 140
141 def update_config(self, config):
142 """Fire the traits events when the config is updated."""
143 # Save a copy of the current config.
144 newconfig = deepcopy(self.config)
145 # Merge the new config into the current one.
146 newconfig._merge(config)
147 # Save the combined config as self.config, which triggers the traits
148 # events.
149 self.config = newconfig
150
141 151 @classmethod
142 152 def class_get_help(cls):
143 153 """Get the help string for this class in ReST format."""
144 154 cls_traits = cls.class_traits(config=True)
145 155 final_help = []
146 156 final_help.append(u'%s options' % cls.__name__)
147 157 final_help.append(len(final_help[0])*u'-')
148 158 for k,v in cls.class_traits(config=True).iteritems():
149 159 help = cls.class_get_trait_help(v)
150 160 final_help.append(help)
151 161 return '\n'.join(final_help)
152 162
153 163 @classmethod
154 164 def class_get_trait_help(cls, trait):
155 165 """Get the help string for a single trait."""
156 166 lines = []
157 167 header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
158 168 lines.append(header)
159 169 try:
160 170 dvr = repr(trait.get_default_value())
161 171 except Exception:
162 172 dvr = None # ignore defaults we can't construct
163 173 if dvr is not None:
164 174 if len(dvr) > 64:
165 175 dvr = dvr[:61]+'...'
166 176 lines.append(indent('Default: %s'%dvr, 4))
167 177 if 'Enum' in trait.__class__.__name__:
168 178 # include Enum choices
169 179 lines.append(indent('Choices: %r'%(trait.values,)))
170 180
171 181 help = trait.get_metadata('help')
172 182 if help is not None:
173 183 help = '\n'.join(wrap_paragraphs(help, 76))
174 184 lines.append(indent(help, 4))
175 185 return '\n'.join(lines)
176 186
177 187 @classmethod
178 188 def class_print_help(cls):
179 189 """Get the help string for a single trait and print it."""
180 190 print cls.class_get_help()
181 191
182 192 @classmethod
183 193 def class_config_section(cls):
184 194 """Get the config class config section"""
185 195 def c(s):
186 196 """return a commented, wrapped block."""
187 197 s = '\n\n'.join(wrap_paragraphs(s, 78))
188 198
189 199 return '# ' + s.replace('\n', '\n# ')
190 200
191 201 # section header
192 202 breaker = '#' + '-'*78
193 203 s = "# %s configuration"%cls.__name__
194 204 lines = [breaker, s, breaker, '']
195 205 # get the description trait
196 206 desc = cls.class_traits().get('description')
197 207 if desc:
198 208 desc = desc.default_value
199 209 else:
200 210 # no description trait, use __doc__
201 211 desc = getattr(cls, '__doc__', '')
202 212 if desc:
203 213 lines.append(c(desc))
204 214 lines.append('')
205 215
206 216 parents = []
207 217 for parent in cls.mro():
208 218 # only include parents that are not base classes
209 219 # and are not the class itself
210 220 # and have some configurable traits to inherit
211 221 if parent is not cls and issubclass(parent, Configurable) and \
212 222 parent.class_traits(config=True):
213 223 parents.append(parent)
214 224
215 225 if parents:
216 226 pstr = ', '.join([ p.__name__ for p in parents ])
217 227 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
218 228 lines.append('')
219 229
220 230 for name,trait in cls.class_traits(config=True).iteritems():
221 231 help = trait.get_metadata('help') or ''
222 232 lines.append(c(help))
223 233 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
224 234 lines.append('')
225 235 return '\n'.join(lines)
226 236
227 237
228 238
229 239 class SingletonConfigurable(Configurable):
230 240 """A configurable that only allows one instance.
231 241
232 242 This class is for classes that should only have one instance of itself
233 243 or *any* subclass. To create and retrieve such a class use the
234 244 :meth:`SingletonConfigurable.instance` method.
235 245 """
236 246
237 247 _instance = None
238 248
239 249 @classmethod
240 250 def _walk_mro(cls):
241 251 """Walk the cls.mro() for parent classes that are also singletons
242 252
243 253 For use in instance()
244 254 """
245 255
246 256 for subclass in cls.mro():
247 257 if issubclass(cls, subclass) and \
248 258 issubclass(subclass, SingletonConfigurable) and \
249 259 subclass != SingletonConfigurable:
250 260 yield subclass
251 261
252 262 @classmethod
253 263 def clear_instance(cls):
254 264 """unset _instance for this class and singleton parents.
255 265 """
256 266 if not cls.initialized():
257 267 return
258 268 for subclass in cls._walk_mro():
259 269 if isinstance(subclass._instance, cls):
260 270 # only clear instances that are instances
261 271 # of the calling class
262 272 subclass._instance = None
263 273
264 274 @classmethod
265 275 def instance(cls, *args, **kwargs):
266 276 """Returns a global instance of this class.
267 277
268 278 This method create a new instance if none have previously been created
269 279 and returns a previously created instance is one already exists.
270 280
271 281 The arguments and keyword arguments passed to this method are passed
272 282 on to the :meth:`__init__` method of the class upon instantiation.
273 283
274 284 Examples
275 285 --------
276 286
277 287 Create a singleton class using instance, and retrieve it::
278 288
279 289 >>> from IPython.config.configurable import SingletonConfigurable
280 290 >>> class Foo(SingletonConfigurable): pass
281 291 >>> foo = Foo.instance()
282 292 >>> foo == Foo.instance()
283 293 True
284 294
285 295 Create a subclass that is retrived using the base class instance::
286 296
287 297 >>> class Bar(SingletonConfigurable): pass
288 298 >>> class Bam(Bar): pass
289 299 >>> bam = Bam.instance()
290 300 >>> bam == Bar.instance()
291 301 True
292 302 """
293 303 # Create and save the instance
294 304 if cls._instance is None:
295 305 inst = cls(*args, **kwargs)
296 306 # Now make sure that the instance will also be returned by
297 307 # parent classes' _instance attribute.
298 308 for subclass in cls._walk_mro():
299 309 subclass._instance = inst
300 310
301 311 if isinstance(cls._instance, cls):
302 312 return cls._instance
303 313 else:
304 314 raise MultipleInstanceError(
305 315 'Multiple incompatible subclass instances of '
306 316 '%s are being created.' % cls.__name__
307 317 )
308 318
309 319 @classmethod
310 320 def initialized(cls):
311 321 """Has an instance been created?"""
312 322 return hasattr(cls, "_instance") and cls._instance is not None
313 323
314 324
315 325 class LoggingConfigurable(Configurable):
316 326 """A parent class for Configurables that log.
317 327
318 328 Subclasses have a log trait, and the default behavior
319 329 is to get the logger from the currently running Application
320 330 via Application.instance().log.
321 331 """
322 332
323 333 log = Instance('logging.Logger')
324 334 def _log_default(self):
325 335 from IPython.config.application import Application
326 336 return Application.instance().log
327 337
328 338
General Comments 0
You need to be logged in to leave comments. Login now