1 #=======================================================================
3 __version__
= '''0.0.15'''
4 __sub_version__
= '''20040724013316'''
5 __copyright__
= '''(c) Alex A. Naanou 2003'''
8 #-----------------------------------------------------------------------
10 import pli
.logictypes
as logictypes
13 #-----------------------------------------------------------------------
17 # TODO write more docs...
18 # TODO add support for pure dict interface definitions...
20 #------------------------------------------------------InterfaceError---
21 class InterfaceError(Exception):
28 #-----------------------------------------------------------------------
29 #---------------------------------------------------------isessential---
30 def isessential(obj
, name
, interface
=None):
33 if interface
== None \
34 and (not hasattr(obj
, '__implemments__') or obj
.__implemments
__ is None):
37 format
= (interface
!= None and interface
or obj
.__implemments
__)
38 format
= type(format
) is tuple \
39 and logictypes
.DictUnion(*[ f
.__format
__ for f
in format
]) \
42 return name
in format
and format
[name
].get('essential', False) or '*' not in format
45 #----------------------------------------------------------iswritable---
46 def iswritable(obj
, name
, interface
=None):
49 if interface
== None \
50 and (not hasattr(obj
, '__implemments__') or obj
.__implemments
__ is None):
53 format
= (interface
!= None and interface
or obj
.__implemments
__)
54 format
= type(format
) is tuple \
55 and logictypes
.DictUnion(*[ f
.__format
__ for f
in format
]) \
58 return name
in format
and format
[name
].get('writable', True) or \
59 ('*' in format
and format
['*'].get('writable', True))
62 #----------------------------------------------------------isreadable---
63 def isreadable(obj
, name
, interface
=None):
66 if interface
== None \
67 and (not hasattr(obj
, '__implemments__') or obj
.__implemments
__ is None):
70 format
= (interface
!= None and interface
or obj
.__implemments
__)
71 format
= type(format
) is tuple \
72 and logictypes
.DictUnion(*[ f
.__format
__ for f
in format
]) \
75 return name
in format
and format
[name
].get('readable', True) or \
76 ('*' in format
and format
['*'].get('readable', True))
79 #--------------------------------------------------------iscompatible---
80 def iscompatible(obj
, name
, value
, interface
=None):
83 if interface
== None \
84 and (not hasattr(obj
, '__implemments__') or obj
.__implemments
__ is None):
87 interface
= (interface
!= None and interface
or obj
.__implemments
__)
90 checkattr(interface
, name
, value
)
91 except InterfaceError
:
96 #-----------------------------------------------------------checkattr---
97 def checkattr(interface
, name
, value
):
100 format
= type(interface
) is tuple \
101 and logictypes
.DictUnion(*[ f
.__format
__ for f
in interface
]) \
102 or interface
.__format
__
103 # resolve the format source...
104 if name
not in format
:
106 attr_format
= format
['*']
108 raise InterfaceError
, 'attribute "%s" is not defined in inteface %s' % (name
, interface
)
110 attr_format
= format
[name
].copy()
113 if 'LIKE' in attr_format
:
114 if type(attr_format
['LIKE']) is str:
115 ext_format
= format
[attr_format
['LIKE']].copy()
116 elif type(attr_format
['LIKE']) is dict:
117 ext_format
= attr_format
['LIKE'].copy()
119 raise TypeError, 'the argument of "LIKE" attribute option must '\
120 'either be of type str or dict (got: %s).' % type(attr_format
['LIKE'])
121 ext_format
.update(attr_format
)
122 attr_format
= ext_format
123 # static attribute options:
125 if 'type' in attr_format
and not issubclass(type(value
), attr_format
['type']):
126 raise InterfaceError
, 'attribute type mismatch. "%s" attribute ' \
127 'must be of type %s (got: %s).' % (name
, type(value
), attr_format
['type'])
129 if 'predicate' in attr_format
and not attr_format
['predicate'](value
):
130 raise InterfaceError
, 'predicate failed for "%s" attribute.' % name
134 #-----------------------------------------------------checkessentials---
135 def checkessentials(interface
, obj
):
138 format
= type(interface
) is tuple \
139 and logictypes
.DictUnion(*[ f
.__format
__ for f
in interface
]) \
140 or interface
.__format
__
143 if format
[attr
].get('essential', False) and attr
not in obj_attrs
:
144 raise InterfaceError
, 'essential attribute "%s" missing.' % attr
148 #---------------------------------------------------------checkobject---
149 def checkobject(interface
, obj
):
151 this will test the object compatibility yith the interface.
153 format
= type(interface
) is tuple \
154 and logictypes
.DictUnion(*[ f
.__format
__ for f
in interface
]) \
155 or interface
.__format
__
157 obj_data
= vars(obj
).copy()
158 for name
in obj_attrs
:
159 val
= obj_data
.pop(name
)
160 if not interface
.checkattr(name
, val
):
164 if len(obj_data
) > 0 and '*' not in format
:
167 raise InterfaceError
, 'excess attributes (%s).' % obj_data
.keys()
168 # check if any essentials are left out...
169 if not interface
.checkessentials(obj
):
176 #-----------------------------------------------------------------------
177 #-----------------------------------------------------------getdocstr---
178 # NOTE: in the following two functions the obj can either be an
179 # interface or an object with interface...
180 def getdocstr(obj
, attr
=None, interface
=None):
187 #----------------------------------------------------------getdocdict---
188 def getdocdict(obj
, attr
=None, interface
=None):
195 #-----------------------------------------------------------------------
196 #-----------------------------------------------------------Interface---
197 # TODO default templates...
198 # TODO attribute name wildcards...
199 # TODO regexp name matching...
201 # TODO interface mathematics... (inheritance, combinations, ...)
203 # TODO add tests and paranoya!!!
205 # Q: does this need to be a class??? ....it might be good to create an
206 # interface object factory...
208 class Interface(object):
210 XXX write more docs...
212 the attribute definition format is as follows:
216 <opt-name>: <opt-value>
224 type - value type or superclass.
225 default - this is the default value of the option.
226 predicate - this will get the option value as argument and
227 test its compliance (if the will return False
228 InterfaceError will be raised).
229 essential - this if true will guarantee the options'
230 existance in the created object.
232 doc - this is the attr documentation
234 handler - this is the alternative attribute handler.
235 this will take the option value and its old value
236 as arguments and its' return will replace the
239 readable - this if False will prevent the attr from being
241 writable - this if False will prevent the attr from being
243 deleteable - this if False will prevent the attr from being
247 LIKE - this states the attribute template. if this is
248 given all options not declared for this attribute
249 will be taken from the template.
250 the value is either the name of the attribute in
251 the current interface or a dict of the interface
252 attr definition format.
254 special attribute names:
255 '*' - this matches any attribute not explicitly given.
262 def __init__(*p
, **n
):
265 raise TypeError, 'can\'t create an interface instance.'
266 checkattr
= classmethod(checkattr
)
267 checkessentials
= classmethod(checkessentials
)
268 checkobject
= classmethod(checkobject
)
271 #-------------------------------------------------ObjectWithInterface---
272 # TODO add tests and paranoya!!!
273 class ObjectWithInterface(object):
275 this is an object with interface support.
277 # this defines the objects' interface.
278 # NOTE: if this is None interface support will be disabled.
279 __implemments__
= None
281 def __new__(cls
, *p
, **n
):
284 obj
= object.__new
__(cls
, *p
, **n
)
285 interface
= obj
.__implemments
__
286 if interface
!= None:
287 obj
.__dict
__.update(dict([ (n
, v
['default']) \
289 in type(interface
) is tuple \
290 and logictypes
.DictUnion(*[ i
.__format
__ for i
in interface
]).iteritems() \
291 or interface
.__format
__.iteritems() \
292 if 'default' in v
and n
!= '*' ]))
294 def __getattribute__(self
, name
):
297 if name
== '__implemments__' \
298 or object.__getattribute
__(self
, '__implemments__') == None \
299 or isreadable(self
, name
):
300 return super(ObjectWithInterface
, self
).__getattribute
__(name
)
301 raise InterfaceError
, 'can\'t read attribute "%s".' % name
302 def __setattr__(self
, name
, value
):
305 if object.__getattribute
__(self
, '__implemments__') == None \
306 or (iswritable(self
, name
) and iscompatible(self
, name
, value
)):
307 return super(ObjectWithInterface
, self
).__setattr
__(name
, value
)
308 raise InterfaceError
, 'can\'t write value "%s" to attribute "%s".' % (value
, name
)
309 def __delattr__(self
, name
):
312 if object.__getattribute
__(self
, '__implemments__') != None \
313 and isessential(self
, name
):
314 raise InterfaceError
, 'can\'t delete an essential attribute "%s".' % name
315 super(ObjectWithInterface
, self
).__delattr
__(name
)
318 #------------------------------------------------------InterfaceProxy---
319 # TODO add tests and paranoya!!!
320 class InterfaceProxy(object):
323 __implemments__
= None
327 def __init__(self
, source
):
330 self
.__source
__ = source
331 def __getattr__(self
, name
):
334 if self
.__implemments
__ == None \
335 or name
== '__implemments__' or isreadable(self
, name
):
336 return getattr(self
.__source
__, name
)
337 raise InterfaceError
, 'can\'t read attribute "%s".' % name
338 def __setattr__(self
, name
, value
):
341 if name
in ('__source__', '__implemments__'):
342 return super(InterfaceProxy
, self
).__setattr
__(name
, value
)
343 if self
.__implemments
__ == None \
344 or (iswritable(self
, name
) and iscompatible(self
, name
, value
)):
345 return setattr(self
.__source
__, name
, value
)
346 raise InterfaceError
, 'can\'t write value "%s" to attribute "%s".' % (value
, name
)
347 def __delattr__(self
, name
):
350 if self
.__implemments
__ != None and isessential(self
, name
):
351 raise InterfaceError
, 'can\'t delete an essential attribute "%s".' % name
352 delattr(self
.__source
__, name
)
356 #=======================================================================
357 # vim:set ts=4 sw=4 nowrap :