4 from cStringIO
import StringIO
5 from types
import DictType
6 from types
import ListType
7 from types
import TupleType
8 from types
import StringTypes
9 from types
import BooleanType
10 from types
import IntType
11 from types
import LongType
12 from types
import FloatType
13 from types
import ComplexType
14 from types
import InstanceType
16 class Observable(object):
17 '''Handles subject/observer notifications.'''
18 def __init__(self
,*args
,**kwargs
):
23 def set_notify(self
, notify
=True):
24 self
.__notify
= notify
25 def add_observer(self
, observer
):
26 if observer
not in self
.__observers
:
27 self
.__observers
.append(observer
)
28 def remove_observer(self
, observer
):
29 if observer
in self
.__observers
:
30 self
.__observers
.remove(observer
)
31 def notify_observers(self
, *param
):
32 if not self
.__notify
: return
33 for observer
in self
.__observers
:
34 observer
.notify(*param
)
36 class Model(Observable
):
37 '''Creates a generic model object with params specified
38 as a name:value dictionary.
40 get_name() and set_name(value) are created automatically
41 for any of the parameters specified in the **kwargs'''
43 def __init__(self
, *args
, **kwargs
):
44 Observable
.__init
__(self
)
46 self
.__list
_params
= {}
47 self
.__object
_params
= {}
48 # For meta-programmability
49 self
.from_dict(kwargs
)
51 def get_param_names(self
):
52 return tuple(self
.__params
)
55 self
.notify_observers(*self
.get_param_names())
57 def create(self
,**kwargs
):
58 return self
.from_dict(kwargs
)
60 def clone(self
, *args
, **kwargs
):
61 return self
.__class
__(*args
, **kwargs
).from_dict(self
.to_dict())
63 def set_list_params(self
, **list_params
):
64 self
.__list
_params
.update(list_params
)
66 def set_object_params(self
, **obj_params
):
67 self
.__object
_params
.update(obj_params
)
69 def has_param(self
,param
):
70 return param
in self
.__params
72 def get_param(self
,param
):
73 return getattr(self
, param
)
75 def __getattr__(self
, param
):
76 '''Provides automatic get/set/add/append methods.'''
78 # Base case: we actually have this param
79 if param
in self
.__dict
__:
80 return getattr(self
, param
)
82 # Check for the translated variant of the param
83 realparam
= self
.__translate
(param
, sep
='')
84 if realparam
in self
.__dict
__:
85 return getattr(self
, realparam
)
87 if realparam
.startswith('get'):
88 param
= self
.__translate
(param
, 'get')
89 return lambda: getattr(self
, param
)
91 elif realparam
.startswith('set'):
92 param
= self
.__translate
(param
, 'set')
93 return lambda v
: self
.set_param(param
, v
,
96 elif realparam
.startswith('add'):
97 self
.__array
= self
.__translate
(param
, 'add')
100 elif realparam
.startswith('append'):
101 self
.__array
= self
.__translate
(param
, 'append')
104 errmsg
= ("%s object has no parameter '%s'"
105 % (self
.__class
__.__name
__, param
))
107 raise AttributeError(errmsg
)
109 def set_param(self
, param
, value
, notify
=True, check_params
=False):
110 '''Set param with optional notification and validity checks.'''
112 param
= param
.lower()
113 if check_params
and param
not in self
.__params
:
114 raise Exception("Parameter '%s' not available for %s"
115 % (param
, self
.__class
__.__name
__))
116 elif param
not in self
.__params
:
117 self
.__params
.append(param
)
119 setattr(self
, param
, value
)
120 if notify
: self
.notify_observers(param
)
122 def copy_params(self
, model
, params
=None):
124 params
= self
.get_param_names()
126 self
.set_param(param
, model
.get_param(param
))
128 def __append(self
, *values
):
129 '''Appends an arbitrary number of values to
130 an array atribute.'''
131 array
= getattr(self
, self
.__array
)
133 errmsg
= "%s object has no parameter '%s'" \
134 %( self
.__class
__.__name
__, self
.__array
)
135 raise AttributeError(errmsg
)
139 def __translate(self
, param
, prefix
='', sep
='_'):
140 '''Translates an param name from the external name
141 used in methods to those used internally. The default
142 settings strip off '_' so that both get_foo() and getFoo()
143 are valid incantations.'''
144 return param
.lstrip(prefix
).lstrip(sep
).lower()
146 def __get_class(self
, objspec
):
147 '''Loads a class from a module and returns the class.'''
149 # str("module.submodule:ClassName")
150 ( modname
, classname
) = objspec
.split(':')
151 modfile
= imp
.find_module(modname
)
152 module
= imp
.load_module(modname
,
153 modfile
[0], modfile
[1], modfile
[2])
155 if classname
in module
.__dict
__:
156 cls
= module
.__dict
__[classname
]
159 warning
= ('WARNING: %s not found in %s\n'
160 %(modname
, classname
))
161 sys
.stderr
.write(warning
)
166 def save(self
, filename
):
168 file = open(filename
, 'w')
169 simplejson
.dump(self
.to_dict(), file, indent
=4)
172 def load(self
, filename
):
174 file = open(filename
, 'r')
175 dict = simplejson
.load(file)
179 def from_dict(self
, source_dict
):
180 '''Import a complex model from a dictionary. The import/export
181 is clued as to nested Model-objects by setting the
182 __list_params or __object_params object specifications.'''
183 for param
,val
in source_dict
.iteritems():
184 self
.set_param(param
, self
.__param
_from
_dict
(param
, val
),
188 def __param_from_dict(self
,param
,val
):
190 # A list of Model-objects
192 if param
in self
.__list
_params
:
193 # A list of Model-derived objects
195 cls
= self
.__list
_params
[param
]
197 listparam
.append(cls().from_dict(item
))
200 # A param that maps to a Model-object
202 if param
in self
.__object
_params
:
203 # "module.submodule:ClassName"
204 cls
= self
.__object
_params
[param
]
205 return cls().from_dict(val
)
207 # Atoms and uninteresting hashes/dictionaries
211 '''Exports a model to a dictionary.
212 This simplifies serialization.'''
214 for param
in self
.__params
:
215 params
[param
] = self
.__param
_to
_dict
(param
)
218 def __param_to_dict(self
, param
):
219 item
= getattr(self
, param
)
220 return self
.__item
_to
_dict
(item
)
222 def __item_to_dict(self
, item
):
224 if is_atom(item
): return item
229 newlist
.append(self
.__item
_to
_dict
(i
))
234 for k
,v
in item
.iteritems():
235 newdict
[k
] = self
.__item
_to
_dict
(v
)
238 elif is_instance(item
):
239 return item
.to_dict()
242 raise NotImplementedError, 'Unknown type:' + str(type(item
))
249 Model
.__INDENT
__ += i
250 return '\t' * Model
.__INDENT
__
253 '''A convenient, recursively-defined stringification method.'''
256 io
.write(Model
.INDENT())
257 io
.write(self
.__class
__.__name
__ + '(')
261 for param
in self
.__params
:
262 if param
.startswith('_'): continue
265 inner
= Model
.INDENT() + param
+ " = "
266 value
= getattr(self
, param
)
268 if type(value
) == ListType
:
269 indent
= Model
.INDENT(1)
270 io
.write(inner
+ "[\n")
273 io
.write(str(val
)+'\n')
279 io
.write(Model
.INDENT(-1))
286 io
.write('\n' + Model
.INDENT(-1) + ')')
287 value
= io
.getvalue()
291 #############################################################################
293 def is_model(item
): return issubclass(item
.__class
__, Model
)
295 return type(item
) is DictType
297 return type(item
) is ListType
or type(item
) is TupleType
299 return(type(item
) in StringTypes
300 or type(item
) is BooleanType
301 or type(item
) is IntType
302 or type(item
) is LongType
303 or type(item
) is FloatType
304 or type(item
) is ComplexType
)
305 def is_instance(item
):
306 return(is_model(item
)
307 or type(item
) is InstanceType
)