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
)
55 def create(self
,**kwargs
):
56 return self
.from_dict(kwargs
)
58 def get_param_names(self
):
59 return tuple(self
.__params
)
62 self
.notify_observers(*self
.get_param_names())
64 def clone(self
, *args
, **kwargs
):
65 return self
.__class
__(*args
, **kwargs
).from_dict(self
.to_dict())
67 def set_list_params(self
, **list_params
):
68 self
.__list
_params
.update(list_params
)
70 def set_object_params(self
, **obj_params
):
71 self
.__object
_params
.update(obj_params
)
73 def has_param(self
,param
):
74 return param
in self
.__params
76 def get_param(self
,param
):
77 return getattr(self
, param
)
79 def __getattr__(self
, param
):
80 '''Provides automatic get/set/add/append methods.'''
82 # Base case: we actually have this param
83 if param
in self
.__dict
__:
84 return getattr(self
, param
)
86 # Check for the translated variant of the param
87 realparam
= self
.__translate
(param
, sep
='')
88 if realparam
in self
.__dict
__:
89 return getattr(self
, realparam
)
91 if realparam
.startswith('get'):
92 param
= self
.__translate
(param
, 'get')
93 return lambda: getattr(self
, param
)
95 elif realparam
.startswith('set'):
96 param
= self
.__translate
(param
, 'set')
97 return lambda v
: self
.set_param(param
, v
,
100 elif (realparam
.startswith('add')
101 or realparam
.startswith('append')):
103 if realparam
.startswith('add'):
104 param
= self
.__translate
(realparam
, 'add')
106 param
= self
.__translate
(realparam
, 'append')
108 def array_append(*values
):
109 array
= getattr(self
, param
)
111 classnm
= self
.__class
__.__name
__
112 errmsg
= ("%s object has no array named '%s'"
114 raise AttributeError(errmsg
)
117 # Cache the function definition
118 setattr(self
, realparam
, array_append
)
121 errmsg
= ("%s object has no parameter '%s'"
122 % (self
.__class
__.__name
__, param
))
124 raise AttributeError(errmsg
)
126 def set_param(self
, param
, value
, notify
=True, check_params
=False):
127 '''Set param with optional notification and validity checks.'''
129 param
= param
.lower()
130 if check_params
and param
not in self
.__params
:
131 raise Exception("Parameter '%s' not available for %s"
132 % (param
, self
.__class
__.__name
__))
133 elif param
not in self
.__params
:
134 self
.__params
.append(param
)
136 setattr(self
, param
, value
)
137 if notify
: self
.notify_observers(param
)
139 def copy_params(self
, model
, params
=None):
141 params
= self
.get_param_names()
143 self
.set_param(param
, model
.get_param(param
))
145 def __translate(self
, param
, prefix
='', sep
='_'):
146 '''Translates an param name from the external name
147 used in methods to those used internally. The default
148 settings strip off '_' so that both get_foo() and getFoo()
149 are valid incantations.'''
150 return param
.lstrip(prefix
).lstrip(sep
).lower()
152 def save(self
, filename
):
156 print "Unable to save."
157 print "You do not have simplejson installed."
158 print "try: sudo apt-get install simplejson"
160 file = open(filename
, 'w')
161 simplejson
.dump(self
.to_dict(), file, indent
=4)
164 def load(self
, filename
):
168 print "Unable to load."
169 print "You do not have simplejson installed."
170 print "try: sudo apt-get install simplejson"
172 file = open(filename
, 'r')
173 dict = simplejson
.load(file)
177 def from_dict(self
, source_dict
):
178 '''Import a complex model from a dictionary. The import/export
179 is clued as to nested Model-objects by setting the
180 __list_params or __object_params object specifications.'''
182 if '__META__' in source_dict
:
183 metadict
= source_dict
['__META__']
184 for param
, clstr
in metadict
.iteritems():
185 cls
= Model
.str_to_class(clstr
)
186 self
.set_object_param(param
, cls
)
188 for param
, val
in source_dict
.iteritems():
191 self
.__param
_from
_dict
(param
, val
),
196 def __param_from_dict(self
,param
,val
):
197 # A list of Model-objects
199 if param
in self
.__list
_params
:
200 # A list of Model-derived objects
202 cls
= self
.__list
_params
[param
]
204 listparam
.append(cls().from_dict(item
))
207 # A param that maps to a Model-object
209 if param
in self
.__object
_params
:
210 # "module.submodule:ClassName"
211 cls
= self
.__object
_params
[param
]
212 return cls().from_dict(val
)
214 # Atoms and uninteresting hashes/dictionaries
218 '''Exports a model to a dictionary.
219 This simplifies serialization.'''
221 for param
in self
.__params
:
222 params
[param
] = self
.__param
_to
_dict
(param
)
223 value
= getattr(self
, param
)
224 if is_instance(value
):
225 clstr
= Model
.class_to_str(value
)
226 params
.setdefault('__META__', {})
227 params
['__META__'][param
] = clstr
230 def __param_to_dict(self
, param
):
231 item
= getattr(self
, param
)
232 return self
.__item
_to
_dict
(item
)
234 def __item_to_dict(self
, item
):
241 newlist
.append(self
.__item
_to
_dict
(i
))
246 for k
,v
in item
.iteritems():
247 newdict
[k
] = self
.__item
_to
_dict
(v
)
250 elif is_instance(item
):
251 return item
.to_dict()
254 raise NotImplementedError, 'Unknown type:' + str(type(item
))
262 Model
.__INDENT
__ += i
263 return '\t' * Model
.__INDENT
__
266 '''A convenient, recursively-defined stringification method.'''
268 # This avoid infinite recursion on cyclical structures
269 if self
in Model
.__STRSTACK
__:
272 Model
.__STRSTACK
__.append(self
)
276 if Model
.__PREINDENT
__:
277 io
.write(Model
.INDENT())
279 io
.write(self
.__class
__.__name
__ + '(')
283 for param
in self
.__params
:
284 if param
.startswith('_'): continue
287 inner
= Model
.INDENT() + param
+ " = "
288 value
= getattr(self
, param
)
290 if type(value
) == ListType
:
291 indent
= Model
.INDENT(1)
292 io
.write(inner
+ "[\n")
295 io
.write(str(val
)+'\n')
301 io
.write(Model
.INDENT(-1))
304 Model
.__PREINDENT
__ = False
308 Model
.__PREINDENT
__ = True
310 io
.write('\n' + Model
.INDENT(-1) + ')')
311 value
= io
.getvalue()
314 Model
.__STRSTACK
__.remove(self
)
318 def str_to_class(clstr
):
319 items
= clstr
.split('.')
321 classname
= items
[-1]
325 search
= imp
.find_module(mod
, path
)
327 module
= imp
.load_module(mod
, *search
)
328 if hasattr(module
, '__path__'):
329 path
= module
.__path
__
331 if search
and search
[0]:
334 return getattr(module
, classname
)
336 raise Exception("No class found for: %s" % clstr
)
339 def class_to_str(instance
):
340 modname
= instance
.__module
__
341 classname
= instance
.__class
__.__name
__
342 return '%s.%s' % (modname
, classname
)
345 #############################################################################
346 def is_model(item
): return issubclass(item
.__class
__, Model
)
348 return type(item
) is DictType
350 return type(item
) is ListType
or type(item
) is TupleType
352 return(type(item
) in StringTypes
353 or type(item
) is BooleanType
354 or type(item
) is IntType
355 or type(item
) is LongType
356 or type(item
) is FloatType
357 or type(item
) is ComplexType
)
358 def is_instance(item
):
359 return(is_model(item
)
360 or type(item
) is InstanceType
)