1 #=======================================================================
3 __version__
= '''0.0.12'''
4 __sub_version__
= '''20040227011349'''
5 __copyright__
= '''(c) Alex A. Naanou 2003'''
8 #-----------------------------------------------------------------------
10 from __future__
import generators
14 #-----------------------------------------------------------------------
15 #-----------------------------------------------------ObjectWithAttrs---
16 # XXX might be good to rename this... and to add method interface
18 # TODO add update callbacks (__attr_update_callbacks__ = {<attr>: <meth>[, ...]})
19 # TODO add regex attribute naming.... (e.g. if <attr> is a regexp
20 # object use it to match the attr name...)
21 # NOTE: might be good to use a predicate...
22 class ObjectWithAttrs(object):
24 a generic object class with attribute creation an update automation.
26 this class checks attribute format.
28 # this defines an acl object to be used...
30 # this will restrict the attribute that can be created for this
31 # object to the ones mentioned in the list (DO NOT SET HERE!).
33 __attr_format__
= None
34 # this defines a tuple of attributes that must be defined on object
37 __essential_attrs__
= None
38 ## # this defines the callbacks for attr update... (RPC only at the
41 ## __attr_update_callbacks__ = {}
42 # if this is unset the checks will ignore all attrs that are not in
45 __strict_attr_format__
= True
46 # this will enable attribute type checking... (change for legacy
47 # support only... though might be good for a speedup)
48 __check_attr_types__
= True
49 # this defines the values that are to be treated as "no-vals" (e.g.
50 # ignored on type checks...)
51 __none_values__
= ('', None)
53 def __init__(self
, name
, attrs
={}):
55 create an object with attrs from a dict...
57 super(ObjectWithAttrs
, self
).__init
__(name
)
58 # check essential attrs....
59 ## if hasattr(self, '__essential_attrs__') and self.__essential_attrs__ != None and \
60 ## False in [ (type(attr) not in (str, unicode) and attr[0] or attr) in attrs \
61 ## for attr in self.__essential_attrs__ ]:
62 ## raise TypeError, 'essential attribute format mismatch.'
63 if hasattr(self
, '__essential_attrs__') and self
.__essential
_attrs
__ != None:
64 essential_attrs
= [ (type(attr
) not in (str, unicode) and attr
[0] or attr
) \
65 for attr
in self
.__essential
_attrs
__ ]
67 if False in [ attr
in attrs
and self
._isattrwritable
(attr
, \
69 strict
=hasattr(self
, '__strict_attr_format__') and self
.__strict
_attr
_format
__, \
70 format
=self
.__essential
_attrs
__) \
71 or (err
.append(attr
), False)[-1] \
72 for attr
in essential_attrs
]:
73 raise TypeError, 'essential attribute format mismatch in %s.' % (err
,)
75 # the isconsisten protocol...
76 def __isconsistent__(self
):
78 check object consistency...
80 return _checkarttrs(self
.__dict
__)
81 # NOTE: this is not very efficient if more than one attr is added
82 # in a loop (e.g. on object init)...
83 def _isattrwritable(self
, name
, value
, format
=None, strict
=True, message
=None, none_vals
=False):
85 this predicate will return true if the attribute is writable.
87 NOTE: this function impements error reporting a side-effect.
89 the error message[s] will be appended to the message argument (if present).
91 NOTE: the argument "message" must be of type list (if present).
92 NOTE: the "message" argument will be modified during this functions execution.
95 if hasattr(self
, '__attr_format__') and self
.__attr
_format
__ != None and len(self
.__attr
_format
__) != 0:
96 format
= self
.__attr
_format
__
101 # cache the complex format...
103 [ cformat
.update({e
[0]: e
[1:]}) for e
in format
if type(e
) not in (str, unicode) ]
104 ## # NOTE: both of the folowing are quite slow...
105 ## # cache the regex format...
107 ## # cache the predicate format...
109 if hasattr(self
, '__check_attr_types__') and self
.__check
_attr
_types
__:
110 if name
not in format
:
114 etype
= len(e
) > 0 and type(e
[0]) not in (str, unicode) and e
[0] or ''
116 if none_vals
and hasattr(self
, '__none_values__') and \
117 self
.__none
_values
__ and value
in self
.__none
_values
__:
121 if type(etype
) in (str, unicode) or issubclass(type(value
), etype
):
124 # we get here if issubclass failse when
125 # comparing types with a function/lambda
127 # check predicate....
128 # XXX (temporary??) the predicate can only be a
129 # function or a lambda...
130 ## if callable(etype):
131 if type(etype
) in (types
.LambdaType
, types
.FunctionType
):
135 except Exception, msg
:
137 # implement the side-effect...
139 if type(message
) != list:
140 raise TypeError, 'message paramiter must be of type "list".'
148 elif name
not in format
and name
not in cformat
:
151 # sloooow _checkarttrs version... (uses _isattrwritable in a
153 def _checkarttrs(self
, attrs
, errors
=None, none_vals
=False):
155 check if attribute dict given is compatible with format (see self._isattrwritable for details...)
157 NOTE: this function impements error reporting a side-effect.
159 all the the arguments that generate errors will be appended to the errors argument (if present).
161 NOTE: the argument "errors" must be of type list (if present).
162 NOTE: the "errors" argument will be modified during this functions execution.
164 # NOTE: this is very inefficient!!!
166 for name
, val
in attrs
.items():
167 if not self
._isattrwritable
(name
, val
, strict
=(hasattr(self
, '__strict_attr_format__') and self
.__strict
_attr
_format
__), none_vals
=none_vals
):
169 elif type(errors
) == list:
171 for name
, val
in attrs
.items():
172 if not self
._isattrwritable
(name
, val
, strict
=(hasattr(self
, '__strict_attr_format__') and self
.__strict
_attr
_format
__), none_vals
=none_vals
):
177 raise TypeError, 'errors paramiter must be of type "list".'
179 def update(self
, attrs
):
181 update object attributes.
183 NOTE: in strict mode this will disallow an update of non-existant
186 # XXX comment the folowing two lines out if __setattr__ is used...
188 if not self
._checkarttrs
(attrs
, errors
=err
):
189 raise AttributeError, 'can\'t update object %s, attribute format mismatch in %s.' % (self
, err
)
190 if self
.__acl
__ != None:
191 acl_setattr
= self
.__acl
__.setattr
192 for n
, v
in attrs
.items():
193 acl_setattr(self
, n
, v
)
195 for n
, v
in attrs
.items():
197 # the attribute interface....
199 def __setattr__(self
, name
, val
):
202 if not self
._isattrwritable
(name
, val
, strict
=(hasattr(self
, '__strict_attr_format__') and self
.__strict
_attr
_format
__)):
203 raise TypeError, 'attribute "%s" does not comply with the format of %s object.' % (name
, self
)
204 self
.__dict
__[name
] = val
205 def __delattr__(self
, name
):
208 if hasattr(self
, '__essential_attrs__') and self
.__essential
_attrs
__ != None:
209 if name
in self
.__essential
_attrs
__ or \
210 name
in [ attr
[0] for attr
in self
.__essential
_attrs
__ if type(attr
) not in (str, unicode) ]:
211 raise TypeError, 'can not remove essential attribute "%s" of %s object.' % (name
, self
)
212 del self
.__dict
__[name
]
214 # TODO make this prittier....
216 return cls
.getattributetextformat()
217 __help__
= classmethod(__help__
)
218 def getattributetextformat(cls
):
220 this will return a text definition of the attr format of obj.
222 def _format_str(struct
):
225 if type(elem
) is str:
229 elif type(elem
) in (tuple, list):
231 atype
= len(elem
) > 1 \
232 and (type(elem
[1]) not in (str, unicode) and elem
[1].__name
__ or False) \
234 doc
= (len(elem
) > 1 and type(elem
[1]) in (str, unicode) and elem
[1] or '') \
235 + (len(elem
) > 2 and type(elem
[2]) in (str, unicode) and elem
[2] or '')
236 res
+= ' %-30s : %s\n' % ('%s (%s)' % (name
, atype
), doc
)
239 res
= 'Essential Attributes:\n'
240 if hasattr(cls
, '__essential_attrs__') and cls
.__essential
_attrs
__ != None:
241 r
= _format_str(cls
.__essential
_attrs
__)
242 res
+= r
== '' and ' None\n' or r
247 res
+= 'Attribute Format:\n'
248 if hasattr(cls
, '__attr_format__') and cls
.__attr
_format
__ != None:
249 r
= _format_str(cls
.__attr
_format
__)
250 res
+= r
== '' and ' None\n' or r
254 getattributetextformat
= classmethod(getattributetextformat
)
255 def getattributeformat(cls
, name
=None):
258 def _format_dict(struct
, target_name
=None):
261 if type(elem
) is str:
265 elif type(elem
) in (tuple, list):
267 atype
= len(elem
) > 1 \
268 and (type(elem
[1]) not in (str, unicode) and elem
[1].__name
__ or False) \
270 doc
= (len(elem
) > 1 and type(elem
[1]) in (str, unicode) and elem
[1] or '') \
271 + (len(elem
) > 2 and type(elem
[2]) in (str, unicode) and elem
[2] or '')
272 # return type of requested attr...
273 if target_name
!= None and target_name
== name
:
274 return [name
, atype
, doc
]
275 res
[name
] = (atype
, doc
)
276 if target_name
!= None:
277 # if name was not found....
283 if hasattr(cls
, '__essential_attrs__') and cls
.__essential
_attrs
__ != None:
284 res
= _format_dict(cls
.__essential
_attrs
__, name
)
285 if res
== [] and hasattr(cls
, '__attr_format__') and cls
.__attr
_format
__ != None:
286 res
= _format_dict(cls
.__attr
_format
__, name
)
291 if hasattr(cls
, '__essential_attrs__') and cls
.__essential
_attrs
__ != None:
292 res
['essential'] = _format_dict(cls
.__essential
_attrs
__)
294 if hasattr(cls
, '__attr_format__') and cls
.__attr
_format
__ != None:
295 res
['format'] = _format_dict(cls
.__attr
_format
__)
297 getattributeformat
= classmethod(getattributeformat
)
301 #=======================================================================
302 # vim:set ts=4 sw=4 nowrap :