diff pydershell/pydershell.py @ 18:69e5523ebdc6

Separated out SafeJsObjectWrapper into itself and a new subclass, SafeJsFunctionWrapper, so that callable() works properly on instances.
author Atul Varma <varmaa@toolness.com>
date Mon, 07 Sep 2009 04:38:44 -0700
parents 1d62177c5c27
children 057260102960
line wrap: on
line diff
--- a/pydershell/pydershell.py	Mon Sep 07 04:23:09 2009 -0700
+++ b/pydershell/pydershell.py	Mon Sep 07 04:38:44 2009 -0700
@@ -81,29 +81,23 @@
         object.__setattr__(self, '_cx', cx)
         object.__setattr__(self, '_this', this)
 
-    def __wrap_to_python(self, value):
-        if isinstance(value, pydermonkey.Object):
-            return SafeJsObjectWrapper(self._cx, value, self._jsobject)
+    @staticmethod
+    def wrap(cx, jsvalue, this):
+        if isinstance(jsvalue, pydermonkey.Function):
+            return SafeJsFunctionWrapper(cx, jsvalue, this)
+        elif isinstance(jsvalue, pydermonkey.Object):
+            return SafeJsObjectWrapper(cx, jsvalue, this)
         else:
-            return value
+            # It's a primitive value.
+            return jsvalue
 
-    def __wrap_to_js(self, value):
+    def _wrap_to_python(self, jsvalue):
+        return self.wrap(self._cx, jsvalue, self._jsobject)
+
+    def _wrap_to_js(self, value):
         # TODO: Add support for wrapping non-primitive python objects.
         return value
 
-    def __call__(self, *args):
-        cx = self._cx
-        jsobject = self._jsobject
-        this = self._this
-
-        if isinstance(jsobject, pydermonkey.Function):
-            obj = cx.call_function(this, jsobject,
-                                   self.__wrap_to_js(args))
-            return self.__wrap_to_python(obj)
-        else:
-            raise TypeError("'%s' object is not callable" %
-                            type(jsobject).__name__)
-
     def __eq__(self, other):
         if isinstance(other, SafeJsObjectWrapper):
             return self._jsobject == other._jsobject
@@ -124,7 +118,7 @@
         jsobject = self._jsobject
 
         cx.define_property(jsobject, name,
-                           self.__wrap_to_js(value))
+                           self._wrap_to_js(value))
 
     def __getitem__(self, item):
         return self.__getattr__(item)
@@ -133,7 +127,7 @@
         cx = self._cx
         jsobject = self._jsobject
 
-        return self.__wrap_to_python(cx.get_property(jsobject, name))
+        return self._wrap_to_python(cx.get_property(jsobject, name))
 
     def __contains__(self, item):
         cx = self._cx
@@ -149,6 +143,26 @@
         for property in properties:
             yield property
 
+class SafeJsFunctionWrapper(SafeJsObjectWrapper):
+    """
+    Securely wraps a JS function to behave like any normal Python object.
+    """
+
+    def __init__(self, cx, jsfunction, this):
+        if not isinstance(jsfunction, pydermonkey.Function):
+            raise TypeError("Cannot wrap '%s' object" %
+                            type(jsobject).__name__)
+        SafeJsObjectWrapper.__init__(self, cx, jsfunction, this)
+
+    def __call__(self, *args):
+        cx = self._cx
+        jsobject = self._jsobject
+        this = self._this
+
+        obj = cx.call_function(this, jsobject,
+                               self._wrap_to_js(args))
+        return self._wrap_to_python(obj)
+
 def safejsfunc(cx, on_obj, name=None):
     """
     Exposes the decorated Python function on the given JS object.
@@ -224,7 +238,7 @@
 
         @safejsfunc(cx, globalobj)
         def bar(cx, this, args):
-            obj = SafeJsObjectWrapper(cx, args[0], this)
+            obj = SafeJsObjectWrapper.wrap(cx, args[0], this)
             print obj.bar()
 
         @safejsfunc(cx, globalobj)