##// END OF EJS Templates
Fixing minor typo.
Brian Granger -
Show More
@@ -1,225 +1,225 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A lightweight component system for IPython.
4 A lightweight component system for IPython.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2009 The IPython Development Team
13 # Copyright (C) 2008-2009 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 from copy import deepcopy
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
27 from IPython.utils.traitlets import (
27 from IPython.utils.traitlets import (
28 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
28 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
29 )
29 )
30
30
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Helper classes for Components
33 # Helper classes for Components
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36
36
37 class ComponentError(Exception):
37 class ComponentError(Exception):
38 pass
38 pass
39
39
40 class MetaComponentTracker(type):
40 class MetaComponentTracker(type):
41 """A metaclass that tracks instances of Components and its subclasses."""
41 """A metaclass that tracks instances of Components and its subclasses."""
42
42
43 def __init__(cls, name, bases, d):
43 def __init__(cls, name, bases, d):
44 super(MetaComponentTracker, cls).__init__(name, bases, d)
44 super(MetaComponentTracker, cls).__init__(name, bases, d)
45 cls.__instance_refs = WeakValueDictionary()
45 cls.__instance_refs = WeakValueDictionary()
46 cls.__numcreated = 0
46 cls.__numcreated = 0
47
47
48 def __call__(cls, *args, **kw):
48 def __call__(cls, *args, **kw):
49 """Called when *class* is called (instantiated)!!!
49 """Called when *class* is called (instantiated)!!!
50
50
51 When a Component or subclass is instantiated, this is called and
51 When a Component or subclass is instantiated, this is called and
52 the instance is saved in a WeakValueDictionary for tracking.
52 the instance is saved in a WeakValueDictionary for tracking.
53 """
53 """
54
54
55 instance = super(MetaComponentTracker, cls).__call__(*args, **kw)
55 instance = super(MetaComponentTracker, cls).__call__(*args, **kw)
56 for c in cls.__mro__:
56 for c in cls.__mro__:
57 if issubclass(cls, c) and issubclass(c, Component):
57 if issubclass(cls, c) and issubclass(c, Component):
58 c.__numcreated += 1
58 c.__numcreated += 1
59 c.__instance_refs[c.__numcreated] = instance
59 c.__instance_refs[c.__numcreated] = instance
60 return instance
60 return instance
61
61
62 def get_instances(cls, name=None, klass=None, root=None):
62 def get_instances(cls, name=None, klass=None, root=None):
63 """Get all instances of cls and its subclasses.
63 """Get all instances of cls and its subclasses.
64
64
65 Parameters
65 Parameters
66 ----------
66 ----------
67 name : str
67 name : str
68 Limit to components with this name.
68 Limit to components with this name.
69 klass : class
69 klass : class
70 Limit to components having isinstance(component, klass)
70 Limit to components having isinstance(component, klass)
71 root : Component or subclass
71 root : Component or subclass
72 Limit to components having this root.
72 Limit to components having this root.
73 """
73 """
74 instances = cls.__instance_refs.values()
74 instances = cls.__instance_refs.values()
75 if name is not None:
75 if name is not None:
76 instances = [i for i in instances if i.name == name]
76 instances = [i for i in instances if i.name == name]
77 if klass is not None:
77 if klass is not None:
78 instances = [i for i in instances if isinstance(i, klass)]
78 instances = [i for i in instances if isinstance(i, klass)]
79 if root is not None:
79 if root is not None:
80 instances = [i for i in instances if i.root == root]
80 instances = [i for i in instances if i.root == root]
81 return instances
81 return instances
82
82
83 def get_instances_by_condition(cls, call, name=None, klass=None, root=None):
83 def get_instances_by_condition(cls, call, name=None, klass=None, root=None):
84 """Get all instances of cls, i such that call(i)==True.
84 """Get all instances of cls, i such that call(i)==True.
85
85
86 This also takes the ``name``, ``klass`` and ``root`` arguments of
86 This also takes the ``name``, ``klass`` and ``root`` arguments of
87 :meth:`get_instance`
87 :meth:`get_instance`
88 """
88 """
89 return [i for i in cls.get_instances(name,klass,root) if call(i)]
89 return [i for i in cls.get_instances(name,klass,root) if call(i)]
90
90
91
91
92 class ComponentNameGenerator(object):
92 class ComponentNameGenerator(object):
93 """A Singleton to generate unique component names."""
93 """A Singleton to generate unique component names."""
94
94
95 def __init__(self, prefix):
95 def __init__(self, prefix):
96 self.prefix = prefix
96 self.prefix = prefix
97 self.i = 0
97 self.i = 0
98
98
99 def __call__(self):
99 def __call__(self):
100 count = self.i
100 count = self.i
101 self.i += 1
101 self.i += 1
102 return "%s%s" % (self.prefix, count)
102 return "%s%s" % (self.prefix, count)
103
103
104
104
105 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
105 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
106
106
107
107
108 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
108 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
109 pass
109 pass
110
110
111
111
112 #-----------------------------------------------------------------------------
112 #-----------------------------------------------------------------------------
113 # Component implementation
113 # Component implementation
114 #-----------------------------------------------------------------------------
114 #-----------------------------------------------------------------------------
115
115
116
116
117 class Component(HasTraitlets):
117 class Component(HasTraitlets):
118
118
119 __metaclass__ = MetaComponent
119 __metaclass__ = MetaComponent
120
120
121 # Traitlets are fun!
121 # Traitlets are fun!
122 config = Instance(Struct,(),{})
122 config = Instance(Struct,(),{})
123 parent = This()
123 parent = This()
124 root = This()
124 root = This()
125
125
126 def __init__(self, parent, name=None, config=None):
126 def __init__(self, parent, name=None, config=None):
127 """Create a component given a parent and possibly and name and config.
127 """Create a component given a parent and possibly and name and config.
128
128
129 Parameters
129 Parameters
130 ----------
130 ----------
131 parent : Component subclass
131 parent : Component subclass
132 The parent in the component graph. The parent is used
132 The parent in the component graph. The parent is used
133 to get the root of the component graph.
133 to get the root of the component graph.
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 : Struct
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
141 parent.config. If a caller wants to modify parent.config
141 parent.config. If a caller wants to modify parent.config
142 (not override), the caller should make a copy and change
142 (not override), the caller should make a copy and change
143 attributes and then pass the copy to this argument.
143 attributes and then pass the copy to this argument.
144
144
145 Notes
145 Notes
146 -----
146 -----
147 Subclasses of Component must call the :meth:`__init__` method of
147 Subclasses of Component must call the :meth:`__init__` method of
148 :class:`Component` *before* doing anything else and using
148 :class:`Component` *before* doing anything else and using
149 :func:`super`::
149 :func:`super`::
150
150
151 class MyComponent(Component):
151 class MyComponent(Component):
152 def __init__(self, parent, name=None, config=None):
152 def __init__(self, parent, name=None, config=None):
153 super(MyComponent, self).__init__(parent, name, config)
153 super(MyComponent, self).__init__(parent, name, config)
154 # Then any other code you need to finish initialization.
154 # Then any other code you need to finish initialization.
155
155
156 This ensures that the :attr:`parent`, :attr:`name` and :attr:`config`
156 This ensures that the :attr:`parent`, :attr:`name` and :attr:`config`
157 attributes are handled properly.
157 attributes are handled properly.
158 """
158 """
159 super(Component, self).__init__()
159 super(Component, self).__init__()
160 self._children = []
160 self._children = []
161 if name is None:
161 if name is None:
162 self.name = ComponentNameGenerator()
162 self.name = ComponentNameGenerator()
163 else:
163 else:
164 self.name = name
164 self.name = name
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 = deepcopy(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 = deepcopy(self.parent.config)
171 self.config = deepcopy(self.parent.config)
172
172
173 #-------------------------------------------------------------------------
173 #-------------------------------------------------------------------------
174 # Static traitlet notifiations
174 # Static traitlet notifiations
175 #-------------------------------------------------------------------------
175 #-------------------------------------------------------------------------
176
176
177 def _parent_changed(self, name, old, new):
177 def _parent_changed(self, name, old, new):
178 if old is not None:
178 if old is not None:
179 old._remove_child(self)
179 old._remove_child(self)
180 if new is not None:
180 if new is not None:
181 new._add_child(self)
181 new._add_child(self)
182
182
183 if new is None:
183 if new is None:
184 self.root = self
184 self.root = self
185 else:
185 else:
186 self.root = new.root
186 self.root = new.root
187
187
188 def _root_changed(self, name, old, new):
188 def _root_changed(self, name, old, new):
189 if self.parent is None:
189 if self.parent is None:
190 if not (new is self):
190 if not (new is self):
191 raise ComponentError("Root not self, but parent is None.")
191 raise ComponentError("Root not self, but parent is None.")
192 else:
192 else:
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):
197 def _config_changed(self, name, old, new):
198 # Get all traitlets with a config_key metadata entry
198 # Get all traitlets with a config_key metadata entry
199 traitlets = self.traitlets('config_key')
199 traitlets = self.traitlets('config_key')
200 for k, v in traitlets.items():
200 for k, v in traitlets.items():
201 try:
201 try:
202 config_value = new[v.get_metadata('config_key')]
202 config_value = new[v.get_metadata('config_key')]
203 except KeyError:
203 except KeyError:
204 pass
204 pass
205 else:
205 else:
206 setattr(self, k, config_value)
206 setattr(self, k, config_value)
207
207
208 @property
208 @property
209 def children(self):
209 def children(self):
210 """A list of all my child components."""
210 """A list of all my child components."""
211 return self._children
211 return self._children
212
212
213 def _remove_child(self, child):
213 def _remove_child(self, child):
214 """A private method for removing children componenets."""
214 """A private method for removing children components."""
215 if child in self._children:
215 if child in self._children:
216 index = self._children.index(child)
216 index = self._children.index(child)
217 del self._children[index]
217 del self._children[index]
218
218
219 def _add_child(self, child):
219 def _add_child(self, child):
220 """A private method for adding children componenets."""
220 """A private method for adding children components."""
221 if child not in self._children:
221 if child not in self._children:
222 self._children.append(child)
222 self._children.append(child)
223
223
224 def __repr__(self):
224 def __repr__(self):
225 return "<Component('%s')>" % self.name
225 return "<Component('%s')>" % self.name
General Comments 0
You need to be logged in to leave comments. Login now