Show More
@@ -23,9 +23,9 b' Authors:' | |||||
23 |
|
23 | |||
24 | from weakref import WeakValueDictionary |
|
24 | from weakref import WeakValueDictionary | |
25 |
|
25 | |||
|
26 | from IPython.utils.ipstruct import Struct | |||
26 | from IPython.utils.traitlets import ( |
|
27 | from IPython.utils.traitlets import ( | |
27 | HasTraitlets, TraitletError, MetaHasTraitlets, |
|
28 | HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This | |
28 | Int, Float, Str, Bool, Unicode |
|
|||
29 | ) |
|
29 | ) | |
30 |
|
30 | |||
31 |
|
31 | |||
@@ -34,10 +34,6 b' from IPython.utils.traitlets import (' | |||||
34 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
35 |
|
35 | |||
36 |
|
36 | |||
37 | class Config(object): |
|
|||
38 | pass |
|
|||
39 |
|
||||
40 |
|
||||
41 | class MetaComponentTracker(type): |
|
37 | class MetaComponentTracker(type): | |
42 | """A metaclass that tracks instances of Components and its subclasses.""" |
|
38 | """A metaclass that tracks instances of Components and its subclasses.""" | |
43 |
|
39 | |||
@@ -47,9 +43,9 b' class MetaComponentTracker(type):' | |||||
47 | cls.__numcreated = 0 |
|
43 | cls.__numcreated = 0 | |
48 |
|
44 | |||
49 | def __call__(cls, *args, **kw): |
|
45 | def __call__(cls, *args, **kw): | |
50 | """Called when class is called (instantiated)!!! |
|
46 | """Called when *class* is called (instantiated)!!! | |
51 |
|
47 | |||
52 |
|
|
48 | When a Component or subclass is instantiated, this is called and | |
53 | the instance is saved in a WeakValueDictionary for tracking. |
|
49 | the instance is saved in a WeakValueDictionary for tracking. | |
54 | """ |
|
50 | """ | |
55 |
|
51 | |||
@@ -60,32 +56,34 b' class MetaComponentTracker(type):' | |||||
60 | c.__instance_refs[c.__numcreated] = instance |
|
56 | c.__instance_refs[c.__numcreated] = instance | |
61 | return instance |
|
57 | return instance | |
62 |
|
58 | |||
63 | def get_instances(cls): |
|
59 | def get_instances(cls, name=None, klass=None, root=None): | |
64 |
"""Get all instances of cls and its subclasses. |
|
60 | """Get all instances of cls and its subclasses. | |
65 | return cls.__instance_refs.values() |
|
|||
66 |
|
|
61 | ||
67 | def get_instances_by_name(cls, name): |
|
62 | Parameters | |
68 | """Get all instances of cls and its subclasses by name.""" |
|
63 | ---------- | |
69 | return [i for i in cls.get_instances() if i.name == name] |
|
64 | name : str | |
70 |
|
65 | Limit to components with this name. | ||
71 | def get_instances_by_subclass(cls, thisclass): |
|
66 | klass : class | |
72 | """Get all instances of cls that are instances of thisclass. |
|
67 | Limit to components having isinstance(component, klass) | |
73 |
|
68 | root : Component or subclass | ||
74 | This includes all instances of subclasses of thisclass. |
|
69 | Limit to components having this root. | |
75 | """ |
|
70 | """ | |
76 | return [i for i in cls.get_instances() if isinstance(i, thisclass)] |
|
71 | instances = cls.__instance_refs.values() | |
77 |
|
72 | if name is not None: | ||
78 | def get_instances_by_class(cls, thisclass): |
|
73 | instances = [i for i in instances if i.name == name] | |
79 | """Get all instances of cls that are instances of thisclass. |
|
74 | if klass is not None: | |
80 |
|
75 | instances = [i for i in instances if isinstance(i, klass)] | ||
81 | This exclused instances of thisclass subclasses. |
|
76 | if root is not None: | |
|
77 | instances = [i for i in instances if i.root == root] | |||
|
78 | return instances | |||
|
79 | ||||
|
80 | def get_instances_by_condition(cls, call, name=None, klass=None, root=None): | |||
|
81 | """Get all instances of cls, i such that call(i)==True. | |||
|
82 | ||||
|
83 | This also takes the ``name``, ``klass`` and ``root`` arguments of | |||
|
84 | :meth:`get_instance` | |||
82 | """ |
|
85 | """ | |
83 |
|
86 | return [i for i in cls.get_instances(name,klass,root) if call(i)] | ||
84 | return [i for i in cls.get_instances() if type(i) is thisclass] |
|
|||
85 |
|
||||
86 | def get_instances_by_condition(cls, call): |
|
|||
87 | """Get all instances of cls, i such that call(i)==True.""" |
|
|||
88 | return [i for i in cls.get_instances() if call(i)] |
|
|||
89 |
|
87 | |||
90 |
|
88 | |||
91 | class ComponentNameGenerator(object): |
|
89 | class ComponentNameGenerator(object): | |
@@ -117,7 +115,10 b' class Component(HasTraitlets):' | |||||
117 |
|
115 | |||
118 | __metaclass__ = MetaComponent |
|
116 | __metaclass__ = MetaComponent | |
119 |
|
117 | |||
120 | config = Config() |
|
118 | # Traitlets are fun! | |
|
119 | config = Instance(Struct) | |||
|
120 | parent = This(allow_none=True) | |||
|
121 | root = This(allow_none=True) | |||
121 |
|
122 | |||
122 | def __init__(self, parent, name=None, config=None): |
|
123 | def __init__(self, parent, name=None, config=None): | |
123 | """Create a component given a parent. |
|
124 | """Create a component given a parent. | |
@@ -140,11 +141,13 b' class Component(HasTraitlets):' | |||||
140 | to this argument. We might think about changing this behavior. |
|
141 | to this argument. We might think about changing this behavior. | |
141 | """ |
|
142 | """ | |
142 | super(Component, self).__init__() |
|
143 | super(Component, self).__init__() | |
|
144 | self._children = [] | |||
143 | if name is None: |
|
145 | if name is None: | |
144 |
self. |
|
146 | self.name = ComponentNameGenerator() | |
145 | else: |
|
147 | else: | |
146 |
self. |
|
148 | self.name = name | |
147 | self.parent = parent # this uses the property and handles None |
|
149 | self.root = self # This is the default, it is set when parent is set | |
|
150 | self.parent = parent | |||
148 | if config is not None: |
|
151 | if config is not None: | |
149 | self.config = config |
|
152 | self.config = config | |
150 | else: |
|
153 | else: | |
@@ -152,33 +155,35 b' class Component(HasTraitlets):' | |||||
152 | self.config = self.parent.config |
|
155 | self.config = self.parent.config | |
153 |
|
156 | |||
154 | #------------------------------------------------------------------------- |
|
157 | #------------------------------------------------------------------------- | |
155 | # Properties |
|
158 | # Static traitlet notifiations | |
156 | #------------------------------------------------------------------------- |
|
159 | #------------------------------------------------------------------------- | |
157 |
|
160 | |||
158 |
def _ |
|
161 | def _parent_changed(self, name, old, new): | |
159 | # This should use the ComponentNameGenerator to test for uniqueness |
|
162 | if old is not None: | |
160 | self._name = name |
|
163 | old._remove_child(self) | |
161 |
|
164 | if new is not None: | ||
162 | def _get_name(self): |
|
165 | new._add_child(self) | |
163 | return self._name |
|
|||
164 |
|
166 | |||
165 | name = property(_get_name, _set_name) |
|
167 | if new is None: | |
166 |
|
168 | self.root = self | ||
167 | def _set_parent(self, parent): |
|
|||
168 | if parent is None: |
|
|||
169 | self._parent = None |
|
|||
170 | self._root = self |
|
|||
171 | else: |
|
169 | else: | |
172 | assert isinstance(parent, Component), 'parent must be a component' |
|
170 | self.root = new.root | |
173 | self._parent = parent |
|
|||
174 | self._root = parent.root |
|
|||
175 |
|
||||
176 | def _get_parent(self): |
|
|||
177 | return self._parent |
|
|||
178 |
|
||||
179 | parent = property(_get_parent, _set_parent) |
|
|||
180 |
|
171 | |||
181 | @property |
|
172 | @property | |
182 |
def |
|
173 | def children(self): | |
183 | return self._root |
|
174 | """A list of all my child components.""" | |
184 |
|
175 | return self._children | ||
|
176 | ||||
|
177 | def _remove_child(self, child): | |||
|
178 | """A private method for removing children componenets.""" | |||
|
179 | if child in self._children: | |||
|
180 | index = self._children.index(child) | |||
|
181 | del self._children[index] | |||
|
182 | ||||
|
183 | def _add_child(self, child): | |||
|
184 | """A private method for adding children componenets.""" | |||
|
185 | if child not in self._children: | |||
|
186 | self._children.append(child) | |||
|
187 | ||||
|
188 | def __repr__(self): | |||
|
189 | return "<Component('%s')>" % self.name No newline at end of file |
@@ -31,7 +31,7 b' from unittest import TestCase' | |||||
31 | from IPython.utils.traitlets import ( |
|
31 | from IPython.utils.traitlets import ( | |
32 | HasTraitlets, MetaHasTraitlets, TraitletType, Any, |
|
32 | HasTraitlets, MetaHasTraitlets, TraitletType, Any, | |
33 | Int, Long, Float, Complex, Str, Unicode, Bool, TraitletError, |
|
33 | Int, Long, Float, Complex, Str, Unicode, Bool, TraitletError, | |
34 | Undefined, Type, Instance |
|
34 | Undefined, Type, Instance, This | |
35 | ) |
|
35 | ) | |
36 |
|
36 | |||
37 |
|
37 | |||
@@ -440,6 +440,44 b' class TestInstance(TestCase):' | |||||
440 | b = B() |
|
440 | b = B() | |
441 | self.assertRaises(TraitletError, getattr, b, 'inst') |
|
441 | self.assertRaises(TraitletError, getattr, b, 'inst') | |
442 |
|
442 | |||
|
443 | ||||
|
444 | class TestThis(TestCase): | |||
|
445 | ||||
|
446 | def test_this_class(self): | |||
|
447 | class Foo(HasTraitlets): | |||
|
448 | this = This | |||
|
449 | ||||
|
450 | f = Foo() | |||
|
451 | self.assertEquals(f.this, None) | |||
|
452 | g = Foo() | |||
|
453 | f.this = g | |||
|
454 | self.assertEquals(f.this, g) | |||
|
455 | self.assertRaises(TraitletError, setattr, f, 'this', 10) | |||
|
456 | ||||
|
457 | def test_this_inst(self): | |||
|
458 | class Foo(HasTraitlets): | |||
|
459 | this = This() | |||
|
460 | ||||
|
461 | f = Foo() | |||
|
462 | f.this = Foo() | |||
|
463 | self.assert_(isinstance(f.this, Foo)) | |||
|
464 | ||||
|
465 | def test_allow_none(self): | |||
|
466 | class Foo(HasTraitlets): | |||
|
467 | this = This(allow_none=False) | |||
|
468 | ||||
|
469 | f = Foo() | |||
|
470 | g = Foo() | |||
|
471 | f.this = g | |||
|
472 | self.assertEquals(f.this, g) | |||
|
473 | ||||
|
474 | f = Foo() | |||
|
475 | self.assertRaises(TraitletError, getattr, f, 'this') | |||
|
476 | ||||
|
477 | f = Foo() | |||
|
478 | self.assertRaises(TraitletError, setattr, f, 'this', None) | |||
|
479 | ||||
|
480 | ||||
443 | class TraitletTestBase(TestCase): |
|
481 | class TraitletTestBase(TestCase): | |
444 | """A best testing class for basic traitlet types.""" |
|
482 | """A best testing class for basic traitlet types.""" | |
445 |
|
483 |
@@ -167,19 +167,24 b' class TraitletType(object):' | |||||
167 | dv = self.default_value |
|
167 | dv = self.default_value | |
168 | return dv |
|
168 | return dv | |
169 |
|
169 | |||
170 | def __get__(self, obj, cls=None): |
|
170 | def __get__(self, obj, cls=None, skipset=False): | |
171 | """Get the value of the traitlet by self.name for the instance. |
|
171 | """Get the value of the traitlet by self.name for the instance. | |
172 |
|
172 | |||
173 | The creation of default values is deferred until this is called the |
|
173 | The creation of default values is deferred until this is called the | |
174 | first time. This is done so instances of the parent HasTraitlets |
|
174 | first time. This is done so instances of the parent HasTraitlets | |
175 | will have their own default value instances. |
|
175 | will have their own default value instances. | |
|
176 | ||||
|
177 | A default value is not validated until it is requested. Thus, if | |||
|
178 | you use an invalid default value, but never request it, you are fine. | |||
176 | """ |
|
179 | """ | |
177 | if obj is None: |
|
180 | if obj is None: | |
178 | return self |
|
181 | return self | |
179 | else: |
|
182 | else: | |
180 | if not obj._traitlet_values.has_key(self.name): |
|
183 | if not obj._traitlet_values.has_key(self.name): | |
181 | dv = self.get_default_value() |
|
184 | dv = self.get_default_value() | |
182 | self.__set__(obj, dv, first=True) |
|
185 | # Call __set__ with first=True so we don't get a recursion | |
|
186 | if not skipset: | |||
|
187 | self.__set__(obj, dv, first=True) | |||
183 | return dv |
|
188 | return dv | |
184 | else: |
|
189 | else: | |
185 | return obj._traitlet_values[self.name] |
|
190 | return obj._traitlet_values[self.name] | |
@@ -187,7 +192,8 b' class TraitletType(object):' | |||||
187 | def __set__(self, obj, value, first=False): |
|
192 | def __set__(self, obj, value, first=False): | |
188 | new_value = self._validate(obj, value) |
|
193 | new_value = self._validate(obj, value) | |
189 | if not first: |
|
194 | if not first: | |
190 | old_value = self.__get__(obj) |
|
195 | # Call __get__ with skipset=True so we don't get a recursion | |
|
196 | old_value = self.__get__(obj, skipset=True) | |||
191 | if old_value != new_value: |
|
197 | if old_value != new_value: | |
192 | obj._traitlet_values[self.name] = new_value |
|
198 | obj._traitlet_values[self.name] = new_value | |
193 | obj._notify_traitlet(self.name, old_value, new_value) |
|
199 | obj._notify_traitlet(self.name, old_value, new_value) | |
@@ -659,6 +665,40 b' class Instance(BaseClassResolver):' | |||||
659 | return dv |
|
665 | return dv | |
660 |
|
666 | |||
661 |
|
667 | |||
|
668 | class This(TraitletType): | |||
|
669 | """A traitlet for instances of the class containing this trait.""" | |||
|
670 | ||||
|
671 | info_text = 'an instance of the same type as the receiver' | |||
|
672 | ||||
|
673 | def __init__(self, default_value=None, allow_none=True, **metadata): | |||
|
674 | if default_value is not None: | |||
|
675 | raise TraitletError("The default value of 'This' can only be None.") | |||
|
676 | super(This, self).__init__(default_value, **metadata) | |||
|
677 | self._allow_none = allow_none | |||
|
678 | if allow_none: | |||
|
679 | self.info_text = self.info_text + ' or None' | |||
|
680 | ||||
|
681 | def validate(self, obj, value): | |||
|
682 | if value is None: | |||
|
683 | if self._allow_none: | |||
|
684 | return value | |||
|
685 | self.validate_failed(obj, value) | |||
|
686 | ||||
|
687 | if isinstance(value, obj.__class__): | |||
|
688 | return value | |||
|
689 | else: | |||
|
690 | self.validate_failed(obj, value) | |||
|
691 | ||||
|
692 | def validate_failed (self, obj, value): | |||
|
693 | kind = type(value) | |||
|
694 | if kind is InstanceType: | |||
|
695 | msg = 'class %s' % value.__class__.__name__ | |||
|
696 | else: | |||
|
697 | msg = '%s (i.e. %s)' % ( str( kind )[1:-1], repr( value ) ) | |||
|
698 | ||||
|
699 | self.error(obj, msg) | |||
|
700 | ||||
|
701 | ||||
662 | #----------------------------------------------------------------------------- |
|
702 | #----------------------------------------------------------------------------- | |
663 | # Basic TraitletTypes implementations/subclasses |
|
703 | # Basic TraitletTypes implementations/subclasses | |
664 | #----------------------------------------------------------------------------- |
|
704 | #----------------------------------------------------------------------------- |
General Comments 0
You need to be logged in to leave comments.
Login now