*** empty log message ***
[pli.git] / pli / objutils.py
blobc4c3019d8465e90efcf30eaf4c31ddcbb6c4a0af
1 #=======================================================================
3 __version__ = '''0.0.22'''
4 __sub_version__ = '''20040729152425'''
5 __copyright__ = '''(c) Alex A. Naanou 2003'''
8 #-----------------------------------------------------------------------
10 from __future__ import generators
11 import types
12 import new
15 #-----------------------------------------------------------------------
16 #-------------------------------------------------classinstancemethod---
17 ##!! revise !!##
18 class classinstancemethod(object):
19 '''
21 a universal class/instance/direct method constructor/dispatcher.
22 '''
23 def __init__(self, func, inst_func=None, direct_func=None):
24 '''
25 '''
26 self.func = func
27 self.inst_func = inst_func != None and inst_func or func
28 self.direct_func = direct_func != None and direct_func or func
29 def __call__(self, *p, **n):
30 '''
31 '''
32 # we are called directly...
33 return self.direct_func(*p, **n)
34 def __get__(self, obj, cls=None):
35 '''
36 '''
37 if obj == None:
38 # we are called from a class...
39 return new.instancemethod(self.func, cls, type(cls))
40 else:
41 # we are called from an instance...
42 return new.instancemethod(self.inst_func, obj, cls)
46 #-----------------------------------------------------------------------
47 #-----------------------------------------------------ObjectWithAttrs---
48 # XXX might be good to rename this... and to add method interface
49 # checking support...
50 # TODO add update callbacks (__attr_update_callbacks__ = {<attr>: <meth>[, ...]})
51 # TODO add regex attribute naming.... (e.g. if <attr> is a regexp
52 # object use it to match the attr name...)
53 # NOTE: might be good to use a predicate...
54 class ObjectWithAttrs(object):
55 '''
56 a generic object class with attribute creation an update automation.
58 this class checks attribute format.
59 '''
60 # this defines an acl object to be used...
61 __acl__ = None
62 # this will restrict the attribute that can be created for this
63 # object to the ones mentioned in the list (DO NOT SET HERE!).
64 # value: tuple
65 __attr_format__ = None
66 # this defines a tuple of attributes that must be defined on object
67 # init.
68 # value: tuple
69 __essential_attrs__ = None
70 ## # this defines the callbacks for attr update... (RPC only at the
71 ## # moment...)
72 ## # value: dict
73 ## __attr_update_callbacks__ = {}
74 # if this is unset the checks will ignore all attrs that are not in
75 # format...
76 # TODO revise....
77 __strict_attr_format__ = True
78 # this will enable attribute type checking... (change for legacy
79 # support only... though might be good for a speedup)
80 __check_attr_types__ = True
81 # this defines the values that are to be treated as "no-vals" (e.g.
82 # ignored on type checks...)
83 __none_values__ = ('', None)
85 def __init__(self, name, attrs={}):
86 '''
87 create an object with attrs from a dict...
88 '''
89 super(ObjectWithAttrs, self).__init__(name)
90 # check essential attrs....
91 if hasattr(self, '__essential_attrs__') and self.__essential_attrs__ != None:
92 essential_attrs = [ (type(attr) not in (str, unicode) and attr[0] or attr) \
93 for attr in self.__essential_attrs__ ]
94 err = []
95 if False in [ attr in attrs and self._isattrwritable(attr, \
96 attrs[attr], \
97 strict=hasattr(self, '__strict_attr_format__') and self.__strict_attr_format__, \
98 format=self.__essential_attrs__) \
99 or (err.append(attr), False)[-1] \
100 for attr in essential_attrs ]:
101 raise TypeError, 'essential attribute format mismatch in %s.' % (err,)
102 self.update(attrs)
103 # the isconsisten protocol...
104 def __isconsistent__(self):
106 check object consistency...
108 return _checkarttrs(self.__dict__)
109 # NOTE: this is not very efficient if more than one attr is added
110 # in a loop (e.g. on object init)...
111 def _isattrwritable(self, name, value, format=None, strict=True, message=None, none_vals=False):
113 this predicate will return true if the attribute is writable.
115 NOTE: this function impements error reporting a side-effect.
117 the error message[s] will be appended to the message argument (if present).
119 NOTE: the argument "message" must be of type list (if present).
120 NOTE: the "message" argument will be modified during this functions execution.
122 if format == None:
123 if hasattr(self, '__attr_format__') and self.__attr_format__ != None and len(self.__attr_format__) != 0:
124 format = self.__attr_format__
125 ##!!!
126 none_vals = True
127 else:
128 return True
129 # cache the complex format...
130 cformat = {}
131 [ cformat.update({e[0]: e[1:]}) for e in format if type(e) not in (str, unicode) ]
132 ## # NOTE: both of the folowing are quite slow...
133 ## # cache the regex format...
134 ## rformat = []
135 ## # cache the predicate format...
136 pformat = []
137 if hasattr(self, '__check_attr_types__') and self.__check_attr_types__:
138 if name not in format:
139 if name in cformat:
140 # get data...
141 e = cformat[name]
142 etype = len(e) > 0 and type(e[0]) not in (str, unicode) and e[0] or ''
143 # check none_vals
144 if none_vals and hasattr(self, '__none_values__') and \
145 self.__none_values__ and value in self.__none_values__:
146 return True
147 # check type...
148 try:
149 if type(etype) in (str, unicode) or issubclass(type(value), etype):
150 return True
151 except TypeError:
152 # we get here if issubclass failse when
153 # comparing types with a function/lambda
154 pass
155 # check predicate....
156 # XXX (temporary??) the predicate can only be a
157 # function or a lambda...
158 ## if callable(etype):
159 if type(etype) in (types.LambdaType, types.FunctionType):
160 try:
161 if etype(value):
162 return True
163 except Exception, msg:
164 print '>>>', msg
165 # implement the side-effect...
166 if message != None:
167 if type(message) != list:
168 raise TypeError, 'message paramiter must be of type "list".'
169 message += [msg]
170 except:
171 pass
172 elif not strict:
173 return True
174 return False
175 # legacy only....
176 elif name not in format and name not in cformat:
177 return False
178 return True
179 # sloooow _checkarttrs version... (uses _isattrwritable in a
180 # loop...)
181 def _checkarttrs(self, attrs, errors=None, none_vals=False):
183 check if attribute dict given is compatible with format (see self._isattrwritable for details...)
185 NOTE: this function impements error reporting a side-effect.
187 all the the arguments that generate errors will be appended to the errors argument (if present).
189 NOTE: the argument "errors" must be of type list (if present).
190 NOTE: the "errors" argument will be modified during this functions execution.
192 # NOTE: this is very inefficient!!!
193 if errors == None:
194 for name, val in attrs.items():
195 if not self._isattrwritable(name, val, strict=(hasattr(self, '__strict_attr_format__') and self.__strict_attr_format__), none_vals=none_vals):
196 return False
197 elif type(errors) == list:
198 errors[:] = []
199 for name, val in attrs.items():
200 if not self._isattrwritable(name, val, strict=(hasattr(self, '__strict_attr_format__') and self.__strict_attr_format__), none_vals=none_vals):
201 errors += [name]
202 if len(errors) > 0:
203 return False
204 else:
205 raise TypeError, 'errors paramiter must be of type "list".'
206 return True
207 def update(self, attrs):
209 update object attributes.
211 NOTE: in strict mode this will disallow an update of non-existant
212 attributes.
214 # XXX comment the folowing two lines out if __setattr__ is used...
215 err = []
216 if not self._checkarttrs(attrs, errors=err):
217 raise AttributeError, 'can\'t update object %s, attribute format mismatch in %s.' % (self, err)
218 if self.__acl__ != None:
219 acl_setattr = self.__acl__.setattr
220 for n, v in attrs.items():
221 acl_setattr(self, n, v)
222 else:
223 for n, v in attrs.items():
224 setattr(self, n, v)
225 # the attribute interface....
226 ##!! test !!##
227 def __setattr__(self, name, val):
230 if not self._isattrwritable(name, val, strict=(hasattr(self, '__strict_attr_format__') and self.__strict_attr_format__)):
231 raise TypeError, 'attribute "%s" does not comply with the format of %s object.' % (name, self)
232 ## self.__dict__[name] = val
233 super(ObjectWithAttrs, self).__setattr__(name, val)
234 def __delattr__(self, name):
237 if hasattr(self, '__essential_attrs__') and self.__essential_attrs__ != None:
238 if name in self.__essential_attrs__ or \
239 name in [ attr[0] for attr in self.__essential_attrs__ if type(attr) not in (str, unicode) ]:
240 raise TypeError, 'can not remove essential attribute "%s" of %s object.' % (name, self)
241 del self.__dict__[name]
242 # introspection...
243 # TODO make this prittier....
244 def __help__(cls):
245 return cls.getattributetextformat()
246 __help__ = classmethod(__help__)
247 def getattributetextformat(cls):
249 this will return a text definition of the attr format of obj.
251 def _format_str(struct):
252 res = ''
253 for elem in struct:
254 if type(elem) is str:
255 name = elem
256 atype = 'any type'
257 doc = ''
258 elif type(elem) in (tuple, list):
259 name = elem[0]
260 atype = len(elem) > 1 \
261 and (type(elem[1]) not in (str, unicode) and elem[1].__name__ or False) \
262 or 'any type'
263 doc = (len(elem) > 1 and type(elem[1]) in (str, unicode) and elem[1] or '') \
264 + (len(elem) > 2 and type(elem[2]) in (str, unicode) and elem[2] or '')
265 res += ' %-30s : %s\n' % ('%s (%s)' % (name, atype), doc)
266 return res
267 # essential attrs:
268 res = 'Essential Attributes:\n'
269 if hasattr(cls, '__essential_attrs__') and cls.__essential_attrs__ != None:
270 r = _format_str(cls.__essential_attrs__)
271 res += r == '' and ' None\n' or r
272 else:
273 res += ' None\n'
274 res += '\n'
275 # attr format:
276 res += 'Attribute Format:\n'
277 if hasattr(cls, '__attr_format__') and cls.__attr_format__ != None:
278 r = _format_str(cls.__attr_format__)
279 res += r == '' and ' None\n' or r
280 else:
281 res += ' None\n'
282 return res + '\n'
283 getattributetextformat = classmethod(getattributetextformat)
284 def getattributeformat(cls, name=None):
287 def _format_dict(struct, target_name=None):
288 res = {}
289 for elem in struct:
290 if type(elem) is str:
291 name = elem
292 atype = 'any'
293 doc = ''
294 elif type(elem) in (tuple, list):
295 name = elem[0]
296 atype = len(elem) > 1 \
297 and (type(elem[1]) not in (str, unicode) and elem[1].__name__ or False) \
298 or 'any'
299 doc = (len(elem) > 1 and type(elem[1]) in (str, unicode) and elem[1] or '') \
300 + (len(elem) > 2 and type(elem[2]) in (str, unicode) and elem[2] or '')
301 # return type of requested attr...
302 if target_name != None and target_name == name:
303 return [name, atype, doc]
304 res[name] = (atype, doc)
305 if target_name != None:
306 # if name was not found....
307 return []
308 return res
309 # requested name...
310 if name != None:
311 res = []
312 if hasattr(cls, '__essential_attrs__') and cls.__essential_attrs__ != None:
313 res = _format_dict(cls.__essential_attrs__, name)
314 if res == [] and hasattr(cls, '__attr_format__') and cls.__attr_format__ != None:
315 res = _format_dict(cls.__attr_format__, name)
316 return res
317 # empty res...
318 res = {}
319 # essential attrs:
320 if hasattr(cls, '__essential_attrs__') and cls.__essential_attrs__ != None:
321 res['essential'] = _format_dict(cls.__essential_attrs__)
322 # attr format:
323 if hasattr(cls, '__attr_format__') and cls.__attr_format__ != None:
324 res['format'] = _format_dict(cls.__attr_format__)
325 return res
326 getattributeformat = classmethod(getattributeformat)
330 #=======================================================================
331 # vim:set ts=4 sw=4 nowrap :