##// 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
@@ -1,99 +1,104 b''
1 # encoding: utf-8
1 # encoding: utf-8
2
2
3 """This is the official entry point to IPython's configuration system. """
3 """This is the official entry point to IPython's configuration system. """
4
4
5 __docformat__ = "restructuredtext en"
5 __docformat__ = "restructuredtext en"
6
6
7 #-------------------------------------------------------------------------------
7 #-------------------------------------------------------------------------------
8 # Copyright (C) 2008 The IPython Development Team
8 # Copyright (C) 2008 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-------------------------------------------------------------------------------
12 #-------------------------------------------------------------------------------
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-------------------------------------------------------------------------------
16 #-------------------------------------------------------------------------------
17
17
18 import os
18 import os
19 from IPython.config.cutils import get_home_dir, get_ipython_dir
19 from IPython.config.cutils import get_home_dir, get_ipython_dir
20 from IPython.external.configobj import ConfigObj
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 class ConfigObjManager(object):
27 class ConfigObjManager(object):
23
28
24 def __init__(self, configObj, filename):
29 def __init__(self, configObj, filename):
25 self.current = configObj
30 self.current = configObj
26 self.current.indent_type = ' '
31 self.current.indent_type = ' '
27 self.filename = filename
32 self.filename = filename
28 # self.write_default_config_file()
33 # self.write_default_config_file()
29
34
30 def get_config_obj(self):
35 def get_config_obj(self):
31 return self.current
36 return self.current
32
37
33 def update_config_obj(self, newConfig):
38 def update_config_obj(self, newConfig):
34 self.current.merge(newConfig)
39 self.current.merge(newConfig)
35
40
36 def update_config_obj_from_file(self, filename):
41 def update_config_obj_from_file(self, filename):
37 newConfig = ConfigObj(filename, file_error=False)
42 newConfig = ConfigObj(filename, file_error=False)
38 self.current.merge(newConfig)
43 self.current.merge(newConfig)
39
44
40 def update_config_obj_from_default_file(self, ipythondir=None):
45 def update_config_obj_from_default_file(self, ipythondir=None):
41 fname = self.resolve_file_path(self.filename, ipythondir)
46 fname = self.resolve_file_path(self.filename, ipythondir)
42 self.update_config_obj_from_file(fname)
47 self.update_config_obj_from_file(fname)
43
48
44 def write_config_obj_to_file(self, filename):
49 def write_config_obj_to_file(self, filename):
45 f = open(filename, 'w')
50 f = open(filename, 'w')
46 self.current.write(f)
51 self.current.write(f)
47 f.close()
52 f.close()
48
53
49 def write_default_config_file(self):
54 def write_default_config_file(self):
50 ipdir = get_ipython_dir()
55 ipdir = get_ipython_dir()
51 fname = ipdir + '/' + self.filename
56 fname = ipdir + '/' + self.filename
52 if not os.path.isfile(fname):
57 if not os.path.isfile(fname):
53 print "Writing the configuration file to: " + fname
58 print "Writing the configuration file to: " + fname
54 self.write_config_obj_to_file(fname)
59 self.write_config_obj_to_file(fname)
55
60
56 def _import(self, key):
61 def _import(self, key):
57 package = '.'.join(key.split('.')[0:-1])
62 package = '.'.join(key.split('.')[0:-1])
58 obj = key.split('.')[-1]
63 obj = key.split('.')[-1]
59 execString = 'from %s import %s' % (package, obj)
64 execString = 'from %s import %s' % (package, obj)
60 exec execString
65 exec execString
61 exec 'temp = %s' % obj
66 exec 'temp = %s' % obj
62 return temp
67 return temp
63
68
64 def resolve_file_path(self, filename, ipythondir = None):
69 def resolve_file_path(self, filename, ipythondir = None):
65 """Resolve filenames into absolute paths.
70 """Resolve filenames into absolute paths.
66
71
67 This function looks in the following directories in order:
72 This function looks in the following directories in order:
68
73
69 1. In the current working directory or by absolute path with ~ expanded
74 1. In the current working directory or by absolute path with ~ expanded
70 2. In ipythondir if that is set
75 2. In ipythondir if that is set
71 3. In the IPYTHONDIR environment variable if it exists
76 3. In the IPYTHONDIR environment variable if it exists
72 4. In the ~/.ipython directory
77 4. In the ~/.ipython directory
73
78
74 Note: The IPYTHONDIR is also used by the trunk version of IPython so
79 Note: The IPYTHONDIR is also used by the trunk version of IPython so
75 changing it will also affect it was well.
80 changing it will also affect it was well.
76 """
81 """
77
82
78 # In cwd or by absolute path with ~ expanded
83 # In cwd or by absolute path with ~ expanded
79 trythis = os.path.expanduser(filename)
84 trythis = os.path.expanduser(filename)
80 if os.path.isfile(trythis):
85 if os.path.isfile(trythis):
81 return trythis
86 return trythis
82
87
83 # In ipythondir if it is set
88 # In ipythondir if it is set
84 if ipythondir is not None:
89 if ipythondir is not None:
85 trythis = ipythondir + '/' + filename
90 trythis = ipythondir + '/' + filename
86 if os.path.isfile(trythis):
91 if os.path.isfile(trythis):
87 return trythis
92 return trythis
88
93
89 trythis = get_ipython_dir() + '/' + filename
94 trythis = get_ipython_dir() + '/' + filename
90 if os.path.isfile(trythis):
95 if os.path.isfile(trythis):
91 return trythis
96 return trythis
92
97
93 return None
98 return None
94
99
95
100
96
101
97
102
98
103
99
104
1 NO CONTENT: file was removed
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
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now