Author: Armin Rigo <[email protected]>
Branch: cffi-1.0
Changeset: r1808:6b749854ff50
Date: 2015-04-25 10:57 +0200
http://bitbucket.org/cffi/cffi/changeset/6b749854ff50/

Log:    ffi.callback()

diff --git a/_cffi1/ffi_obj.c b/_cffi1/ffi_obj.c
--- a/_cffi1/ffi_obj.c
+++ b/_cffi1/ffi_obj.c
@@ -94,6 +94,7 @@
 #define ACCEPT_CTYPE    2
 #define ACCEPT_CDATA    4
 #define ACCEPT_ALL      (ACCEPT_STRING | ACCEPT_CTYPE | ACCEPT_CDATA)
+#define CONSIDER_FN_AS_FNPTR  8
 
 static CTypeDescrObject *_ffi_type(FFIObject *ffi, PyObject *arg,
                                    int accept)
@@ -120,8 +121,14 @@
                          input_text, spaces);
             return NULL;
         }
-        CTypeDescrObject *ct = realize_c_type(ffi->types_builder,
-                                              ffi->info.output, index);
+        CTypeDescrObject *ct;
+        if (accept & CONSIDER_FN_AS_FNPTR) {
+            ct = realize_c_type_fn_as_fnptr(ffi->types_builder,
+                                            ffi->info.output, index);
+        }
+        else {
+            ct = realize_c_type(ffi->types_builder, ffi->info.output, index);
+        }
         if (ct == NULL)
             return NULL;
 
@@ -505,6 +512,55 @@
 }
 #endif
 
+PyDoc_STRVAR(ffi_callback_doc,
+"Return a callback object or a decorator making such a callback object.\n"
+"'cdecl' must name a C function pointer type.  The callback invokes the\n"
+"specified 'python_callable' (which may be provided either directly or\n"
+"via a decorator).  Important: the callback object must be manually\n"
+"kept alive for as long as the callback may be invoked from the C code.");
+
+static PyObject *_ffi_callback_decorator(PyObject *outer_args, PyObject *fn)
+{
+    PyObject *res, *old;
+
+    old = PyTuple_GET_ITEM(outer_args, 1);
+    PyTuple_SET_ITEM(outer_args, 1, fn);
+    res = b_callback(NULL, outer_args);
+    PyTuple_SET_ITEM(outer_args, 1, old);
+    return res;
+}
+
+static PyObject *ffi_callback(FFIObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *cdecl, *python_callable = Py_None, *error = Py_None;
+    PyObject *res;
+    static char *keywords[] = {"cdecl", "python_callable", "error", NULL};
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OO", keywords,
+                                     &cdecl, &python_callable, &error))
+        return NULL;
+
+    cdecl = (PyObject *)_ffi_type(self, cdecl, ACCEPT_STRING | ACCEPT_CDATA |
+                                               CONSIDER_FN_AS_FNPTR);
+    if (cdecl == NULL)
+        return NULL;
+
+    args = Py_BuildValue("(OOO)", cdecl, python_callable, error);
+    if (args == NULL)
+        return NULL;
+
+    if (python_callable != Py_None) {
+        res = b_callback(NULL, args);
+    }
+    else {
+        static PyMethodDef md = {"callback_decorator",
+                                 (PyCFunction)_ffi_callback_decorator, METH_O};
+        res = PyCFunction_New(&md, args);
+    }
+    Py_DECREF(args);
+    return res;
+}
+
 PyDoc_STRVAR(ffi_errno_doc, "the value of 'errno' from/to the C calls");
 
 static PyObject *ffi_get_errno(PyObject *self, void *closure)
@@ -610,27 +666,27 @@
 }
 
 static PyMethodDef ffi_methods[] = {
-    {"__set_types",   (PyCFunction)ffi__set_types,METH_VARARGS},
+    {"__set_types",(PyCFunction)ffi__set_types, METH_VARARGS},
 #if 0
-    {"addressof",     (PyCFunction)ffi_addressof, METH_VARARGS},
+    {"addressof",  (PyCFunction)ffi_addressof,  METH_VARARGS},
 #endif
-    {"alignof",       (PyCFunction)ffi_alignof,   METH_O,      
ffi_alignof_doc},
-    {"cast",          (PyCFunction)ffi_cast,      METH_VARARGS, ffi_cast_doc},
+    {"alignof",    (PyCFunction)ffi_alignof,    METH_O,       ffi_alignof_doc},
+    {"callback",   (PyCFunction)ffi_callback,   METH_VARARGS |
+                                                
METH_KEYWORDS,ffi_callback_doc},
+    {"cast",       (PyCFunction)ffi_cast,       METH_VARARGS, ffi_cast_doc},
 #if 0
-    {"close_library", ffi_close_library,          METH_VARARGS | METH_STATIC},
-    {"from_handle",   (PyCFunction)ffi_from_handle,METH_O},
-    {"gc",            (PyCFunction)ffi_gc,        METH_VARARGS},
-    {"getctype",      (PyCFunction)ffi_getctype,  METH_VARARGS},
-    {"load_library",  
(PyCFunction)ffi_load_library,METH_VARARGS|METH_KEYWORDS},
+    {"from_handle",(PyCFunction)ffi_from_handle,METH_O},
+    {"gc",         (PyCFunction)ffi_gc,         METH_VARARGS},
+    {"getctype",   (PyCFunction)ffi_getctype,   METH_VARARGS},
 #endif
-    {"offsetof",      (PyCFunction)ffi_offsetof, 
METH_VARARGS,ffi_offsetof_doc},
-    {"new",           (PyCFunction)ffi_new,       METH_VARARGS, ffi_new_doc},
+    {"offsetof",   (PyCFunction)ffi_offsetof,   METH_VARARGS, 
ffi_offsetof_doc},
+    {"new",        (PyCFunction)ffi_new,        METH_VARARGS, ffi_new_doc},
 #if 0
-    {"new_handle",    (PyCFunction)ffi_new_handle,METH_O},
+    {"new_handle", (PyCFunction)ffi_new_handle, METH_O},
 #endif
-    {"sizeof",        (PyCFunction)ffi_sizeof,    METH_O,       
ffi_sizeof_doc},
-    {"string",        (PyCFunction)ffi_string,    METH_VARARGS, 
ffi_string_doc},
-    {"typeof",        (PyCFunction)ffi_typeof,    METH_O,       
ffi_typeof_doc},
+    {"sizeof",     (PyCFunction)ffi_sizeof,     METH_O,       ffi_sizeof_doc},
+    {"string",     (PyCFunction)ffi_string,     METH_VARARGS, ffi_string_doc},
+    {"typeof",     (PyCFunction)ffi_typeof,     METH_O,       ffi_typeof_doc},
     {NULL}
 };
 
diff --git a/_cffi1/realize_c_type.c b/_cffi1/realize_c_type.c
--- a/_cffi1/realize_c_type.c
+++ b/_cffi1/realize_c_type.c
@@ -266,6 +266,27 @@
     }
 }
 
+/* Same as realize_c_type(), but if it's a function type, return the
+   corresponding function pointer ctype instead of complaining.
+*/
+static CTypeDescrObject *
+realize_c_type_fn_as_fnptr(builder_c_t *builder,
+                           _cffi_opcode_t opcodes[], int index)
+{
+    PyObject *x = _realize_c_type_or_func(builder, opcodes, index);
+    if (x == NULL || CTypeDescr_Check(x)) {
+        return (CTypeDescrObject *)x;
+    }
+    else {
+        PyObject *y;
+        assert(PyTuple_Check(x));
+        y = PyTuple_GET_ITEM(x, 0);
+        Py_INCREF(y);
+        Py_DECREF(x);
+        return (CTypeDescrObject *)y;
+    }
+}
+
 static PyObject *
 _realize_c_type_or_func(builder_c_t *builder,
                         _cffi_opcode_t opcodes[], int index)
diff --git a/_cffi1/recompiler.py b/_cffi1/recompiler.py
--- a/_cffi1/recompiler.py
+++ b/_cffi1/recompiler.py
@@ -762,4 +762,7 @@
             attr = getattr(module.ffi, name)
             if attr is not getattr(ffi, name, object()):
                 setattr(ffi, name, attr)
+    def typeof_disabled(*args, **kwds):
+        raise NotImplementedError
+    ffi._typeof = typeof_disabled
     return module.lib
diff --git a/_cffi1/test_ffi_obj.py b/_cffi1/test_ffi_obj.py
--- a/_cffi1/test_ffi_obj.py
+++ b/_cffi1/test_ffi_obj.py
@@ -82,3 +82,17 @@
     assert ffi.sizeof("int[41]") == 41 * 4
     assert ffi.sizeof(ffi.new("int[41]")) == 41 * 4
     assert ffi.sizeof(ffi.new("int[]", 41)) == 41 * 4
+
+def test_ffi_callback():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.callback("int(int)", lambda x: x + 42)(10) == 52
+    assert ffi.callback("int(*)(int)", lambda x: x + 42)(10) == 52
+    assert ffi.callback("int(int)", lambda x: x + "", -66)(10) == -66
+    assert ffi.callback("int(int)", lambda x: x + "", error=-66)(10) == -66
+
+def test_ffi_callback_decorator():
+    ffi = _cffi1_backend.FFI()
+    assert ffi.callback("int(*)(int)")(lambda x: x + 42)(10) == 52
+    deco = ffi.callback("int(int)", error=-66)
+    assert deco(lambda x: x + "")(10) == -66
+    assert deco(lambda x: x + 42)(10) == 52
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to