##// END OF EJS Templates
Improvement to how config is handled in Components....
Brian Granger -
Show More
@@ -20,7 +20,7 b' Authors:'
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23
23 from copy import deepcopy
24 from weakref import WeakValueDictionary
24 from weakref import WeakValueDictionary
25
25
26 from IPython.utils.ipstruct import Struct
26 from IPython.utils.ipstruct import Struct
@@ -134,7 +134,7 b' class Component(HasTraitlets):'
134 name : str
134 name : str
135 The unique name of the component. If empty, then a unique
135 The unique name of the component. If empty, then a unique
136 one will be autogenerated.
136 one will be autogenerated.
137 config : Config
137 config : Struct
138 If this is empty, self.config = parent.config, otherwise
138 If this is empty, self.config = parent.config, otherwise
139 self.config = config and root.config is ignored. This argument
139 self.config = config and root.config is ignored. This argument
140 should only be used to *override* the automatic inheritance of
140 should only be used to *override* the automatic inheritance of
@@ -165,10 +165,10 b' class Component(HasTraitlets):'
165 self.root = self # This is the default, it is set when parent is set
165 self.root = self # This is the default, it is set when parent is set
166 self.parent = parent
166 self.parent = parent
167 if config is not None:
167 if config is not None:
168 self.config = config
168 self.config = deepcopy(config)
169 else:
169 else:
170 if self.parent is not None:
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 # Static traitlet notifiations
174 # Static traitlet notifiations
@@ -193,7 +193,18 b' class Component(HasTraitlets):'
193 if not self.parent.root is new:
193 if not self.parent.root is new:
194 raise ComponentError("Error in setting the root attribute: "
194 raise ComponentError("Error in setting the root attribute: "
195 "root != parent.root")
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 @property
208 @property
198 def children(self):
209 def children(self):
199 """A list of all my child components."""
210 """A list of all my child components."""
@@ -24,7 +24,7 b' from unittest import TestCase'
24
24
25 from IPython.core.component import Component, ComponentError
25 from IPython.core.component import Component, ComponentError
26 from IPython.utils.traitlets import (
26 from IPython.utils.traitlets import (
27 TraitletError
27 TraitletError, Int, Float, Str
28 )
28 )
29 from IPython.utils.ipstruct import Struct
29 from IPython.utils.ipstruct import Struct
30
30
@@ -147,6 +147,30 b' class TestComponentConfig(TestCase):'
147 self.assertEquals(c1.config, config)
147 self.assertEquals(c1.config, config)
148 self.assertEquals(c2.config, config)
148 self.assertEquals(c2.config, config)
149 self.assertEquals(c3.config, config)
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 class TestComponentName(TestCase):
175 class TestComponentName(TestCase):
152
176
@@ -42,7 +42,7 b' class Struct(dict):'
42 * Intelligent merging.
42 * Intelligent merging.
43 * Overloaded operators.
43 * Overloaded operators.
44 """
44 """
45
45 _allownew = True
46 def __init__(self, *args, **kw):
46 def __init__(self, *args, **kw):
47 """Initialize with a dictionary, another Struct, or data.
47 """Initialize with a dictionary, another Struct, or data.
48
48
@@ -327,15 +327,37 b' class TestHasTraitletsNotify(TestCase):'
327 self.assertEquals(len(a._traitlet_notifiers['a']),0)
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 class A(HasTraitlets):
333 class A(HasTraitlets):
334 a = Int
334 i = Int
335 b = Float
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 a = A()
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 # Tests for specific traitlet types
363 # Tests for specific traitlet types
@@ -52,7 +52,7 b' Authors:'
52 import inspect
52 import inspect
53 import sys
53 import sys
54 import types
54 import types
55 from types import InstanceType, ClassType
55 from types import InstanceType, ClassType, FunctionType
56
56
57 ClassTypes = (ClassType, type)
57 ClassTypes = (ClassType, type)
58
58
@@ -133,6 +133,18 b' def parse_notifier_name(name):'
133 assert isinstance(n, str), "names must be strings"
133 assert isinstance(n, str), "names must be strings"
134 return name
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 # Base TraitletType for all traitlets
149 # Base TraitletType for all traitlets
138 #-----------------------------------------------------------------------------
150 #-----------------------------------------------------------------------------
@@ -166,7 +178,16 b' class TraitletType(object):'
166 """
178 """
167 if default_value is not NoDefaultSpecified:
179 if default_value is not NoDefaultSpecified:
168 self.default_value = default_value
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 self.init()
191 self.init()
171
192
172 def init(self):
193 def init(self):
@@ -238,6 +259,12 b' class TraitletType(object):'
238 % (self.name, self.info(), repr_type(value))
259 % (self.name, self.info(), repr_type(value))
239 raise TraitletError(e)
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 # The HasTraitlets implementation
270 # The HasTraitlets implementation
@@ -303,7 +330,6 b' class HasTraitlets(object):'
303 for key in dir(cls):
330 for key in dir(cls):
304 value = getattr(cls, key)
331 value = getattr(cls, key)
305 if isinstance(value, TraitletType):
332 if isinstance(value, TraitletType):
306 # print 'value: ', value
307 value.set_default_value(inst)
333 value.set_default_value(inst)
308 return inst
334 return inst
309
335
@@ -408,10 +434,44 b' class HasTraitlets(object):'
408 for n in names:
434 for n in names:
409 self._add_notifiers(handler, n)
435 self._add_notifiers(handler, n)
410
436
411 def traitlet_names(self):
437 def traitlet_names(self, **metadata):
412 """Get a list of all the names of this classes traitlets."""
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 # Actual TraitletTypes implementations/subclasses
477 # Actual TraitletTypes implementations/subclasses
General Comments 0
You need to be logged in to leave comments. Login now