Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r938:f4aed9ff91d6
Date: 2012-09-18 11:07 +0200
http://bitbucket.org/cffi/cffi/changeset/f4aed9ff91d6/

Log:    Make ffi.callback work both in normal mode and in "decorator mode".

diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -207,17 +207,25 @@
         """
         return self._backend.buffer(cdata, size)
 
-    def callback(self, cdecl, python_callable, error=None):
-        """Return a callback object.  'cdecl' must name a C function pointer
-        type.  The callback invokes the specified 'python_callable'.
-        Important: the callback object must be manually kept alive for as
-        long as the callback may be invoked from the C level.
+    def callback(self, cdecl, python_callable=None, error=None):
+        """Return a callback object or a decorator making such a
+        callback object.  'cdecl' must name a C function pointer type.
+        The callback invokes the specified 'python_callable' (which may
+        be provided either directly or via a decorator).  Important: the
+        callback object must be manually kept alive for as long as the
+        callback may be invoked from the C level.
         """
-        if not callable(python_callable):
-            raise TypeError("the 'python_callable' argument is not callable")
+        def callback_decorator_wrap(python_callable):
+            if not callable(python_callable):
+                raise TypeError("the 'python_callable' argument "
+                                "is not callable")
+            return self._backend.callback(cdecl, python_callable, error)
         if isinstance(cdecl, str):
             cdecl = self._typeof(cdecl, consider_function_as_funcptr=True)
-        return self._backend.callback(cdecl, python_callable, error)
+        if python_callable is None:
+            return callback_decorator_wrap                # decorator mode
+        else:
+            return callback_decorator_wrap(python_callable)  # direct mode
 
     def getctype(self, cdecl, replace_with=''):
         """Return a string giving the C type 'cdecl', which may be itself
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -901,6 +901,13 @@
     >>> ffi.callback("int(*)(int, int)", myfunc)
     <cdata 'int(*)(int, int)' calling <function myfunc at 0xf757bbc4>>
 
+.. versionadded:: 0.4
+   Or equivalently as a decorator:
+
+    >>> @ffi.callback("int(*)(int, int)")
+    ... def myfunc(x, y):
+    ...    return x + y
+
 Warning: like ffi.new(), ffi.callback() returns a cdata that has
 ownership of its C data.  (In this case, the necessary C data contains
 the libffi data structures to do a callback.)  This means that the
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -663,7 +663,6 @@
 
     def test_functionptr_simple(self):
         ffi = FFI(backend=self.Backend())
-        py.test.raises(TypeError, ffi.callback, "int(*)(int)")
         py.test.raises(TypeError, ffi.callback, "int(*)(int)", 0)
         def cb(n):
             return n + 1
@@ -1259,6 +1258,16 @@
         res = f(10, ffi.cast("int", 100), ffi.cast("long long", 1000))
         assert res == 20 + 300 + 5000
 
+    def test_callback_decorator(self):
+        ffi = FFI(backend=self.Backend())
+        #
+        @ffi.callback("long(long, long)", error=42)
+        def cb(a, b):
+            return a - b
+        #
+        assert cb(-100, -10) == -90
+        assert cb(sys.maxint, -10) == 42
+
     def test_unique_types(self):
         ffi1 = FFI(backend=self.Backend())
         ffi2 = FFI(backend=self.Backend())
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to