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 objspec
= self
.__list
_params
[param
]
196 cls
= self
.__get
_class
(objspec
)
198 listparam
.append(cls().from_dict(item
))
201 # A param that maps to a Model-object
203 if param
in self
.__object
_params
:
204 # "module.submodule:ClassName"
205 objectspec
= self
.__object
_params
[param
]
206 cls
= self
.__get
_class
(objectspec
)
207 return cls().from_dict(val
)
209 # Atoms and uninteresting hashes/dictionaries
213 '''Exports a model to a dictionary.
214 This simplifies serialization.'''
216 for param
in self
.__params
:
217 params
[param
] = self
.__param
_to
_dict
(param
)
220 def __param_to_dict(self
, param
):
221 item
= getattr(self
, param
)
222 return self
.__item
_to
_dict
(item
)
224 def __item_to_dict(self
, item
):
226 if is_atom(item
): return item
231 newlist
.append(self
.__item
_to
_dict
(i
))
236 for k
,v
in item
.iteritems():
237 newdict
[k
] = self
.__item
_to
_dict
(v
)
240 elif is_instance(item
):
241 return item
.to_dict()
244 raise NotImplementedError, 'Unknown type:' + str(type(item
))
251 Model
.__INDENT
__ += i
252 return '\t' * Model
.__INDENT
__
255 '''A convenient, recursively-defined stringification method.'''
258 io
.write(Model
.INDENT())
259 io
.write(self
.__class
__.__name
__ + '(')
263 for param
in self
.__params
:
264 if param
.startswith('_'): continue
267 inner
= Model
.INDENT() + param
+ " = "
268 value
= getattr(self
, param
)
270 if type(value
) == ListType
:
271 indent
= Model
.INDENT(1)
272 io
.write(inner
+ "[" + os
.linesep
)
283 io
.write(Model
.INDENT(-1))
291 io
.write(Model
.INDENT(-1))
294 value
= io
.getvalue()
298 #############################################################################
301 return type(item
) is DictType
303 return type(item
) is ListType
or type(item
) is TupleType
305 return(type(item
) in StringTypes
306 or type(item
) is BooleanType
307 or type(item
) is IntType
308 or type(item
) is LongType
309 or type(item
) is FloatType
310 or type(item
) is ComplexType
)
311 def is_model(item
): return issubclass(item
.__class
__, Model
)
312 def is_instance(item
):
313 return(is_model(item
)
314 or type(item
) is InstanceType
)