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