##// END OF EJS Templates
add LazyConfigValue...
MinRK -
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
@@ -137,7 +137,7 b' class Configurable(HasTraits):'
137 137 if c._has_section(sname):
138 138 my_config.merge(c[sname])
139 139 return my_config
140
140
141 141 def _load_config(self, cfg, section_names=None, traits=None):
142 142 """load traits from a Config object"""
143 143
@@ -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:
@@ -147,7 +234,7 b' class Config(dict):'
147 234 def __deepcopy__(self, memo):
148 235 import copy
149 236 return type(self)(copy.deepcopy(self.items()))
150
237
151 238 def __getitem__(self, key):
152 239 # We cannot use directly self._is_section_key, because it triggers
153 240 # infinite recursion on top of PyPy. Instead, we manually fish the
@@ -170,7 +257,14 b' class Config(dict):'
170 257 dict.__setitem__(self, key, c)
171 258 return c
172 259 else:
173 return dict.__getitem__(self, key)
260 try:
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