##// END OF EJS Templates
cleanup inheritance line in auto-config files...
MinRK -
Show More
@@ -1,327 +1,328 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 141 @classmethod
142 142 def class_get_help(cls):
143 143 """Get the help string for this class in ReST format."""
144 144 cls_traits = cls.class_traits(config=True)
145 145 final_help = []
146 146 final_help.append(u'%s options' % cls.__name__)
147 147 final_help.append(len(final_help[0])*u'-')
148 148 for k,v in cls.class_traits(config=True).iteritems():
149 149 help = cls.class_get_trait_help(v)
150 150 final_help.append(help)
151 151 return '\n'.join(final_help)
152 152
153 153 @classmethod
154 154 def class_get_trait_help(cls, trait):
155 155 """Get the help string for a single trait."""
156 156 lines = []
157 157 header = "--%s.%s=<%s>" % (cls.__name__, trait.name, trait.__class__.__name__)
158 158 lines.append(header)
159 159 try:
160 160 dvr = repr(trait.get_default_value())
161 161 except Exception:
162 162 dvr = None # ignore defaults we can't construct
163 163 if dvr is not None:
164 164 if len(dvr) > 64:
165 165 dvr = dvr[:61]+'...'
166 166 lines.append(indent('Default: %s'%dvr, 4))
167 167 if 'Enum' in trait.__class__.__name__:
168 168 # include Enum choices
169 169 lines.append(indent('Choices: %r'%(trait.values,)))
170 170
171 171 help = trait.get_metadata('help')
172 172 if help is not None:
173 173 help = '\n'.join(wrap_paragraphs(help, 76))
174 174 lines.append(indent(help, 4))
175 175 return '\n'.join(lines)
176 176
177 177 @classmethod
178 178 def class_print_help(cls):
179 179 """Get the help string for a single trait and print it."""
180 180 print cls.class_get_help()
181 181
182 182 @classmethod
183 183 def class_config_section(cls):
184 184 """Get the config class config section"""
185 185 def c(s):
186 186 """return a commented, wrapped block."""
187 187 s = '\n\n'.join(wrap_paragraphs(s, 78))
188 188
189 189 return '# ' + s.replace('\n', '\n# ')
190 190
191 191 # section header
192 192 breaker = '#' + '-'*78
193 193 s = "# %s configuration"%cls.__name__
194 194 lines = [breaker, s, breaker, '']
195 195 # get the description trait
196 196 desc = cls.class_traits().get('description')
197 197 if desc:
198 198 desc = desc.default_value
199 199 else:
200 200 # no description trait, use __doc__
201 201 desc = getattr(cls, '__doc__', '')
202 202 if desc:
203 203 lines.append(c(desc))
204 204 lines.append('')
205 205
206 206 parents = []
207 207 for parent in cls.mro():
208 208 # only include parents that are not base classes
209 209 # and are not the class itself
210 if issubclass(parent, Configurable) and \
211 not parent in (Configurable, SingletonConfigurable, cls):
210 # and have some configurable traits to inherit
211 if parent is not cls and issubclass(parent, Configurable) and \
212 parent.class_traits(config=True):
212 213 parents.append(parent)
213 214
214 215 if parents:
215 216 pstr = ', '.join([ p.__name__ for p in parents ])
216 217 lines.append(c('%s will inherit config from: %s'%(cls.__name__, pstr)))
217 218 lines.append('')
218 219
219 220 for name,trait in cls.class_traits(config=True).iteritems():
220 221 help = trait.get_metadata('help') or ''
221 222 lines.append(c(help))
222 223 lines.append('# c.%s.%s = %r'%(cls.__name__, name, trait.get_default_value()))
223 224 lines.append('')
224 225 return '\n'.join(lines)
225 226
226 227
227 228
228 229 class SingletonConfigurable(Configurable):
229 230 """A configurable that only allows one instance.
230 231
231 232 This class is for classes that should only have one instance of itself
232 233 or *any* subclass. To create and retrieve such a class use the
233 234 :meth:`SingletonConfigurable.instance` method.
234 235 """
235 236
236 237 _instance = None
237 238
238 239 @classmethod
239 240 def _walk_mro(cls):
240 241 """Walk the cls.mro() for parent classes that are also singletons
241 242
242 243 For use in instance()
243 244 """
244 245
245 246 for subclass in cls.mro():
246 247 if issubclass(cls, subclass) and \
247 248 issubclass(subclass, SingletonConfigurable) and \
248 249 subclass != SingletonConfigurable:
249 250 yield subclass
250 251
251 252 @classmethod
252 253 def clear_instance(cls):
253 254 """unset _instance for this class and singleton parents.
254 255 """
255 256 if not cls.initialized():
256 257 return
257 258 for subclass in cls._walk_mro():
258 259 if isinstance(subclass._instance, cls):
259 260 # only clear instances that are instances
260 261 # of the calling class
261 262 subclass._instance = None
262 263
263 264 @classmethod
264 265 def instance(cls, *args, **kwargs):
265 266 """Returns a global instance of this class.
266 267
267 268 This method create a new instance if none have previously been created
268 269 and returns a previously created instance is one already exists.
269 270
270 271 The arguments and keyword arguments passed to this method are passed
271 272 on to the :meth:`__init__` method of the class upon instantiation.
272 273
273 274 Examples
274 275 --------
275 276
276 277 Create a singleton class using instance, and retrieve it::
277 278
278 279 >>> from IPython.config.configurable import SingletonConfigurable
279 280 >>> class Foo(SingletonConfigurable): pass
280 281 >>> foo = Foo.instance()
281 282 >>> foo == Foo.instance()
282 283 True
283 284
284 285 Create a subclass that is retrived using the base class instance::
285 286
286 287 >>> class Bar(SingletonConfigurable): pass
287 288 >>> class Bam(Bar): pass
288 289 >>> bam = Bam.instance()
289 290 >>> bam == Bar.instance()
290 291 True
291 292 """
292 293 # Create and save the instance
293 294 if cls._instance is None:
294 295 inst = cls(*args, **kwargs)
295 296 # Now make sure that the instance will also be returned by
296 297 # parent classes' _instance attribute.
297 298 for subclass in cls._walk_mro():
298 299 subclass._instance = inst
299 300
300 301 if isinstance(cls._instance, cls):
301 302 return cls._instance
302 303 else:
303 304 raise MultipleInstanceError(
304 305 'Multiple incompatible subclass instances of '
305 306 '%s are being created.' % cls.__name__
306 307 )
307 308
308 309 @classmethod
309 310 def initialized(cls):
310 311 """Has an instance been created?"""
311 312 return hasattr(cls, "_instance") and cls._instance is not None
312 313
313 314
314 315 class LoggingConfigurable(Configurable):
315 316 """A parent class for Configurables that log.
316 317
317 318 Subclasses have a log trait, and the default behavior
318 319 is to get the logger from the currently running Application
319 320 via Application.instance().log.
320 321 """
321 322
322 323 log = Instance('logging.Logger')
323 324 def _log_default(self):
324 325 from IPython.config.application import Application
325 326 return Application.instance().log
326 327
327 328
General Comments 0
You need to be logged in to leave comments. Login now