##// END OF EJS Templates
add parent to Configurable...
MinRK -
Show More
@@ -52,6 +52,7 b' class MultipleInstanceError(ConfigurableError):'
52 class Configurable(HasTraits):
52 class Configurable(HasTraits):
53
53
54 config = Instance(Config, (), {})
54 config = Instance(Config, (), {})
55 parent = Instance('IPython.config.configurable.Configurable')
55 created = None
56 created = None
56
57
57 def __init__(self, **kwargs):
58 def __init__(self, **kwargs):
@@ -63,6 +64,8 b' class Configurable(HasTraits):'
63 If this is empty, default values are used. If config is a
64 If this is empty, default values are used. If config is a
64 :class:`Config` instance, it will be used to configure the
65 :class:`Config` instance, it will be used to configure the
65 instance.
66 instance.
67 parent : Configurable instance
68 The parent
66
69
67 Notes
70 Notes
68 -----
71 -----
@@ -77,6 +80,13 b' class Configurable(HasTraits):'
77
80
78 This ensures that instances will be configured properly.
81 This ensures that instances will be configured properly.
79 """
82 """
83 parent = kwargs.pop('parent', None)
84 if parent:
85 # config is implied from parent
86 if 'config' not in kwargs:
87 kwargs['config'] = parent.config
88 self.parent = parent
89
80 config = kwargs.pop('config', None)
90 config = kwargs.pop('config', None)
81 if config is not None:
91 if config is not None:
82 # We used to deepcopy, but for now we are trying to just save
92 # We used to deepcopy, but for now we are trying to just save
@@ -96,38 +106,27 b' class Configurable(HasTraits):'
96 # Static trait notifiations
106 # Static trait notifiations
97 #-------------------------------------------------------------------------
107 #-------------------------------------------------------------------------
98
108
99 def _config_changed(self, name, old, new):
109 @classmethod
100 """Update all the class traits having ``config=True`` as metadata.
110 def section_names(cls):
111 """return section names as a list"""
112 return [c.__name__ for c in reversed(cls.__mro__) if
113 issubclass(c, Configurable) and issubclass(cls, c)
114 ]
101
115
102 For any class trait with a ``config`` metadata attribute that is
116 def _load_config(self, cfg, section_names=None, traits=None):
103 ``True``, we update the trait with the value of the corresponding
117 """load traits from a Config object"""
104 config entry.
105 """
106 # Get all traits with a config metadata entry that is True
107 traits = self.traits(config=True)
108
118
109 # We auto-load config section for this class as well as any parent
119 if traits is None:
110 # classes that are Configurable subclasses. This starts with Configurable
120 traits = self.traits(config=True)
111 # and works down the mro loading the config for each section.
121 if section_names is None:
112 section_names = [cls.__name__ for cls in \
122 section_names = self.section_names()
113 reversed(self.__class__.__mro__) if
114 issubclass(cls, Configurable) and issubclass(self.__class__, cls)]
115
123
116 for sname in section_names:
124 for sname in section_names:
117 # Don't do a blind getattr as that would cause the config to
125 # Don't do a blind getattr as that would cause the config to
118 # dynamically create the section with name self.__class__.__name__.
126 # dynamically create the section with name self.__class__.__name__.
119 if new._has_section(sname):
127 if cfg._has_section(sname):
120 my_config = new[sname]
128 my_config = cfg[sname]
121 for k, v in traits.iteritems():
129 for k, v in traits.iteritems():
122 # Don't allow traitlets with config=True to start with
123 # uppercase. Otherwise, they are confused with Config
124 # subsections. But, developers shouldn't have uppercase
125 # attributes anyways! (PEP 6)
126 if k[0].upper()==k[0] and not k.startswith('_'):
127 raise ConfigurableError('Configurable traitlets with '
128 'config=True must start with a lowercase so they are '
129 'not confused with Config subsections: %s.%s' % \
130 (self.__class__.__name__, k))
131 try:
130 try:
132 # Here we grab the value from the config
131 # Here we grab the value from the config
133 # If k has the naming convention of a config
132 # If k has the naming convention of a config
@@ -136,13 +135,35 b' class Configurable(HasTraits):'
136 except KeyError:
135 except KeyError:
137 pass
136 pass
138 else:
137 else:
139 # print "Setting %s.%s from %s.%s=%r" % \
140 # (self.__class__.__name__,k,sname,k,config_value)
141 # We have to do a deepcopy here if we don't deepcopy the entire
138 # We have to do a deepcopy here if we don't deepcopy the entire
142 # config object. If we don't, a mutable config_value will be
139 # config object. If we don't, a mutable config_value will be
143 # shared by all instances, effectively making it a class attribute.
140 # shared by all instances, effectively making it a class attribute.
144 setattr(self, k, deepcopy(config_value))
141 setattr(self, k, deepcopy(config_value))
145
142
143 def _config_changed(self, name, old, new):
144 """Update all the class traits having ``config=True`` as metadata.
145
146 For any class trait with a ``config`` metadata attribute that is
147 ``True``, we update the trait with the value of the corresponding
148 config entry.
149 """
150 # Get all traits with a config metadata entry that is True
151 traits = self.traits(config=True)
152
153 # We auto-load config section for this class as well as any parent
154 # classes that are Configurable subclasses. This starts with Configurable
155 # and works down the mro loading the config for each section.
156 section_names = self.section_names()
157 self._load_config(new, traits=traits, section_names=section_names)
158
159 # load parent config as well, if we have one
160 parent_section_names = [] if self.parent is None else self.parent.section_names()
161 for parent in parent_section_names:
162 print parent, new._has_section(parent), new[parent]
163 if not new._has_section(parent):
164 continue
165 self._load_config(new[parent], traits=traits, section_names=section_names)
166
146 def update_config(self, config):
167 def update_config(self, config):
147 """Fire the traits events when the config is updated."""
168 """Fire the traits events when the config is updated."""
148 # Save a copy of the current config.
169 # Save a copy of the current config.
@@ -161,7 +182,6 b' class Configurable(HasTraits):'
161 class defaults.
182 class defaults.
162 """
183 """
163 assert inst is None or isinstance(inst, cls)
184 assert inst is None or isinstance(inst, cls)
164 cls_traits = cls.class_traits(config=True)
165 final_help = []
185 final_help = []
166 final_help.append(u'%s options' % cls.__name__)
186 final_help.append(u'%s options' % cls.__name__)
167 final_help.append(len(final_help[0])*u'-')
187 final_help.append(len(final_help[0])*u'-')
@@ -181,3 +181,59 b' class TestSingletonConfigurable(TestCase):'
181 self.assertEqual(bam, Bam._instance)
181 self.assertEqual(bam, Bam._instance)
182 self.assertEqual(bam, Bar._instance)
182 self.assertEqual(bam, Bar._instance)
183 self.assertEqual(SingletonConfigurable._instance, None)
183 self.assertEqual(SingletonConfigurable._instance, None)
184
185
186 class MyParent(Configurable):
187 pass
188
189 class MyParent2(MyParent):
190 pass
191
192 class TestParentConfigurable(TestCase):
193
194 def test_parent_config(self):
195
196 cfg = Config({
197 'MyParent' : {
198 'MyConfigurable' : {
199 'b' : 2.0,
200 }
201 }
202 })
203 parent = MyParent(config=cfg)
204 myc = MyConfigurable(parent=parent)
205 self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b)
206
207 def test_parent_inheritance(self):
208
209 cfg = Config({
210 'MyParent' : {
211 'MyConfigurable' : {
212 'b' : 2.0,
213 }
214 }
215 })
216 parent = MyParent2(config=cfg)
217 myc = MyConfigurable(parent=parent)
218 self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b)
219
220 def test_parent_priority(self):
221
222 cfg = Config({
223 'MyConfigurable' : {
224 'b' : 2.0,
225 },
226 'MyParent' : {
227 'MyConfigurable' : {
228 'b' : 3.0,
229 }
230 },
231 'MyParent2' : {
232 'MyConfigurable' : {
233 'b' : 4.0,
234 }
235 }
236 })
237 parent = MyParent2(config=cfg)
238 myc = MyConfigurable(parent=parent)
239 self.assertEqual(myc.b, parent.config.MyParent2.MyConfigurable.b)
General Comments 0
You need to be logged in to leave comments. Login now