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