pull in additional changes from 2.0 branch.
[monitor.git] / monitor / Rpyc / Boxing.py
diff --git a/monitor/Rpyc/Boxing.py b/monitor/Rpyc/Boxing.py
new file mode 100644 (file)
index 0000000..93e89ba
--- /dev/null
@@ -0,0 +1,124 @@
+import sys\r
+import traceback\r
+import cPickle as pickle\r
+from weakref import WeakValueDictionary\r
+from Lib import ImmDict\r
+from NetProxy import NetProxy, SyncNetProxy\r
+\r
+\r
+class BoxingError(Exception):\r
+    pass\r
+class NestedException(Exception): \r
+    pass\r
+\r
+PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL\r
+TYPE_SIMPLE = 0\r
+TYPE_PROXY = 1\r
+TYPE_TUPLE = 2\r
+TYPE_SLICE = 3\r
+TYPE_LOCAL_PROXY = 4\r
+TYPE_IMMDICT = 5\r
+simple_types = (\r
+    bool, \r
+    int, \r
+    long, \r
+    float, \r
+    complex, \r
+    basestring, \r
+    type(None),\r
+)\r
+\r
+def dump_exception(typ, val, tb):\r
+    """dumps the given exception using pickle (since not all exceptions are picklable)"""\r
+    tbtext = "".join(traceback.format_exception(typ, val, tb))\r
+    sys.last_type, sys.last_value, sys.last_traceback = typ, val, tb\r
+    try:\r
+        pickled_exc = pickle.dumps((typ, val, tbtext), PICKLE_PROTOCOL)\r
+    except pickle.PicklingError, ex:\r
+        newval = NestedException("pickling error %s\nexception type: %r\nexception object: %s" % (ex, typ, val))\r
+        pickled_exc = pickle.dumps((NestedException, newval, tbtext), PICKLE_PROTOCOL)\r
+    return pickled_exc\r
+\r
+def load_exception(package):\r
+    """returns an exception object"""\r
+    try:\r
+        return pickle.loads(package)\r
+    except pickle.PicklingError, ex:\r
+        return NestedException("failed to unpickle remote exception -- %r" % (ex,))\r
+\r
+class Box(object):\r
+    """a box is where local objects are stored, and remote proxies are created"""\r
+    def __init__(self, conn):\r
+        self.conn = conn\r
+        self.objects = {}\r
+        self.proxy_cache = WeakValueDictionary()\r
+\r
+    def close(self):\r
+        del self.conn\r
+        del self.objects\r
+        del self.proxy_cache\r
+    \r
+    def __getitem__(self, oid):\r
+        return self.objects[oid][1]\r
+\r
+    def _box(self, obj):\r
+        if isinstance(obj, simple_types):\r
+            return TYPE_SIMPLE, obj\r
+        elif isinstance(obj, slice):\r
+            return TYPE_SLICE, (obj.start, obj.stop, obj.step)\r
+        elif isinstance(obj, NetProxy) and obj.__dict__["____conn"] is self.conn:\r
+            return TYPE_LOCAL_PROXY, obj.__dict__["____oid"]\r
+        elif isinstance(obj, tuple):\r
+            if obj:\r
+                return TYPE_TUPLE, [self._box(subobj) for subobj in obj]\r
+            else:\r
+                return TYPE_SIMPLE, ()\r
+        elif isinstance(obj, ImmDict):\r
+            if not obj.dict:\r
+                return TYPE_SIMPLE, {}\r
+            else:\r
+                return TYPE_IMMDICT, [(self._box(k), self._box(v)) for k, v in obj.items()]\r
+        else:\r
+            oid = id(obj)\r
+            if oid not in self.objects:\r
+                self.objects[oid] = [0, obj]\r
+            return TYPE_PROXY, oid\r
+        \r
+    def _unbox(self, (type, value)):\r
+        if type == TYPE_SIMPLE:\r
+            return value\r
+        elif type == TYPE_TUPLE:\r
+            return tuple([self._unbox(subobj) for subobj in value])\r
+        elif type == TYPE_SLICE:\r
+            return slice(*value)\r
+        elif type == TYPE_LOCAL_PROXY:\r
+            return self[value]\r
+        elif type == TYPE_IMMDICT:\r
+            return dict([(self._unbox(k), self._unbox(v)) for k, v in value])\r
+        elif type == TYPE_PROXY:\r
+            if value in self.proxy_cache:\r
+                proxy = self.proxy_cache[value]\r
+            else:\r
+                proxy = SyncNetProxy(self.conn, value)\r
+                self.proxy_cache[value] = proxy\r
+            return proxy\r
+        else:\r
+            raise BoxingError("invalid boxed object type", type, value)\r
+        \r
+    def incref(self, oid):\r
+        self.objects[oid][0] += 1\r
+\r
+    def decref(self, oid):\r
+        self.objects[oid][0] -= 1\r
+        if self.objects[oid][0] <= 0:\r
+            del self.objects[oid]\r
+    \r
+    def pack(self, obj):\r
+        """packs an object (returns a package)"""\r
+        return pickle.dumps(self._box(obj), PICKLE_PROTOCOL)\r
+\r
+    def unpack(self, package):\r
+        """unpacks a package (returns an object)"""\r
+        return self._unbox(pickle.loads(package))\r
+\r
+\r