import types, itertools, exceptions def type_string(obj): objType = type(obj) if objType is types.InstanceType: objType = obj.__class__ return getattr(objType, '__module__', '-') + '.' + objType.__name__ class NetRepr(object): def __init__(self, objectPool): self.objectPool = objectPool self.cache = {} self._identfactory = itertools.count() def clear(self): self.cache.clear() self._identfactory = itertools.count() def netrepr_tuple(self, obj): return repr(tuple(itertools.imap(self.netrepr, obj))) def netrepr_list(self, obj): return repr(map(self.netrepr, obj)) def netrepr_exception(self, e): cls = e.__class__ if cls.__module__ == 'exceptions': rval = cls.__name__ + self.netrepr_tuple(e.args) else: rval = 'Exception(%r)' % ('[Remote] %s.%s %s' % (cls.__module__, cls.__name__, e),) return rval def netrepr(self, obj): if obj is None: return 'None' objtype = type(obj) if objtype is int or objtype is long or objtype is float: return repr(obj) elif objtype is str or objtype is unicode: if True: return repr(obj) else: # "intern" these obj_id = id(obj) cached = self.get(cache, obj_id, None) if cached is None: ident = self._identfactory.next() self.cache[obj_id] = '__cached__(%r)' % (obj_id,) cached = '__cache__(%r, %r)' % (obj_id, obj) return cached return self.netrepr_default(obj) def netrepr_default(self, obj): method = getattr(obj, '__netrepr__', None) if method is None: method = self.objectPool.referenceForObject(obj).__netrepr__ return method() class BaseObjectPool(object): def __init__(self): self.idents = {} self.refs = {} self.pools = [] def referenceForIdent(self, ident): return self.idents[ident] def base_alloc(self, ref, ident): self.refs[ref] = ident self.idents[ident] = ref def base_dealloc(self, ref, ident): del self.refs[ref] del self.idents[ident] def autorelease(self, ref): if not self.pools: raise RuntimeError, "no autoreleasepool for %r" % (ref,) pool = self.pools[-1] pool[ref] = pool.get(ref, 0) + 1 def push(self): #print "pushed pool" self.pools.append({}) def pop(self): if not self.pools: raise RuntimeError, "popped too many pools" #print "popped pool" pool = self.pools.pop() for ref, count in pool.iteritems(): ref.release(count) def referenceForObject(self, obj): raise TypeError, "Can not create a reference to %r, the bridge is unidirectional" % (obj,) class RemoteObjectPool(BaseObjectPool): def __init__(self, writecode): BaseObjectPool.__init__(self) self.writecode = writecode self.namespace = { 'None': None, '__ref__': self.referenceForRemoteIdent, } def referenceForRemoteIdent(self, ident, type_string): rval = self.idents.get(ident) if rval is None: rval = RemoteObjectReference(self, ident, type_string) return rval class ObjectPool(BaseObjectPool): def __init__(self): BaseObjectPool.__init__(self) self._identfactory = itertools.count() self.obj_ids = {} self.namespace = { '__obj__': self.objectForIdent, } def object_alloc(self, ref, obj_id): self.obj_ids[obj_id] = ref def object_dealloc(self, ref, obj_id): del self.obj_ids[obj_id] def objectForIdent(self, ident): return self.referenceForIdent(ident).obj def referenceForObject(self, obj): obj_id = id(obj) rval = self.obj_ids.get(obj_id) if rval is None: ident = self._identfactory.next() rval = ObjectReference(self, ident, type_string(obj), obj, obj_id) rval = rval.alloc().autorelease() return rval class BaseObjectReference(object): def __init__(self, objectPool, ident, type_string): self.ident = ident self.type_string = type_string self.objectPool = objectPool self.retainCount = 1 def retain(self, count=1): #print "%r.retain(%d)" % (self, count) self.retainCount += count return self def alloc(self): self.objectPool.base_alloc(self, self.ident) return self def dealloc(self): self.objectPool.base_dealloc(self, self.ident) self.retainCount = -1 def release(self, count=1): #print "%r.release(%d)" % (self, count) newCount = self.retainCount - count #print " newCount = %d" % (newCount,) if newCount == 0: self.dealloc() elif newCount < 0: raise ValueError, "Reference %r over-released (%r -> %r)" % (self, self.retainCount, newCount) self.retainCount = newCount return self def autorelease(self): #print "%s.autorelease()" % (self,) self.objectPool.autorelease(self) return self def __repr__(self): return "%s(%r, %r)" % (type(self).__name__, self.ident, self.type_string) class RemoteObjectReference(BaseObjectReference): def __netrepr__(self): return "__obj__(%r)" % (self.ident,) class ObjectReference(BaseObjectReference): def __init__(self, objectPool, ident, type_string, obj, obj_id): BaseObjectReference.__init__(self, objectPool, ident, type_string) self.obj = obj self.obj_id = id(obj) def alloc(self): self = BaseObjectReference.alloc(self) self.objectPool.object_alloc(self, self.obj_id) return self def dealloc(self): self.objectPool.object_dealloc(self, self.obj_id) self.obj = None self.obj_id = -1 BaseObjectReference.dealloc(self) def __netrepr__(self): return "__ref__(%r, %r)" % (self.ident, self.type_string) def test_netrepr(): import compiler pool = ObjectPool() pool.push() netrepr = NetRepr(pool).netrepr assert netrepr("foo") == repr("foo") ref = pool.referenceForObject(object) assert ref.obj is object assert ref is pool.referenceForObject(object) assert ref.retainCount == 1 refrepr = netrepr(ref) assert refrepr == netrepr(ref) ref.retain() assert ref.retainCount == 2 pool.pop() pool.push() assert ref.retainCount == 1 def __ref__(ident, type_string): return pool.referenceForIdent(ident) netref = eval(refrepr) assert netref is ref assert netref.obj is object ref.release() pool.pop() assert ref.obj is None