##// END OF EJS Templates
Merge sconfig branch.
Stefan van der Walt -
Show More
@@ -0,0 +1,179 b''
1 """Load and store configuration objects.
2
3 Test this module using
4
5 nosetests -v --with-doctest --doctest-tests IPython.config
6
7 """
8
9 from __future__ import with_statement
10 from contextlib import contextmanager
11
12 import inspect
13 import types
14 from IPython.config import traitlets
15 from traitlets import Traitlet
16 from IPython.external.configobj import ConfigObj
17
18 def debug(s):
19 import sys
20 sys.stderr.write(str(s) + '\n')
21
22 @contextmanager
23 def raw(config):
24 """Context manager for accessing traitlets directly.
25
26 """
27 config.__getattribute__('',raw_access=True)
28 yield config
29 config.__getattribute__('',raw_access=False)
30
31 class Config(object):
32 """
33 Implementation Notes
34 ====================
35 All instances of the same Config class share properties. Therefore,
36
37 >>> class Sample(Config):
38 ... my_float = traitlets.Float(3)
39
40 >>> s0 = Sample()
41 >>> s1 = Sample()
42 >>> s0.my_float = 5
43 >>> s0.my_float == s1.my_float
44 True
45
46 """
47 def __init__(self):
48 # Instantiate subconfigs
49 with raw(self):
50 subconfigs = [(n,v) for n,v in
51 inspect.getmembers(self, inspect.isclass)
52 if not n.startswith('__')]
53
54 for n,v in subconfigs:
55 setattr(self, n, v())
56
57 def __getattribute__(self,attr,raw_access=None,
58 _ns={'raw_access':False}):
59 if raw_access is not None:
60 _ns['raw_access'] = raw_access
61 return
62
63 obj = object.__getattribute__(self,attr)
64 if isinstance(obj,Traitlet) and not _ns['raw_access']:
65 return obj.__call__()
66 else:
67 return obj
68
69 def __setattr__(self,attr,value):
70 obj = object.__getattribute__(self,attr)
71 if isinstance(obj,Traitlet):
72 obj(value)
73 else:
74 self.__dict__[attr] = value
75
76 def __str__(self,level=1,only_modified=True):
77 ci = ConfigInspector(self)
78 out = ''
79 spacer = ' '*(level-1)
80
81 # Add traitlet representations
82 for p,v in ci.properties:
83 if (v.modified and only_modified) or not only_modified:
84 out += spacer + '%s = %s\n' % (p,v)
85
86 # Add subconfig representations
87 for (n,v) in ci.subconfigs:
88 sub_str = v.__str__(level=level+1,only_modified=only_modified)
89 if sub_str:
90 out += '\n' + spacer + '[' * level + ('%s' % n) \
91 + ']'*level + '\n'
92 out += sub_str
93
94 return out
95
96 def __iadd__(self,source):
97 """Load configuration from filename, and update self.
98
99 """
100 if not isinstance(source,dict):
101 source = ConfigObj(source, unrepr=True)
102 update_from_dict(self,source)
103 return self
104
105
106 class ConfigInspector(object):
107 """Allow the inspection of Config objects.
108
109 """
110 def __init__(self,config):
111 self._config = config
112
113 @property
114 def properties(self):
115 "Return all traitlet names."
116 with raw(self._config):
117 return inspect.getmembers(self._config,
118 lambda obj: isinstance(obj, Traitlet))
119
120 @property
121 def subconfigs(self):
122 "Return all subconfig names and values."
123 with raw(self._config):
124 return [(n,v) for n,v in
125 inspect.getmembers(self._config,
126 lambda obj: isinstance(obj,Config))
127 if not n.startswith('__')]
128
129 def reset(self):
130 for (p,v) in self.properties:
131 v.reset()
132
133 for (s,v) in self.subconfigs:
134 ConfigInspector(v).reset()
135
136 def update_from_dict(config,d):
137 """Propagate the values of the dictionary to the given configuration.
138
139 Useful to load configobj instances.
140
141 """
142 for k,v in d.items():
143 try:
144 prop_or_subconfig = getattr(config, k)
145 except AttributeError:
146 print "Invalid section/property in config file: %s" % k
147 else:
148 if isinstance(v,dict):
149 update_from_dict(prop_or_subconfig,v)
150 else:
151 setattr(config, k, v)
152
153 def dict_from_config(config,only_modified=True):
154 """Create a dictionary from a Config object."""
155 ci = ConfigInspector(config)
156 out = {}
157
158 for p,v in ci.properties:
159 if (v.modified and only_modified) or not only_modified:
160 out[p] = v
161
162 for s,v in ci.subconfigs:
163 d = dict_from_config(v,only_modified)
164 if d != {}:
165 out[s] = d
166
167 return out
168
169 def write(config, target):
170 """Write a configuration to file.
171
172 """
173 if isinstance(target, str):
174 target = open(target, 'w+')
175 target.flush()
176 target.seek(0)
177
178 confobj = ConfigObj(dict_from_config(config), unrepr=True)
179 confobj.write(target)
@@ -0,0 +1,19 b''
1 from IPython.config.api import *
2
3 class SubSubSample(Config):
4 my_int = Int(3)
5
6
7 class Sample(Config):
8 my_float = Float(3)
9 my_choice = Enum('a','b','c')
10
11 class MiddleSection(Config):
12 left_alone = Enum('1','2','c')
13 unknown_mod = Module('asd')
14
15 class SubSample(Config):
16 subsample_uri = URI('http://localhost:8080')
17
18 # Example of how to include external config
19 SubSubSample = SubSubSample()
@@ -0,0 +1,135 b''
1 """
2 # Test utilities
3
4 >>> import os
5
6 >>> def dict_as_sorted_list(d):
7 ... for k in d:
8 ... if isinstance(d[k],dict):
9 ... d[k] = dict_as_sorted_list(d[k])
10 ... return sorted(d.items())
11
12 >>> def pprint(d,level=0):
13 ... if isinstance(d,dict):
14 ... d = dict_as_sorted_list(d)
15 ... for item,value in d:
16 ... if isinstance(value,list):
17 ... print "%s%s" % (' '*level, item)
18 ... pprint(value,level+2)
19 ... else:
20 ... print "%s%s: %s" % (' '*level, item, value)
21
22
23 # Tests
24
25 >>> from IPython.config.api import *
26 >>> from sample_config import *
27
28 >>> s = Sample()
29 >>> print s.my_float
30 3.0
31 >>> s.my_float = 4
32 >>> print s.my_float
33 4.0
34 >>> print type(s.my_float)
35 <type 'float'>
36 >>> s.SubSample.SubSubSample.my_int = 5.0
37 >>> print s.SubSample.SubSubSample.my_int
38 5
39
40 >>> i = ConfigInspector(s)
41 >>> print i.properties
42 [('my_choice', 'a'), ('my_float', 4.0)]
43 >>> print tuple(s for s,v in i.subconfigs)
44 ('MiddleSection', 'SubSample')
45
46 >>> print s
47 my_float = 4.0
48 <BLANKLINE>
49 [SubSample]
50 <BLANKLINE>
51 [[SubSubSample]]
52 my_int = 5
53 <BLANKLINE>
54
55 >>> import tempfile
56 >>> fn = tempfile.mktemp()
57 >>> f = open(fn,'w')
58 >>> f.write(str(s))
59 >>> f.close()
60
61 >>> s += fn
62
63 >>> from IPython.external.configobj import ConfigObj
64 >>> c = ConfigObj(fn)
65 >>> c['SubSample']['subsample_uri'] = 'http://ipython.scipy.org'
66
67 >>> s += c
68 >>> print s
69 my_float = 4.0
70 <BLANKLINE>
71 [SubSample]
72 subsample_uri = 'http://ipython.scipy.org'
73 <BLANKLINE>
74 [[SubSubSample]]
75 my_int = 5
76 <BLANKLINE>
77
78 >>> pprint(dict_from_config(s,only_modified=False))
79 MiddleSection
80 left_alone: '1'
81 unknown_mod: 'asd'
82 SubSample
83 SubSubSample
84 my_int: 5
85 subsample_uri: 'http://ipython.scipy.org'
86 my_choice: 'a'
87 my_float: 4.0
88
89 >>> pprint(dict_from_config(s))
90 SubSample
91 SubSubSample
92 my_int: 5
93 subsample_uri: 'http://ipython.scipy.org'
94 my_float: 4.0
95
96 Test roundtripping:
97
98 >>> fn = tempfile.mktemp()
99 >>> f = open(fn, 'w')
100 >>> f.write('''
101 ... [MiddleSection]
102 ... # some comment here
103 ... left_alone = 'c'
104 ... ''')
105 >>> f.close()
106
107 >>> s += fn
108
109 >>> pprint(dict_from_config(s))
110 MiddleSection
111 left_alone: 'c'
112 SubSample
113 SubSubSample
114 my_int: 5
115 subsample_uri: 'http://ipython.scipy.org'
116 my_float: 4.0
117
118 >>> write(s, fn)
119 >>> f = file(fn,'r')
120 >>> ConfigInspector(s).reset()
121 >>> pprint(dict_from_config(s))
122
123 >>> s += fn
124 >>> os.unlink(fn)
125 >>> pprint(dict_from_config(s))
126 MiddleSection
127 left_alone: 'c'
128 SubSample
129 SubSubSample
130 my_int: 5
131 subsample_uri: 'http://ipython.scipy.org'
132 my_float: 4.0
133
134
135 """
@@ -0,0 +1,322 b''
1 """Traitlets -- a light-weight meta-class free stand-in for Traits.
2
3 Traitlet behaviour
4 ==================
5 - Automatic casting, equivalent to traits.C* classes, e.g. CFloat, CBool etc.
6
7 - By default, validation is done by attempting to cast a given value
8 to the underlying type, e.g. bool for Bool, float for Float etc.
9
10 - To set or get a Traitlet value, use the ()-operator. E.g.
11
12 >>> b = Bool(False)
13 >>> b(True)
14 >>> print b # returns a string representation of the Traitlet
15 True
16 >>> print b() # returns the underlying bool object
17 True
18
19 This makes it possible to change values "in-place", unlike an assigment
20 of the form
21
22 >>> c = Bool(False)
23 >>> c = True
24
25 which results in
26
27 >>> print type(b), type(c)
28 <class 'IPython.config.traitlets.Bool'> <type 'bool'>
29
30 - Each Traitlet keeps track of its modification state, e.g.
31
32 >>> c = Bool(False)
33 >>> print c.modified
34 False
35 >>> c(False)
36 >>> print c.modified
37 False
38 >>> c(True)
39 >>> print c.modified
40 True
41
42 How to customize Traitlets
43 ==========================
44
45 The easiest way to create a new Traitlet is by wrapping an underlying
46 Python type. This is done by setting the "_type" class attribute. For
47 example, creating an int-like Traitlet is done as follows:
48
49 >>> class MyInt(Traitlet):
50 ... _type = int
51
52 >>> i = MyInt(3)
53 >>> i(4)
54 >>> print i
55 4
56
57 >>> try:
58 ... i('a')
59 ... except ValidationError:
60 ... pass # this is expected
61 ... else:
62 ... "This should not be reached."
63
64 Furthermore, the following methods are provided for finer grained
65 control of validation and assignment:
66
67 - validate(self,value)
68 Ensure that "value" is valid. If not, raise an exception of any kind
69 with a suitable error message, which is reported to the user.
70
71 - prepare_value(self)
72 When using the ()-operator to query the underlying Traitlet value,
73 that value is first passed through prepare_value. For example:
74
75 >>> class Eval(Traitlet):
76 ... _type = str
77 ...
78 ... def prepare_value(self):
79 ... return eval(self._value)
80
81 >>> x = Eval('1+1')
82 >>> print x
83 '1+1'
84 >>> print x()
85 2
86
87 - __repr__(self)
88 By default, repr(self._value) is returned. This can be customised
89 to, for example, first call prepare_value and return the repr of
90 the resulting object.
91
92 """
93
94 import re
95 import types
96
97 class ValidationError(Exception):
98 pass
99
100 class Traitlet(object):
101 """Traitlet which knows its modification state.
102
103 """
104 def __init__(self, value):
105 "Validate and store the default value. State is 'unmodified'."
106 self._type = getattr(self,'_type',None)
107 value = self._parse_validation(value)
108 self._default_value = value
109 self.reset()
110
111 def reset(self):
112 self._value = self._default_value
113 self._changed = False
114
115 def validate(self, value):
116 "Validate the given value."
117 if self._type is not None:
118 self._type(value)
119
120 def _parse_validation(self, value):
121 """Run validation and return a descriptive error if needed.
122
123 """
124 try:
125 self.validate(value)
126 except Exception, e:
127 err_message = 'Cannot convert "%s" to %s' % \
128 (value, self.__class__.__name__.lower())
129 if e.message:
130 err_message += ': %s' % e.message
131 raise ValidationError(err_message)
132 else:
133 # Cast to appropriate type before storing
134 if self._type is not None:
135 value = self._type(value)
136 return value
137
138 def prepare_value(self):
139 """Run on value before it is ever returned to the user.
140
141 """
142 return self._value
143
144 def __call__(self,value=None):
145 """Query or set value depending on whether `value` is specified.
146
147 """
148 if value is None:
149 return self.prepare_value()
150
151 self._value = self._parse_validation(value)
152 self._changed = (self._value != self._default_value)
153
154 @property
155 def modified(self):
156 "Whether value has changed from original definition."
157 return self._changed
158
159 def __repr__(self):
160 """This class is represented by the underlying repr. Used when
161 dumping value to file.
162
163 """
164 return repr(self._value)
165
166 class Float(Traitlet):
167 """
168 >>> f = Float(0)
169 >>> print f.modified
170 False
171
172 >>> f(3)
173 >>> print f()
174 3.0
175 >>> print f.modified
176 True
177
178 >>> f(0)
179 >>> print f()
180 0.0
181 >>> print f.modified
182 False
183
184 >>> try:
185 ... f('a')
186 ... except ValidationError:
187 ... pass
188
189 """
190 _type = float
191
192 class Enum(Traitlet):
193 """
194 >>> c = Enum('a','b','c')
195 >>> print c()
196 a
197
198 >>> try:
199 ... c('unknown')
200 ... except ValidationError:
201 ... pass
202
203 >>> print c.modified
204 False
205
206 >>> c('b')
207 >>> print c()
208 b
209
210 """
211 def __init__(self, *options):
212 self._options = options
213 super(Enum,self).__init__(options[0])
214
215 def validate(self, value):
216 if not value in self._options:
217 raise ValueError('must be one of %s' % str(self._options))
218
219 class Module(Traitlet):
220 """
221 >>> m = Module('some.unknown.module')
222 >>> print m
223 'some.unknown.module'
224
225 >>> m = Module('re')
226 >>> assert type(m()) is types.ModuleType
227
228 """
229 _type = str
230
231 def prepare_value(self):
232 try:
233 module = eval(self._value)
234 except:
235 module = None
236
237 if type(module) is not types.ModuleType:
238 raise ValueError("Invalid module name: %s" % self._value)
239 else:
240 return module
241
242
243 class URI(Traitlet):
244 """
245 >>> u = URI('http://')
246
247 >>> try:
248 ... u = URI('something.else')
249 ... except ValidationError:
250 ... pass
251
252 >>> u = URI('http://ipython.scipy.org/')
253 >>> print u
254 'http://ipython.scipy.org/'
255
256 """
257 _regexp = re.compile(r'^[a-zA-Z]+:\/\/')
258 _type = str
259
260 def validate(self, uri):
261 if not self._regexp.match(uri):
262 raise ValueError()
263
264 class Int(Traitlet):
265 """
266 >>> i = Int(3.5)
267 >>> print i
268 3
269 >>> print i()
270 3
271
272 >>> i = Int('4')
273 >>> print i
274 4
275
276 >>> try:
277 ... i = Int('a')
278 ... except ValidationError:
279 ... pass
280 ... else:
281 ... raise "Should fail"
282
283 """
284 _type = int
285
286 class Bool(Traitlet):
287 """
288 >>> b = Bool(2)
289 >>> print b
290 True
291 >>> print b()
292 True
293
294 >>> b = Bool('True')
295 >>> print b
296 True
297 >>> b(True)
298 >>> print b.modified
299 False
300
301 >>> print Bool(0)
302 False
303
304 """
305 _type = bool
306
307 class Unicode(Traitlet):
308 """
309 >>> u = Unicode(123)
310 >>> print u
311 u'123'
312
313 >>> u = Unicode('123')
314 >>> print u.modified
315 False
316
317 >>> u('hello world')
318 >>> print u
319 u'hello world'
320
321 """
322 _type = unicode
@@ -19,6 +19,11 b' import os'
19 19 from IPython.config.cutils import get_home_dir, get_ipython_dir
20 20 from IPython.external.configobj import ConfigObj
21 21
22 # Traitlets config imports
23 from IPython.config import traitlets
24 from IPython.config.config import *
25 from traitlets import *
26
22 27 class ConfigObjManager(object):
23 28
24 29 def __init__(self, configObj, filename):
1 NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (622 lines changed) Show them Hide them
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now