Show More
@@ -28,7 +28,7 b' Authors:' | |||
|
28 | 28 | import datetime |
|
29 | 29 | from copy import deepcopy |
|
30 | 30 | |
|
31 | from loader import Config | |
|
31 | from .loader import Config, LazyConfigValue | |
|
32 | 32 | from IPython.utils.traitlets import HasTraits, Instance |
|
33 | 33 | from IPython.utils.text import indent, wrap_paragraphs |
|
34 | 34 | |
@@ -149,6 +149,11 b' class Configurable(HasTraits):' | |||
|
149 | 149 | my_config = self._find_my_config(cfg) |
|
150 | 150 | for name, config_value in my_config.iteritems(): |
|
151 | 151 | if name in traits: |
|
152 | if isinstance(config_value, LazyConfigValue): | |
|
153 | # ConfigValue is a wrapper for using append / update on containers | |
|
154 | # without having to copy the | |
|
155 | initial = getattr(self, name) | |
|
156 | config_value = config_value.get_value(initial) | |
|
152 | 157 | # We have to do a deepcopy here if we don't deepcopy the entire |
|
153 | 158 | # config object. If we don't, a mutable config_value will be |
|
154 | 159 | # shared by all instances, effectively making it a class attribute. |
@@ -25,6 +25,7 b' Authors' | |||
|
25 | 25 | |
|
26 | 26 | import __builtin__ as builtin_mod |
|
27 | 27 | import argparse |
|
28 | import copy | |
|
28 | 29 | import os |
|
29 | 30 | import re |
|
30 | 31 | import sys |
@@ -32,6 +33,7 b' import sys' | |||
|
32 | 33 | from IPython.utils.path import filefind, get_ipython_dir |
|
33 | 34 | from IPython.utils import py3compat, warn |
|
34 | 35 | from IPython.utils.encoding import DEFAULT_ENCODING |
|
36 | from IPython.utils.traitlets import HasTraits, List, Any, TraitError | |
|
35 | 37 | |
|
36 | 38 | #----------------------------------------------------------------------------- |
|
37 | 39 | # Exceptions |
@@ -74,6 +76,83 b' class ArgumentParser(argparse.ArgumentParser):' | |||
|
74 | 76 | # Config class for holding config information |
|
75 | 77 | #----------------------------------------------------------------------------- |
|
76 | 78 | |
|
79 | class LazyConfigValue(HasTraits): | |
|
80 | """Proxy object for exposing methods on configurable containers | |
|
81 | ||
|
82 | Exposes: | |
|
83 | ||
|
84 | - append, extend, insert on lists | |
|
85 | - update on dicts | |
|
86 | - update, add on sets | |
|
87 | """ | |
|
88 | ||
|
89 | _value = None | |
|
90 | ||
|
91 | # list methods | |
|
92 | _extend = List() | |
|
93 | ||
|
94 | def append(self, obj): | |
|
95 | self._extend.append(obj) | |
|
96 | ||
|
97 | def extend(self, other): | |
|
98 | self._extend.extend(other) | |
|
99 | ||
|
100 | _inserts = List() | |
|
101 | def insert(self, index, other): | |
|
102 | if not isinstance(index, int): | |
|
103 | raise TypeError("An integer is required") | |
|
104 | self._inserts.append((index, other)) | |
|
105 | ||
|
106 | # dict methods | |
|
107 | # update is used for both dict and set | |
|
108 | _update = Any() | |
|
109 | def update(self, other): | |
|
110 | if self._update is None: | |
|
111 | if isinstance(other, dict): | |
|
112 | self._update = {} | |
|
113 | else: | |
|
114 | self._update = set() | |
|
115 | self._update.update(other) | |
|
116 | ||
|
117 | # set methods | |
|
118 | def add(self, obj): | |
|
119 | self.update({obj}) | |
|
120 | ||
|
121 | def get_value(self, initial): | |
|
122 | """construct the value from the initial one | |
|
123 | ||
|
124 | after applying any insert / extend / update changes | |
|
125 | """ | |
|
126 | if self._value is not None: | |
|
127 | return self._value | |
|
128 | value = copy.deepcopy(initial) | |
|
129 | if isinstance(value, list): | |
|
130 | for idx, obj in self._inserts: | |
|
131 | value.insert(idx, obj) | |
|
132 | value.extend(self._extend) | |
|
133 | elif isinstance(value, dict): | |
|
134 | if self._update: | |
|
135 | value.update(self._update) | |
|
136 | elif isinstance(value, set): | |
|
137 | if self._update: | |
|
138 | value.update(self._update) | |
|
139 | self._value = value | |
|
140 | return value | |
|
141 | ||
|
142 | def to_dict(self): | |
|
143 | """return JSONable dict form of my data | |
|
144 | ||
|
145 | Currently update as dict or set, extend as list, and inserts as list of tuples. | |
|
146 | """ | |
|
147 | d = {} | |
|
148 | if self._update: | |
|
149 | d['update'] = self._update | |
|
150 | if self._extend: | |
|
151 | d['extend'] = self._extend | |
|
152 | elif self._inserts: | |
|
153 | d['inserts'] = self._inserts | |
|
154 | return d | |
|
155 | ||
|
77 | 156 | |
|
78 | 157 | class Config(dict): |
|
79 | 158 | """An attribute based dict that can do smart merges.""" |
@@ -125,6 +204,14 b' class Config(dict):' | |||
|
125 | 204 | return False |
|
126 | 205 | |
|
127 | 206 | def __contains__(self, key): |
|
207 | # allow nested contains of the form `Section.key in config` | |
|
208 | if '.' in key: | |
|
209 | first, remainder = key.split('.', 1) | |
|
210 | if first not in self: | |
|
211 | return False | |
|
212 | return remainder in self[first] | |
|
213 | ||
|
214 | # we always have Sections | |
|
128 | 215 | if self._is_section_key(key): |
|
129 | 216 | return True |
|
130 | 217 | else: |
@@ -170,7 +257,14 b' class Config(dict):' | |||
|
170 | 257 | dict.__setitem__(self, key, c) |
|
171 | 258 | return c |
|
172 | 259 | else: |
|
260 | try: | |
|
173 | 261 | return dict.__getitem__(self, key) |
|
262 | except KeyError: | |
|
263 | # undefined | |
|
264 | v = LazyConfigValue() | |
|
265 | dict.__setitem__(self, key, v) | |
|
266 | return v | |
|
267 | ||
|
174 | 268 | |
|
175 | 269 | def __setitem__(self, key, value): |
|
176 | 270 | if self._is_section_key(key): |
General Comments 0
You need to be logged in to leave comments.
Login now