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 : |
|
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 TestTraitlet |
|
330 | class TestHasTraitlets(TestCase): | |
331 |
|
331 | |||
332 |
def test_ |
|
332 | def test_traitlet_names(self): | |
333 | class A(HasTraitlets): |
|
333 | class A(HasTraitlets): | |
334 |
|
|
334 | i = Int | |
335 |
|
|
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_ |
|
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