*** empty log message ***
[pli.git] / pli / interface.py
blob47ace307906c172edee9fdc21f27c0a2d3aaca65
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 #-----------------------------------------------------------------------
15 ##!!! REVISE !!!##
17 # TODO write more docs...
18 # TODO add support for pure dict interface definitions...
20 #------------------------------------------------------InterfaceError---
21 class InterfaceError(Exception):
22 '''
23 '''
24 pass
28 #-----------------------------------------------------------------------
29 #---------------------------------------------------------isessential---
30 def isessential(obj, name, interface=None):
31 '''
32 '''
33 if interface == None \
34 and (not hasattr(obj, '__implemments__') or obj.__implemments__ is None):
35 return False
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 ]) \
40 or format.__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):
47 '''
48 '''
49 if interface == None \
50 and (not hasattr(obj, '__implemments__') or obj.__implemments__ is None):
51 return True
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 ]) \
56 or format.__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):
64 '''
65 '''
66 if interface == None \
67 and (not hasattr(obj, '__implemments__') or obj.__implemments__ is None):
68 return True
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 ]) \
73 or format.__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):
81 '''
82 '''
83 if interface == None \
84 and (not hasattr(obj, '__implemments__') or obj.__implemments__ is None):
85 return True
87 interface = (interface != None and interface or obj.__implemments__)
89 try:
90 checkattr(interface, name, value)
91 except InterfaceError:
92 return False
93 return True
96 #-----------------------------------------------------------checkattr---
97 def checkattr(interface, name, value):
98 '''
99 '''
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:
105 if '*' in format:
106 attr_format = format['*']
107 else:
108 raise InterfaceError, 'attribute "%s" is not defined in inteface %s' % (name, interface)
109 else:
110 attr_format = format[name].copy()
111 # special options:
112 # LIKE:
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()
118 else:
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:
124 # type:
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'])
128 # predicate:
129 if 'predicate' in attr_format and not attr_format['predicate'](value):
130 raise InterfaceError, 'predicate failed for "%s" attribute.' % name
131 return True
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__
141 obj_attrs = dir(obj)
142 for attr in format:
143 if format[attr].get('essential', False) and attr not in obj_attrs:
144 raise InterfaceError, 'essential attribute "%s" missing.' % attr
145 return True
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__
156 obj_attrs = dir(obj)
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):
161 ##!!! reason...
162 return False
163 # check the star :)
164 if len(obj_data) > 0 and '*' not in format:
165 ## ##!!! reason...
166 ## return False
167 raise InterfaceError, 'excess attributes (%s).' % obj_data.keys()
168 # check if any essentials are left out...
169 if not interface.checkessentials(obj):
170 ##!!! reason...
171 return False
172 return True
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):
183 ##!!!
184 pass
187 #----------------------------------------------------------getdocdict---
188 def getdocdict(obj, attr=None, interface=None):
191 ##!!!
192 pass
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:
214 <attr-name> :
216 <opt-name>: <opt-value>
217 [...]
219 [...]
223 supported options:
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
237 original value.
239 readable - this if False will prevent the attr from being
240 read.
241 writable - this if False will prevent the attr from being
242 written.
243 deleteable - this if False will prevent the attr from being
244 removed.
246 special options:
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.
260 __format__ = None
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']) \
288 for n, v \
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 != '*' ]))
293 return obj
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
325 __source__ = 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 :