##// END OF EJS Templates
Improvement to how config is handled in Components....
Brian Granger -
Show More
@@ -20,7 +20,7 b' Authors:'
20 20 # Imports
21 21 #-----------------------------------------------------------------------------
22 22
23
23 from copy import deepcopy
24 24 from weakref import WeakValueDictionary
25 25
26 26 from IPython.utils.ipstruct import Struct
@@ -134,7 +134,7 b' class Component(HasTraitlets):'
134 134 name : str
135 135 The unique name of the component. If empty, then a unique
136 136 one will be autogenerated.
137 config : Config
137 config : Struct
138 138 If this is empty, self.config = parent.config, otherwise
139 139 self.config = config and root.config is ignored. This argument
140 140 should only be used to *override* the automatic inheritance of
@@ -165,10 +165,10 b' class Component(HasTraitlets):'
165 165 self.root = self # This is the default, it is set when parent is set
166 166 self.parent = parent
167 167 if config is not None:
168 self.config = config
168 self.config = deepcopy(config)
169 169 else:
170 170 if self.parent is not None:
171 self.config = self.parent.config
171 self.config = deepcopy(self.parent.config)
172 172
173 173 #-------------------------------------------------------------------------
174 174 # Static traitlet notifiations
@@ -194,6 +194,17 b' class Component(HasTraitlets):'
194 194 raise ComponentError("Error in setting the root attribute: "
195 195 "root != parent.root")
196 196
197 def _config_changed(self, name, old, new):
198 # Get all traitlets with a config_key metadata entry
199 traitlets = self.traitlets(config_key=lambda v: True)
200 for k, v in traitlets.items():
201 try:
202 config_value = new[v.get_metadata('config_key')]
203 except KeyError:
204 pass
205 else:
206 setattr(self, k, config_value)
207
197 208 @property
198 209 def children(self):
199 210 """A list of all my child components."""
@@ -24,7 +24,7 b' from unittest import TestCase'
24 24
25 25 from IPython.core.component import Component, ComponentError
26 26 from IPython.utils.traitlets import (
27 TraitletError
27 TraitletError, Int, Float, Str
28 28 )
29 29 from IPython.utils.ipstruct import Struct
30 30
@@ -147,6 +147,30 b' class TestComponentConfig(TestCase):'
147 147 self.assertEquals(c1.config, config)
148 148 self.assertEquals(c2.config, config)
149 149 self.assertEquals(c3.config, config)
150 # Test that we always make copies
151 self.assert_(c1.config is not config)
152 self.assert_(c2.config is not config)
153 self.assert_(c3.config is not config)
154 self.assert_(c1.config is not c2.config)
155 self.assert_(c2.config is not c3.config)
156
157 def test_inheritance(self):
158 class MyComponent(Component):
159 a = Int(1, config_key='A')
160 b = Float(1.0, config_key='B')
161 c = Str('no config')
162 config = Struct()
163 config.A = 2
164 config.B = 2.0
165 c1 = MyComponent(None, config=config)
166 c2 = MyComponent(c1)
167 self.assertEquals(c1.a, config.A)
168 self.assertEquals(c1.b, config.B)
169 self.assertEquals(c2.a, config.A)
170 self.assertEquals(c2.b, config.B)
171 c4 = MyComponent(c2, config=Struct())
172 self.assertEquals(c4.a, 1)
173 self.assertEquals(c4.b, 1.0)
150 174
151 175 class TestComponentName(TestCase):
152 176
@@ -42,7 +42,7 b' class Struct(dict):'
42 42 * Intelligent merging.
43 43 * Overloaded operators.
44 44 """
45
45 _allownew = True
46 46 def __init__(self, *args, **kw):
47 47 """Initialize with a dictionary, another Struct, or data.
48 48
@@ -327,15 +327,37 b' class TestHasTraitletsNotify(TestCase):'
327 327 self.assertEquals(len(a._traitlet_notifiers['a']),0)
328 328
329 329
330 class TestTraitletKeys(TestCase):
330 class TestHasTraitlets(TestCase):
331 331
332 def test_keys(self):
332 def test_traitlet_names(self):
333 333 class A(HasTraitlets):
334 a = Int
335 b = Float
334 i = Int
335 f = Float
336 a = A()
337 self.assertEquals(a.traitlet_names(),['i','f'])
338
339 def test_traitlet_metadata(self):
340 class A(HasTraitlets):
341 i = Int(config_key='MY_VALUE')
336 342 a = A()
337 self.assertEquals(a.traitlet_names(),['a','b'])
343 self.assertEquals(a.traitlet_metadata('i','config_key'), 'MY_VALUE')
338 344
345 def test_traitlets(self):
346 class A(HasTraitlets):
347 i = Int
348 f = Float
349 a = A()
350 self.assertEquals(a.traitlets(), dict(i=A.i, f=A.f))
351
352 def test_traitlets_metadata(self):
353 class A(HasTraitlets):
354 i = Int(config_key='VALUE1', other_thing='VALUE2')
355 f = Float(config_key='VALUE3', other_thing='VALUE2')
356 a = A()
357 # traitlets = a.traitlets(config_key=lambda v: True)
358 # self.assertEquals(traitlets, dict(i=A.i, f=A.f))
359 traitlets = a.traitlets(config_key='VALUE1', other_thing='VALUE2')
360 self.assertEquals(traitlets, dict(i=A.i))
339 361
340 362 #-----------------------------------------------------------------------------
341 363 # Tests for specific traitlet types
@@ -52,7 +52,7 b' Authors:'
52 52 import inspect
53 53 import sys
54 54 import types
55 from types import InstanceType, ClassType
55 from types import InstanceType, ClassType, FunctionType
56 56
57 57 ClassTypes = (ClassType, type)
58 58
@@ -133,6 +133,18 b' def parse_notifier_name(name):'
133 133 assert isinstance(n, str), "names must be strings"
134 134 return name
135 135
136
137 class _SimpleTest:
138 def __init__ ( self, value ): self.value = value
139 def __call__ ( self, test ):
140 print test, self.value
141 return test == self.value
142 def __repr__(self):
143 return "<SimpleTest(%r)" % self.value
144 def __str__(self):
145 return self.__repr__()
146
147
136 148 #-----------------------------------------------------------------------------
137 149 # Base TraitletType for all traitlets
138 150 #-----------------------------------------------------------------------------
@@ -166,7 +178,16 b' class TraitletType(object):'
166 178 """
167 179 if default_value is not NoDefaultSpecified:
168 180 self.default_value = default_value
169 self.metadata.update(metadata)
181
182 if len(metadata) > 0:
183 if len(self.metadata) > 0:
184 self._metadata = self.metadata.copy()
185 self._metadata.update(metadata)
186 else:
187 self._metadata = metadata
188 else:
189 self._metadata = self.metadata
190
170 191 self.init()
171 192
172 193 def init(self):
@@ -238,6 +259,12 b' class TraitletType(object):'
238 259 % (self.name, self.info(), repr_type(value))
239 260 raise TraitletError(e)
240 261
262 def get_metadata(self, key):
263 return getattr(self, '_metadata', {}).get(key, None)
264
265 def set_metadata(self, key, value):
266 getattr(self, '_metadata', {})[key] = value
267
241 268
242 269 #-----------------------------------------------------------------------------
243 270 # The HasTraitlets implementation
@@ -303,7 +330,6 b' class HasTraitlets(object):'
303 330 for key in dir(cls):
304 331 value = getattr(cls, key)
305 332 if isinstance(value, TraitletType):
306 # print 'value: ', value
307 333 value.set_default_value(inst)
308 334 return inst
309 335
@@ -408,10 +434,44 b' class HasTraitlets(object):'
408 434 for n in names:
409 435 self._add_notifiers(handler, n)
410 436
411 def traitlet_names(self):
437 def traitlet_names(self, **metadata):
412 438 """Get a list of all the names of this classes traitlets."""
413 return [memb[0] for memb in inspect.getmembers(self.__class__) if isinstance(memb[1], TraitletType)]
439 return self.traitlets(**metadata).keys()
414 440
441 def traitlets(self, **metadata):
442 """Get a list of all the traitlets of this class.
443
444 The TraitletTypes returned don't know anything about the values
445 that the various HasTraitlet's instances are holding.
446 """
447 traitlets = dict([memb for memb in inspect.getmembers(self.__class__) if \
448 isinstance(memb[1], TraitletType)])
449 if len(metadata) == 0:
450 return traitlets
451
452 for meta_name, meta_eval in metadata.items():
453 if type(meta_eval) is not FunctionType:
454 metadata[meta_name] = _SimpleTest(meta_eval)
455
456 result = {}
457 for name, traitlet in traitlets.items():
458 for meta_name, meta_eval in metadata.items():
459 if not meta_eval(traitlet.get_metadata(meta_name)):
460 break
461 else:
462 result[name] = traitlet
463
464 return result
465
466 def traitlet_metadata(self, traitletname, key):
467 """Get metadata values for traitlet by key."""
468 try:
469 traitlet = getattr(self.__class__, traitletname)
470 except AttributeError:
471 raise TraitletError("Class %s does not have a traitlet named %s" %
472 (self.__class__.__name__, traitletname))
473 else:
474 return traitlet.get_metadata(key)
415 475
416 476 #-----------------------------------------------------------------------------
417 477 # Actual TraitletTypes implementations/subclasses
General Comments 0
You need to be logged in to leave comments. Login now