##// END OF EJS Templates
First prototype of component, traitlets and a config loader.
Brian Granger -
Show More
@@ -0,0 +1,186 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """A factory for creating configuration objects.
4 """
5
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2008-2009 The IPython Development Team
8 #
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
12
13 #-----------------------------------------------------------------------------
14 # Imports
15 #-----------------------------------------------------------------------------
16
17 import os
18
19 from IPython.utils.ipstruct import Struct
20
21 #-----------------------------------------------------------------------------
22 # Code
23 #-----------------------------------------------------------------------------
24
25
26 class ConfigLoaderError(Exception):
27 pass
28
29
30 class ConfigLoader(object):
31 """A object for loading configurations from just about anywhere.
32
33 The resulting configuration is packaged as a :class:`Struct`.
34 """
35
36 def __init__(self):
37 """A base class for config loaders.
38
39 Examples
40 --------
41
42 >>> cl = ConfigLoader()
43 >>> config = cl.load_config()
44 >>> config
45 {}
46 """
47 self.clear()
48
49 def clear(self):
50 self.config = Struct()
51
52 def load_config(self):
53 """Load a config from somewhere, return a Struct.
54
55 Usually, this will cause self.config to be set and then returned.
56 """
57 return self.config
58
59
60 class FileConfigLoader(ConfigLoader):
61 """A config loader for pure python files.
62
63 This calls execfile on a plain python file and looks for attributes
64 that are all caps. These attribute are added to the config Struct.
65 """
66
67 def __init__(self, filename, path='.'):
68 """Build a config loader for a filename and path.
69
70 Parameters
71 ----------
72 filename : str
73 The file name of the config file.
74 path : str, list, tuple
75 The path to search for the config file on, or a sequence of
76 paths to try in order
77
78 Examples
79 --------
80
81
82 """
83 self.filename = filename
84 self.path = path
85 self.full_filename = ''
86 self.data = None
87 ConfigLoader.__init__(self)
88
89 def find_file(self):
90 """Implement file finding logic here."""
91 self.full_filename = self.filename
92
93 def read_file_as_dict(self):
94 if not os.path.isfile(self.full_filename):
95 raise IOError("config file does not exist: %r" % self.fullfilename)
96 self.data = {}
97 execfile(self.full_filename, self.data)
98
99 def convert_to_struct(self):
100 if self.data is None:
101 ConfigLoaderError('self.data does not exist')
102 for k, v in self.data.iteritems():
103 if k == k.upper():
104 self.config[k] = v
105
106 def load_config(self):
107 self.find_file()
108 self.read_file_as_dict()
109 self.convert_to_struct()
110 return self.config
111
112 class PyConfigLoader(object):
113 pass
114
115
116 class DefaultFileConfigLoader(object):
117
118 def __init__(self, filename, install_location):
119 pass
120
121 def load_config(self):
122 pass
123
124 def install(self, force=False):
125 pass
126
127
128 class CommandLineConfigLoader(ConfigLoader):
129
130 def __init__(self):
131 self.parser = None
132 self.parsed_data = None
133 self.clear()
134
135
136 def clear(self):
137 self.config = Struct()
138
139 def load_config(self, args=None):
140 self.create_parser()
141 self.parse_args(args)
142 self.convert_to_struct()
143 return self.config
144
145 def create_parser(self):
146 """Create self.parser"""
147
148 def parse_args(self, args=None):
149 """self.parser->self.parsed_data"""
150 if self.parser is None:
151 raise ConfigLoaderError('self.parser does not exist')
152 if args is None:
153 self.parsed_data = parser.parse_args()
154 else:
155 self.parse_data = parser.parse_args(args)
156
157 def convert_to_struct(self):
158 """self.parsed_data->self.config"""
159 if self.parsed_data is None:
160 raise ConfigLoaderError('self.parsed_data does not exist')
161 self.config = Struct(vars(self.parsed_data))
162
163
164 class ArgParseConfigLoader(CommandLineConfigLoader):
165
166 # arguments = [(('-f','--file'),dict(type=str,dest='file'))]
167 arguments = []
168
169 def __init__(self, *args, **kw):
170 """Create a config loader for use with argparse.
171
172 The args and kwargs arguments here are passed onto the constructor
173 of :class:`argparse.ArgumentParser`.
174 """
175 self.args = args
176 self.kw = kw
177 CommandLineConfigLoader.__init__(self)
178
179 def create_parser(self):
180 self.parser = argparse.ArgumentParser(*self.args, **self.kw)
181 self.add_arguments()
182
183 def add_arguments(self):
184 for argument in self.arguments:
185 self.parser.add_argument(*argument[0],**argument[1])
186
@@ -0,0 +1,184 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 A lightweight component system for IPython.
5
6 Authors:
7
8 * Brian Granger
9 * Fernando Perez
10 """
11
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2009 The IPython Development Team
14 #
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
18
19 #-----------------------------------------------------------------------------
20 # Imports
21 #-----------------------------------------------------------------------------
22
23
24 from weakref import WeakValueDictionary
25
26 from IPython.utils.traitlets import (
27 HasTraitlets, TraitletError, MetaHasTraitlets,
28 Int, Float, Str, Bool, Unicode
29 )
30
31
32 #-----------------------------------------------------------------------------
33 # Helper classes for Components
34 #-----------------------------------------------------------------------------
35
36
37 class Config(object):
38 pass
39
40
41 class MetaComponentTracker(type):
42 """A metaclass that tracks instances of Components and its subclasses."""
43
44 def __init__(cls, name, bases, d):
45 super(MetaComponentTracker, cls).__init__(name, bases, d)
46 cls.__instance_refs = WeakValueDictionary()
47 cls.__numcreated = 0
48
49 def __call__(cls, *args, **kw):
50 """Called when class is called (instantiated)!!!
51
52 Then a Component or subclass is instantiated, this is called and
53 the instance is saved in a WeakValueDictionary for tracking.
54 """
55
56 instance = super(MetaComponentTracker, cls).__call__(*args, **kw)
57 for c in cls.__mro__:
58 if issubclass(cls, c) and issubclass(c, Component):
59 c.__numcreated += 1
60 c.__instance_refs[c.__numcreated] = instance
61 return instance
62
63 def get_instances(cls):
64 """Get all instances of cls and its subclasses."""
65 return cls.__instance_refs.values()
66
67 def get_instances_by_name(cls, name):
68 """Get all instances of cls and its subclasses by name."""
69 return [i for i in cls.get_instances() if i.name == name]
70
71 def get_instances_by_subclass(cls, thisclass):
72 """Get all instances of cls that are instances of thisclass.
73
74 This includes all instances of subclasses of thisclass.
75 """
76 return [i for i in cls.get_instances() if isinstance(i, thisclass)]
77
78 def get_instances_by_class(cls, thisclass):
79 """Get all instances of cls that are instances of thisclass.
80
81 This exclused instances of thisclass subclasses.
82 """
83
84 return [i for i in cls.get_instances() if type(i) is thisclass]
85
86 def get_instances_by_condition(cls, call):
87 """Get all instances of cls, i such that call(i)==True."""
88 return [i for i in cls.get_instances() if call(i)]
89
90
91 class ComponentNameGenerator(object):
92 """A Singleton to generate unique component names."""
93
94 def __init__(self, prefix):
95 self.prefix = prefix
96 self.i = 0
97
98 def __call__(self):
99 count = self.i
100 self.i += 1
101 return "%s%s" % (self.prefix, count)
102
103
104 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
105
106
107 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
108 pass
109
110
111 #-----------------------------------------------------------------------------
112 # Component implementation
113 #-----------------------------------------------------------------------------
114
115
116 class Component(HasTraitlets):
117
118 __metaclass__ = MetaComponent
119
120 config = Config()
121
122 def __init__(self, parent, name=None, config=None):
123 """Create a component given a parent.
124
125 Parameters
126 ----------
127 parent : Component subclass
128 The parent in the component graph. The parent is used
129 to get the root of the component graph.
130 name : str
131 The unique name of the component. If empty, then a unique
132 one will be autogenerated.
133 config : Config
134 If this is empty, self.config = root.config, otherwise
135 self.config = config and root.config is ignored. This argument
136 should be used to pass the config to the root. Otherwise, it
137 can be used to *override* the inheritance of root.config. If a
138 caller wants to modify root.config (not override), the caller
139 should make a copy and change attributes and then pass the copy
140 to this argument. We might think about changing this behavior.
141 """
142 super(Component, self).__init__()
143 if name is None:
144 self._name = ComponentNameGenerator()
145 else:
146 self._name = name
147 self.parent = parent # this uses the property and handles None
148 if config is not None:
149 self.config = config
150 else:
151 if self.parent is not None:
152 self.config = self.parent.config
153
154 #-------------------------------------------------------------------------
155 # Properties
156 #-------------------------------------------------------------------------
157
158 def _set_name(self, name):
159 # This should use the ComponentNameGenerator to test for uniqueness
160 self._name = name
161
162 def _get_name(self):
163 return self._name
164
165 name = property(_get_name, _set_name)
166
167 def _set_parent(self, parent):
168 if parent is None:
169 self._parent = None
170 self._root = self
171 else:
172 assert isinstance(parent, Component), 'parent must be a component'
173 self._parent = parent
174 self._root = parent.root
175
176 def _get_parent(self):
177 return self._parent
178
179 parent = property(_get_parent, _set_parent)
180
181 @property
182 def root(self):
183 return self._root
184
@@ -0,0 +1,324 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 """
4 A lightweight Traits like module.
5
6 Authors:
7
8 * Brian Granger
9 * Enthought, Inc. Some of the code in this file comes from enthought.traits
10 and is licensed under the BSD license. Also, many of the ideas also come
11 from enthought.traits even though our implementation is very different.
12 """
13
14 #-----------------------------------------------------------------------------
15 # Copyright (C) 2008-2009 The IPython Development Team
16 #
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
19 #-----------------------------------------------------------------------------
20
21 #-----------------------------------------------------------------------------
22 # Imports
23 #-----------------------------------------------------------------------------
24
25 import inspect
26 import types
27 from types import InstanceType
28
29 #-----------------------------------------------------------------------------
30 # Basic classes
31 #-----------------------------------------------------------------------------
32
33
34 class NoDefaultSpecified ( object ): pass
35 NoDefaultSpecified = NoDefaultSpecified()
36
37
38 class Undefined ( object ): pass
39 Undefined = Undefined()
40
41
42 class TraitletError(Exception):
43 pass
44
45
46 #-----------------------------------------------------------------------------
47 # Utilities
48 #-----------------------------------------------------------------------------
49
50
51 def class_of ( object ):
52 """ Returns a string containing the class name of an object with the
53 correct indefinite article ('a' or 'an') preceding it (e.g., 'an Image',
54 'a PlotValue').
55 """
56 if isinstance( object, basestring ):
57 return add_article( object )
58
59 return add_article( object.__class__.__name__ )
60
61
62 def add_article ( name ):
63 """ Returns a string containing the correct indefinite article ('a' or 'an')
64 prefixed to the specified string.
65 """
66 if name[:1].lower() in 'aeiou':
67 return 'an ' + name
68
69 return 'a ' + name
70
71
72 def repr_type(obj):
73 """ Return a string representation of a value and its type for readable
74 error messages.
75 """
76 the_type = type(obj)
77 if the_type is InstanceType:
78 # Old-style class.
79 the_type = obj.__class__
80 msg = '%r %r' % (obj, the_type)
81 return msg
82
83
84 def parse_notifier_name(name):
85 if isinstance(name, str):
86 return [name]
87 elif name is None:
88 return ['anytraitlet']
89 elif isinstance(name, (list, tuple)):
90 for n in name:
91 assert isinstance(n, str), "names must be strings"
92 return name
93
94
95 #-----------------------------------------------------------------------------
96 # Base TraitletType for all traitlets
97 #-----------------------------------------------------------------------------
98
99
100 class TraitletType(object):
101
102 metadata = {}
103 default_value = None
104 info_text = 'any value'
105
106 # def __init__(self, name, default_value=NoDefaultSpecified, **metadata):
107 # self.name = name
108 def __init__(self, default_value=NoDefaultSpecified, **metadata):
109 if default_value is not NoDefaultSpecified:
110 self.default_value = default_value
111 self.metadata.update(metadata)
112
113 def __get__(self, inst, cls=None):
114 if inst is None:
115 return self
116 else:
117 return inst._traitlet_values.get(self.name, self.default_value)
118
119 def __set__(self, inst, value):
120 new_value = self._validate(inst, value)
121 old_value = self.__get__(inst)
122 if old_value != new_value:
123 inst._traitlet_values[self.name] = new_value
124 inst._notify(self.name, old_value, value)
125
126 def _validate(self, inst, value):
127 if hasattr(self, 'validate'):
128 return self.validate(inst, value)
129 elif hasattr(self, 'is_valid_for'):
130 valid = self.is_valid_for(value)
131 if valid:
132 return value
133 else:
134 raise TraitletError('invalid value for type: %r' % value)
135 elif hasattr(self, 'value_for'):
136 return self.value_for(value)
137 else:
138 return value
139
140 def info(self):
141 return self.info_text
142
143 def error(self, obj, value):
144 if obj is not None:
145 e = "The '%s' traitlet of %s instance must be %s, but a value of %s was specified." \
146 % (self.name, class_of(obj),
147 self.info(), repr_type(value))
148 else:
149 e = "The '%s' traitlet must be %s, but a value of %r was specified." \
150 % (self.name, self.info(), repr_type(value))
151 raise TraitletError(e)
152
153
154 #-----------------------------------------------------------------------------
155 # The HasTraitlets implementation
156 #-----------------------------------------------------------------------------
157
158
159 class MetaHasTraitlets(type):
160 """A metaclass for HasTraitlets.
161
162 This metaclass makes sure that any TraitletType class attributes are
163 instantiated and sets their name attribute.
164 """
165
166 def __new__(mcls, name, bases, classdict):
167 for k,v in classdict.iteritems():
168 if isinstance(v, TraitletType):
169 v.name = k
170 elif inspect.isclass(v):
171 if issubclass(v, TraitletType):
172 vinst = v()
173 vinst.name = k
174 classdict[k] = vinst
175 return super(MetaHasTraitlets, mcls).__new__(mcls, name, bases, classdict)
176
177
178 class HasTraitlets(object):
179
180 __metaclass__ = MetaHasTraitlets
181
182 def __init__(self):
183 self._traitlet_values = {}
184 self._notifiers = {}
185
186 def _notify(self, name, old_value, new_value):
187 callables = self._notifiers.get(name,[])
188 more_callables = self._notifiers.get('anytraitlet',[])
189 callables.extend(more_callables)
190 for c in callables:
191 # Traits catches and logs errors here. I allow them to raise
192 c(name, old_value, new_value)
193
194 def _add_notifiers(self, handler, name):
195 if not self._notifiers.has_key(name):
196 nlist = []
197 self._notifiers[name] = nlist
198 else:
199 nlist = self._notifiers[name]
200 if handler not in nlist:
201 nlist.append(handler)
202
203 def _remove_notifiers(self, handler, name):
204 if self._notifiers.has_key(name):
205 nlist = self._notifiers[name]
206 try:
207 index = nlist.index(handler)
208 except ValueError:
209 pass
210 else:
211 del nlist[index]
212
213 def on_traitlet_change(self, handler, name=None, remove=False):
214 if remove:
215 names = parse_notifier_name(name)
216 for n in names:
217 self._remove_notifiers(handler, n)
218 else:
219 names = parse_notifier_name(name)
220 for n in names:
221 self._add_notifiers(handler, n)
222
223
224 #-----------------------------------------------------------------------------
225 # Actual TraitletTypes implementations/subclasses
226 #-----------------------------------------------------------------------------
227
228
229 class Any(TraitletType):
230 default_value = None
231 info_text = 'any value'
232
233
234 class Int(TraitletType):
235
236 evaluate = int
237 default_value = 0
238 info_text = 'an integer'
239
240 def validate(self, obj, value):
241 if isinstance(value, int):
242 return value
243 self.error(obj, value)
244
245
246 class Long(TraitletType):
247
248 evaluate = long
249 default_value = 0L
250 info_text = 'a long'
251
252 def validate(self, obj, value):
253 if isinstance(value, long):
254 return value
255 if isinstance(value, int):
256 return long(value)
257 self.error(obj, value)
258
259
260 class Float(TraitletType):
261
262 evaluate = float
263 default_value = 0.0
264 info_text = 'a float'
265
266 def validate(self, obj, value):
267 if isinstance(value, float):
268 return value
269 if isinstance(value, int):
270 return float(value)
271 self.error(obj, value)
272
273
274 class Complex(TraitletType):
275
276 evaluate = complex
277 default_value = 0.0 + 0.0j
278 info_text = 'a complex number'
279
280 def validate(self, obj, value):
281 if isinstance(value, complex):
282 return value
283 if isinstance(value, (float, int)):
284 return complex(value)
285 self.error(obj, value)
286
287
288 class Str(TraitletType):
289
290 evaluate = lambda x: x
291 default_value = ''
292 info_text = 'a string'
293
294 def validate(self, obj, value):
295 if isinstance(value, str):
296 return value
297 self.error(obj, value)
298
299
300 class Unicode(TraitletType):
301
302 evaluate = unicode
303 default_value = u''
304 info_text = 'a unicode string'
305
306 def validate(self, obj, value):
307 if isinstance(value, unicode):
308 return value
309 if isinstance(value, str):
310 return unicode(value)
311 self.error(obj, value)
312
313
314 class Bool(TraitletType):
315
316 evaluate = bool
317 default_value = False
318 info_text = 'a boolean'
319
320 def validate(self, obj, value):
321 if isinstance(value, bool):
322 return value
323 self.error(obj, value)
324
@@ -1,4 +1,5 b''
1 # -*- coding: utf-8 -*-
1 #!/usr/bin/env python
2 # encoding: utf-8
2 """IPython Shell classes.
3 """IPython Shell classes.
3
4
4 Originally, this module was horribly complicated because of the need to
5 Originally, this module was horribly complicated because of the need to
General Comments 0
You need to be logged in to leave comments. Login now