##// END OF EJS Templates
allow multi-level Config parentage...
MinRK -
Show More
@@ -64,8 +64,8 b' class Configurable(HasTraits):'
64 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
65 :class:`Config` instance, it will be used to configure the
65 :class:`Config` instance, it will be used to configure the
66 instance.
66 instance.
67 parent : Configurable instance
67 parent : Configurable instance, optional
68 The parent
68 The parent Configurable instance of this object.
69
69
70 Notes
70 Notes
71 -----
71 -----
@@ -112,6 +112,31 b' class Configurable(HasTraits):'
112 return [c.__name__ for c in reversed(cls.__mro__) if
112 return [c.__name__ for c in reversed(cls.__mro__) if
113 issubclass(c, Configurable) and issubclass(cls, c)
113 issubclass(c, Configurable) and issubclass(cls, c)
114 ]
114 ]
115
116 def _find_my_config(self, cfg):
117 """extract my config from a global Config object
118
119 will construct a Config object of only the config values that apply to me
120 based on my mro(), as well as those of my parent(s) if they exist.
121
122 If I am Bar and my parent is Foo, and their parent is Tim,
123 this will return merge following config sections, in this order::
124
125 [Bar, Foo.bar, Tim.Foo.Bar]
126
127 With the last item being the highest priority.
128 """
129 cfgs = [cfg]
130 if self.parent:
131 cfgs.append(self.parent._find_my_config(cfg))
132 my_config = Config()
133 for c in cfgs:
134 for sname in self.section_names():
135 # Don't do a blind getattr as that would cause the config to
136 # dynamically create the section with name Class.__name__.
137 if c._has_section(sname):
138 my_config.merge(c[sname])
139 return my_config
115
140
116 def _load_config(self, cfg, section_names=None, traits=None):
141 def _load_config(self, cfg, section_names=None, traits=None):
117 """load traits from a Config object"""
142 """load traits from a Config object"""
@@ -121,24 +146,13 b' class Configurable(HasTraits):'
121 if section_names is None:
146 if section_names is None:
122 section_names = self.section_names()
147 section_names = self.section_names()
123
148
124 for sname in section_names:
149 my_config = self._find_my_config(cfg)
125 # Don't do a blind getattr as that would cause the config to
150 for name, config_value in my_config.iteritems():
126 # dynamically create the section with name self.__class__.__name__.
151 if name in traits:
127 if cfg._has_section(sname):
152 # We have to do a deepcopy here if we don't deepcopy the entire
128 my_config = cfg[sname]
153 # config object. If we don't, a mutable config_value will be
129 for k, v in traits.iteritems():
154 # shared by all instances, effectively making it a class attribute.
130 try:
155 setattr(self, name, deepcopy(config_value))
131 # Here we grab the value from the config
132 # If k has the naming convention of a config
133 # section, it will be auto created.
134 config_value = my_config[k]
135 except KeyError:
136 pass
137 else:
138 # We have to do a deepcopy here if we don't deepcopy the entire
139 # config object. If we don't, a mutable config_value will be
140 # shared by all instances, effectively making it a class attribute.
141 setattr(self, k, deepcopy(config_value))
142
156
143 def _config_changed(self, name, old, new):
157 def _config_changed(self, name, old, new):
144 """Update all the class traits having ``config=True`` as metadata.
158 """Update all the class traits having ``config=True`` as metadata.
@@ -155,13 +169,6 b' class Configurable(HasTraits):'
155 # and works down the mro loading the config for each section.
169 # and works down the mro loading the config for each section.
156 section_names = self.section_names()
170 section_names = self.section_names()
157 self._load_config(new, traits=traits, section_names=section_names)
171 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 if not new._has_section(parent):
163 continue
164 self._load_config(new[parent], traits=traits, section_names=section_names)
165
172
166 def update_config(self, config):
173 def update_config(self, config):
167 """Fire the traits events when the config is updated."""
174 """Fire the traits events when the config is updated."""
@@ -192,7 +192,6 b' class MyParent2(MyParent):'
192 class TestParentConfigurable(TestCase):
192 class TestParentConfigurable(TestCase):
193
193
194 def test_parent_config(self):
194 def test_parent_config(self):
195
196 cfg = Config({
195 cfg = Config({
197 'MyParent' : {
196 'MyParent' : {
198 'MyConfigurable' : {
197 'MyConfigurable' : {
@@ -205,7 +204,6 b' class TestParentConfigurable(TestCase):'
205 self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b)
204 self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b)
206
205
207 def test_parent_inheritance(self):
206 def test_parent_inheritance(self):
208
209 cfg = Config({
207 cfg = Config({
210 'MyParent' : {
208 'MyParent' : {
211 'MyConfigurable' : {
209 'MyConfigurable' : {
@@ -217,8 +215,26 b' class TestParentConfigurable(TestCase):'
217 myc = MyConfigurable(parent=parent)
215 myc = MyConfigurable(parent=parent)
218 self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b)
216 self.assertEqual(myc.b, parent.config.MyParent.MyConfigurable.b)
219
217
218 def test_multi_parent(self):
219 cfg = Config({
220 'MyParent2' : {
221 'MyParent' : {
222 'MyConfigurable' : {
223 'b' : 2.0,
224 }
225 },
226 # this one shouldn't count
227 'MyConfigurable' : {
228 'b' : 3.0,
229 },
230 }
231 })
232 parent2 = MyParent2(config=cfg)
233 parent = MyParent(parent=parent2)
234 myc = MyConfigurable(parent=parent)
235 self.assertEqual(myc.b, parent.config.MyParent2.MyParent.MyConfigurable.b)
236
220 def test_parent_priority(self):
237 def test_parent_priority(self):
221
222 cfg = Config({
238 cfg = Config({
223 'MyConfigurable' : {
239 'MyConfigurable' : {
224 'b' : 2.0,
240 'b' : 2.0,
@@ -237,3 +253,32 b' class TestParentConfigurable(TestCase):'
237 parent = MyParent2(config=cfg)
253 parent = MyParent2(config=cfg)
238 myc = MyConfigurable(parent=parent)
254 myc = MyConfigurable(parent=parent)
239 self.assertEqual(myc.b, parent.config.MyParent2.MyConfigurable.b)
255 self.assertEqual(myc.b, parent.config.MyParent2.MyConfigurable.b)
256
257 def test_multi_parent_priority(self):
258 cfg = Config({
259 'MyConfigurable' : {
260 'b' : 2.0,
261 },
262 'MyParent' : {
263 'MyConfigurable' : {
264 'b' : 3.0,
265 }
266 },
267 'MyParent2' : {
268 'MyConfigurable' : {
269 'b' : 4.0,
270 }
271 },
272 'MyParent2' : {
273 'MyParent' : {
274 'MyConfigurable' : {
275 'b' : 5.0,
276 }
277 }
278 }
279 })
280 parent2 = MyParent2(config=cfg)
281 parent = MyParent2(parent=parent2)
282 myc = MyConfigurable(parent=parent)
283 self.assertEqual(myc.b, parent.config.MyParent2.MyParent.MyConfigurable.b)
284
General Comments 0
You need to be logged in to leave comments. Login now