1import types, itertools, exceptions 2 3def type_string(obj): 4 objType = type(obj) 5 if objType is types.InstanceType: 6 objType = obj.__class__ 7 return getattr(objType, '__module__', '-') + '.' + objType.__name__ 8 9class NetRepr(object): 10 def __init__(self, objectPool): 11 self.objectPool = objectPool 12 self.cache = {} 13 self._identfactory = itertools.count() 14 15 def clear(self): 16 self.cache.clear() 17 self._identfactory = itertools.count() 18 19 def netrepr_tuple(self, obj): 20 return repr(tuple(itertools.imap(self.netrepr, obj))) 21 22 def netrepr_list(self, obj): 23 return repr(map(self.netrepr, obj)) 24 25 def netrepr_exception(self, e): 26 cls = e.__class__ 27 if cls.__module__ == 'exceptions': 28 rval = cls.__name__ + self.netrepr_tuple(e.args) 29 else: 30 rval = 'Exception(%r)' % ('[Remote] %s.%s %s' % (cls.__module__, cls.__name__, e),) 31 return rval 32 33 def netrepr(self, obj): 34 if obj is None: 35 return 'None' 36 objtype = type(obj) 37 if objtype is int or objtype is long or objtype is float: 38 return repr(obj) 39 elif objtype is str or objtype is unicode: 40 if True: 41 return repr(obj) 42 else: 43 # "intern" these 44 obj_id = id(obj) 45 cached = self.get(cache, obj_id, None) 46 if cached is None: 47 ident = self._identfactory.next() 48 self.cache[obj_id] = '__cached__(%r)' % (obj_id,) 49 cached = '__cache__(%r, %r)' % (obj_id, obj) 50 return cached 51 return self.netrepr_default(obj) 52 53 def netrepr_default(self, obj): 54 method = getattr(obj, '__netrepr__', None) 55 if method is None: 56 method = self.objectPool.referenceForObject(obj).__netrepr__ 57 return method() 58 59 60class BaseObjectPool(object): 61 def __init__(self): 62 self.idents = {} 63 self.refs = {} 64 self.pools = [] 65 66 def referenceForIdent(self, ident): 67 return self.idents[ident] 68 69 def base_alloc(self, ref, ident): 70 self.refs[ref] = ident 71 self.idents[ident] = ref 72 73 def base_dealloc(self, ref, ident): 74 del self.refs[ref] 75 del self.idents[ident] 76 77 def autorelease(self, ref): 78 if not self.pools: 79 raise RuntimeError, "no autoreleasepool for %r" % (ref,) 80 pool = self.pools[-1] 81 pool[ref] = pool.get(ref, 0) + 1 82 83 def push(self): 84 #print "pushed pool" 85 self.pools.append({}) 86 87 def pop(self): 88 if not self.pools: 89 raise RuntimeError, "popped too many pools" 90 #print "popped pool" 91 pool = self.pools.pop() 92 for ref, count in pool.iteritems(): 93 ref.release(count) 94 95 def referenceForObject(self, obj): 96 raise TypeError, "Can not create a reference to %r, the bridge is unidirectional" % (obj,) 97 98 99class RemoteObjectPool(BaseObjectPool): 100 def __init__(self, writecode): 101 BaseObjectPool.__init__(self) 102 self.writecode = writecode 103 self.namespace = { 104 'None': None, 105 '__ref__': self.referenceForRemoteIdent, 106 } 107 108 def referenceForRemoteIdent(self, ident, type_string): 109 rval = self.idents.get(ident) 110 if rval is None: 111 rval = RemoteObjectReference(self, ident, type_string) 112 return rval 113 114 115class ObjectPool(BaseObjectPool): 116 def __init__(self): 117 BaseObjectPool.__init__(self) 118 self._identfactory = itertools.count() 119 self.obj_ids = {} 120 self.namespace = { 121 '__obj__': self.objectForIdent, 122 } 123 124 def object_alloc(self, ref, obj_id): 125 self.obj_ids[obj_id] = ref 126 127 def object_dealloc(self, ref, obj_id): 128 del self.obj_ids[obj_id] 129 130 def objectForIdent(self, ident): 131 return self.referenceForIdent(ident).obj 132 133 def referenceForObject(self, obj): 134 obj_id = id(obj) 135 rval = self.obj_ids.get(obj_id) 136 if rval is None: 137 ident = self._identfactory.next() 138 rval = ObjectReference(self, ident, type_string(obj), obj, obj_id) 139 rval = rval.alloc().autorelease() 140 return rval 141 142 143class BaseObjectReference(object): 144 def __init__(self, objectPool, ident, type_string): 145 self.ident = ident 146 self.type_string = type_string 147 self.objectPool = objectPool 148 self.retainCount = 1 149 150 def retain(self, count=1): 151 #print "%r.retain(%d)" % (self, count) 152 self.retainCount += count 153 return self 154 155 def alloc(self): 156 self.objectPool.base_alloc(self, self.ident) 157 return self 158 159 def dealloc(self): 160 self.objectPool.base_dealloc(self, self.ident) 161 self.retainCount = -1 162 163 def release(self, count=1): 164 #print "%r.release(%d)" % (self, count) 165 newCount = self.retainCount - count 166 #print " newCount = %d" % (newCount,) 167 if newCount == 0: 168 self.dealloc() 169 elif newCount < 0: 170 raise ValueError, "Reference %r over-released (%r -> %r)" % (self, self.retainCount, newCount) 171 self.retainCount = newCount 172 return self 173 174 def autorelease(self): 175 #print "%s.autorelease()" % (self,) 176 self.objectPool.autorelease(self) 177 return self 178 179 def __repr__(self): 180 return "%s(%r, %r)" % (type(self).__name__, self.ident, self.type_string) 181 182 183class RemoteObjectReference(BaseObjectReference): 184 def __netrepr__(self): 185 return "__obj__(%r)" % (self.ident,) 186 187 188class ObjectReference(BaseObjectReference): 189 def __init__(self, objectPool, ident, type_string, obj, obj_id): 190 BaseObjectReference.__init__(self, objectPool, ident, type_string) 191 self.obj = obj 192 self.obj_id = id(obj) 193 194 def alloc(self): 195 self = BaseObjectReference.alloc(self) 196 self.objectPool.object_alloc(self, self.obj_id) 197 return self 198 199 def dealloc(self): 200 self.objectPool.object_dealloc(self, self.obj_id) 201 self.obj = None 202 self.obj_id = -1 203 BaseObjectReference.dealloc(self) 204 205 def __netrepr__(self): 206 return "__ref__(%r, %r)" % (self.ident, self.type_string) 207 208 209def test_netrepr(): 210 import compiler 211 pool = ObjectPool() 212 pool.push() 213 netrepr = NetRepr(pool).netrepr 214 assert netrepr("foo") == repr("foo") 215 ref = pool.referenceForObject(object) 216 assert ref.obj is object 217 assert ref is pool.referenceForObject(object) 218 assert ref.retainCount == 1 219 refrepr = netrepr(ref) 220 assert refrepr == netrepr(ref) 221 ref.retain() 222 assert ref.retainCount == 2 223 pool.pop() 224 pool.push() 225 assert ref.retainCount == 1 226 def __ref__(ident, type_string): 227 return pool.referenceForIdent(ident) 228 netref = eval(refrepr) 229 assert netref is ref 230 assert netref.obj is object 231 ref.release() 232 pool.pop() 233 assert ref.obj is None 234