Show More
@@ -52,6 +52,7 b' class MultipleInstanceError(ConfigurableError):' | |||
|
52 | 52 | class Configurable(HasTraits): |
|
53 | 53 | |
|
54 | 54 | config = Instance(Config, (), {}) |
|
55 | parent = Instance('IPython.config.configurable.Configurable') | |
|
55 | 56 | created = None |
|
56 | 57 | |
|
57 | 58 | def __init__(self, **kwargs): |
@@ -63,6 +64,8 b' class Configurable(HasTraits):' | |||
|
63 | 64 | If this is empty, default values are used. If config is a |
|
64 | 65 | :class:`Config` instance, it will be used to configure the |
|
65 | 66 | instance. |
|
67 | parent : Configurable instance | |
|
68 | The parent | |
|
66 | 69 | |
|
67 | 70 | Notes |
|
68 | 71 | ----- |
@@ -77,6 +80,13 b' class Configurable(HasTraits):' | |||
|
77 | 80 | |
|
78 | 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 | 90 | config = kwargs.pop('config', None) |
|
81 | 91 | if config is not None: |
|
82 | 92 | # We used to deepcopy, but for now we are trying to just save |
@@ -95,39 +105,28 b' class Configurable(HasTraits):' | |||
|
95 | 105 | #------------------------------------------------------------------------- |
|
96 | 106 | # Static trait notifiations |
|
97 | 107 | #------------------------------------------------------------------------- |
|
98 | ||
|
99 | def _config_changed(self, name, old, new): | |
|
100 | """Update all the class traits having ``config=True`` as metadata. | |
|
101 | ||
|
102 | For any class trait with a ``config`` metadata attribute that is | |
|
103 | ``True``, we update the trait with the value of the corresponding | |
|
104 | config entry. | |
|
105 | """ | |
|
106 | # Get all traits with a config metadata entry that is True | |
|
107 | traits = self.traits(config=True) | |
|
108 | ||
|
109 | # We auto-load config section for this class as well as any parent | |
|
110 | # classes that are Configurable subclasses. This starts with Configurable | |
|
111 | # and works down the mro loading the config for each section. | |
|
112 |
section_names = |
|
|
113 | reversed(self.__class__.__mro__) if | |
|
114 | issubclass(cls, Configurable) and issubclass(self.__class__, cls)] | |
|
115 | ||
|
108 | ||
|
109 | @classmethod | |
|
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 | ] | |
|
115 | ||
|
116 | def _load_config(self, cfg, section_names=None, traits=None): | |
|
117 | """load traits from a Config object""" | |
|
118 | ||
|
119 | if traits is None: | |
|
120 | traits = self.traits(config=True) | |
|
121 | if section_names is None: | |
|
122 | section_names = self.section_names() | |
|
123 | ||
|
116 | 124 | for sname in section_names: |
|
117 | 125 | # Don't do a blind getattr as that would cause the config to |
|
118 | 126 | # dynamically create the section with name self.__class__.__name__. |
|
119 |
if |
|
|
120 |
my_config = |
|
|
127 | if cfg._has_section(sname): | |
|
128 | my_config = cfg[sname] | |
|
121 | 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 | 130 | try: |
|
132 | 131 | # Here we grab the value from the config |
|
133 | 132 | # If k has the naming convention of a config |
@@ -136,13 +135,35 b' class Configurable(HasTraits):' | |||
|
136 | 135 | except KeyError: |
|
137 | 136 | pass |
|
138 | 137 | else: |
|
139 | # print "Setting %s.%s from %s.%s=%r" % \ | |
|
140 | # (self.__class__.__name__,k,sname,k,config_value) | |
|
141 | 138 | # We have to do a deepcopy here if we don't deepcopy the entire |
|
142 | 139 | # config object. If we don't, a mutable config_value will be |
|
143 | 140 | # shared by all instances, effectively making it a class attribute. |
|
144 | 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 | 167 | def update_config(self, config): |
|
147 | 168 | """Fire the traits events when the config is updated.""" |
|
148 | 169 | # Save a copy of the current config. |
@@ -161,7 +182,6 b' class Configurable(HasTraits):' | |||
|
161 | 182 | class defaults. |
|
162 | 183 | """ |
|
163 | 184 | assert inst is None or isinstance(inst, cls) |
|
164 | cls_traits = cls.class_traits(config=True) | |
|
165 | 185 | final_help = [] |
|
166 | 186 | final_help.append(u'%s options' % cls.__name__) |
|
167 | 187 | final_help.append(len(final_help[0])*u'-') |
@@ -181,3 +181,59 b' class TestSingletonConfigurable(TestCase):' | |||
|
181 | 181 | self.assertEqual(bam, Bam._instance) |
|
182 | 182 | self.assertEqual(bam, Bar._instance) |
|
183 | 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