Package gmisclib :: Module g_implements
[frames] | no frames]

Source Code for Module gmisclib.g_implements

  1  """This module tells you if an object's signature matches a class. 
  2  It lets you know if one class can substitute for another, 
  3  even if they are unrelated in an OO hierarchy. 
  4   
  5  This allows you to begin to check that a foreign object will have the 
  6  necessary properties to be used in your code.   It is more of a 
  7  functional check than C{isimplements}(). 
  8   
  9  For instance, if your code needs a write() method, you 
 10  can check for it like this:: 
 11   
 12          class some_template(object): 
 13                  def write(self, somearg): 
 14                          pass 
 15          # 
 16          g_implements.check(x, some_template) 
 17          #... at this point, we can be assured that a 
 18          # call to x.write(3) is possible. 
 19          # (Note that we don't know what the call will *do*, 
 20          # merely that the proper method of x exists and that 
 21          # it can take an argument. 
 22   
 23  Semi-kluge:   if you have an instance, C{i} that needs to convince 
 24  C{g_implements} that it can actually behave as class C{C}, 
 25  then set C{i.__g_implements__ = ['C']}. 
 26  Generally, this can be a list or set of class names. 
 27  This trick is useful for C-implementations of python classes 
 28  (where introspection cannot get all the necessary information) or 
 29  for fancy python classes that use C{__getattribute__} and similar. 
 30  """ 
 31   
 32  import inspect 
 33   
 34  _cache = {} 
 35  # g_implements keeps a cache of previous results for speed.  MAXCACHE 
 36  # sets the size of the number if types to remember.  Note that the classes 
 37  # are cached, not the instances themselves.   You can call L{why}(o, C) 
 38  # with a million different objects C{o}, and as long as all of them are 
 39  # in the same class, this will only occupy one cache slot. 
 40  # (And, the million queries will be answered from the cache as long as all 
 41  # of the C{o} are in the same class.) 
 42  MAXCACHE = 100 
 43   
 44  Optional = 'optional' 
 45  Varargs = 'varargs' 
46 -def Vartype(mem, com):
47 return True
48 -def Strict(mem, com):
49 return False
50 51 Ignore = set( ['__class__', '__delattr__', '__module__', '__new__', 52 '__doc__', '__dict__', '__getattribute__', 53 '__hash__', 54 '__repr__', '__str__', 55 '__reduce__', '__reduce_ex__', '__setattr__', 56 '__weakref__', '__slots__' 57 ] 58 ) 59 60
61 -def why(instance, classobj, ignore=None):
62 """Explains why instance doesn't implement the classobj. 63 @param instance: an instance of a class 64 @param classobj: a class object 65 @return: None (if it does) or a string explanation of why it doesn't 66 @rtype None or str 67 """ 68 try: 69 c = instance.__class__ 70 except AttributeError: 71 return "Instance (arg 1) has no __class__ attribute." 72 key = (c, classobj) 73 try: 74 return _cache[key] 75 except KeyError: 76 pass 77 assert MAXCACHE >= 0 78 while len(_cache) > MAXCACHE: 79 _cache.popitem() 80 81 try: 82 # This is a semi-kluge to allow an instance to declare that it 83 # can implement a particular class. 84 if classobj.__name__ in instance.__g_implements__: 85 _cache[key] = None 86 return None 87 except AttributeError: 88 pass 89 90 for mem in dir(classobj): 91 if mem in Ignore or (ignore is not None and mem in ignore): 92 continue 93 v = getattr(classobj, mem) 94 gis = getattr(v, 'g_implements', Strict) 95 if gis == Optional: 96 continue 97 if not hasattr(c, mem): 98 _cache[key] = 'Missing member: %s; %s does not implement %s' % (mem, str(type(instance)), str(classobj)) 99 return _cache[key] 100 com = getattr(c, mem) 101 if callable(gis) and gis(mem, com): 102 continue 103 if inspect.ismethod(com): 104 if inspect.ismethod(v): 105 vargs, vv, vk, vdef = inspect.getargspec(v.im_func) 106 margs, mv, mk, mdef = inspect.getargspec(com.im_func) 107 108 if mdef is None: 109 matchlen = len(margs) 110 else: 111 matchlen = len(margs)-len(mdef) 112 if vargs[:matchlen] != margs[:matchlen]: 113 if gis == Varargs: 114 continue 115 _cache[key] = "Method argument list does not match for %s: %s instead of %s; type=%s does not implement %s" \ 116 % (mem, margs[:matchlen], vargs[:matchlen], 117 str(type(instance)), str(classobj)) 118 return _cache[key] 119 elif inspect.ismethoddescriptor(v): 120 continue 121 elif type(com) != type(v): 122 _cache[key] = 'Wrong type for %s: %s instead of %s; type=%s does not implement %s' \ 123 % (mem, type(com), type(v), 124 str(type(instance)), str(classobj) 125 ) 126 return _cache[key] 127 _cache[key] = None 128 return _cache[key]
129 130
131 -def impl(instance, classobj):
132 """Tells you if an instance of an object implements a class. 133 By implements, I mean that the instance supplies every member 134 that the class supplies, and every member has the same type. 135 The instance may have *more* members, of course. 136 Functions require that the argument names must match, too 137 at least as far as the required arguments in the classobj's function. 138 139 The match may be made looser by adding a g_implements attribute 140 to various class members. Possibilities for the value 141 are Optional, Strict, Vartype, Varargs, or you can give 142 a two-argument function, and that function will be called 143 to decide whether the match is acceptable or not. 144 """ 145 return why(instance, classobj) is None
146 147
148 -def make_optional(x):
149 """This is a decorator for a function. 150 make_optional implies make_varargs. 151 """ 152 x.g_implements = Optional 153 return x
154
155 -def make_varargs(x):
156 """This is a decorator for a function.""" 157 x.g_implements = Varargs 158 return x
159
160 -def make_vartype(x):
161 """This is a decorator for a function.""" 162 x.g_implements = Vartype 163 return x
164
165 -def make_strict(x):
166 """This is a decorator for a function.""" 167 x.g_implements = Strict 168 return x
169 170
171 -class GITypeError(TypeError):
172 """Exception raised by L{check} to indicate failure. 173 """
174 - def __init__(self, s):
175 TypeError.__init__(self, s)
176 177
178 -def check(instance, classobj, ignore=None):
179 """Require that C{instance} has the attributes defined by C{classobj}. 180 This function either quietly succeeds or it raises a GITypeError exception. 181 @type instance: any instance of a class (i.e. anything with a C{__class__} attribute). 182 @type classobj: any class object (i.e. not an instance, typically). 183 @param instance: the thing to check 184 @param classobj: a class that provides a pattern of attributes. 185 @raise GITypeError: C{instance} doesn't provide all the features of C{classobj}. 186 """ 187 w = why(instance, classobj, ignore=ignore) 188 if w is not None: 189 raise GITypeError, w
190 191
192 -class _tc1(object):
193 x = 1 194 y = ''
195 - def z(self, foo):
196 return foo
197
198 -class _tc2(object):
199 - def __init__(self):
200 pass
201
202 -class _tc1a(object):
203 x = 0 204 y = 'x'
205 - def z(self, foo):
206 return foo+1
207
208 -class _tc3(object):
209 x = 0.0 210 y = 'x'
211 - def z(self, foo):
212 return foo+1
213
214 -class _tc1b(object):
215 x = 0 216 y = 'x'
217 - def z(self, foo):
218 return foo+1
219 w = []
220 - def foo(self, a, b):
221 return a+b
222 223
224 -class _tc0a(object):
225 x = 1
226
227 -class _tc0b(object):
228 y = ''
229 - def z(self, foo):
230 return 0
231
232 -class _tc1c(_tc0a, _tc0b):
233 pass
234
235 -class _tc2a(object):
236 x = 1 237 y = ''
238 - def z(self, bar):
239 return bar
240
241 -def test():
242 assert not impl(_tc1(), _tc2) 243 check(_tc1(), _tc1a) 244 assert not impl(_tc1(), _tc3) 245 assert not impl(_tc1(), _tc1b) 246 check(_tc1b(), _tc1) 247 check(_tc1c(), _tc1) 248 check(_tc1(), _tc1c) 249 assert not impl(_tc1(), _tc2a)
250 251 if __name__ == '__main__': 252 test() 253