Author: Armin Rigo <[email protected]>
Branch:
Changeset: r2669:eeef3869b994
Date: 2016-04-19 10:21 +0200
http://bitbucket.org/cffi/cffi/changeset/eeef3869b994/
Log: Support help(lib.foo)
diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -4659,7 +4659,8 @@
#undef ALIGN_ARG
-static void fb_cat_name(struct funcbuilder_s *fb, char *piece, int piecelen)
+static void fb_cat_name(struct funcbuilder_s *fb, const char *piece,
+ int piecelen)
{
if (fb->bufferp == NULL) {
fb->nb_bytes += piecelen;
@@ -4670,10 +4671,11 @@
}
}
-static int fb_build_name(struct funcbuilder_s *fb, PyObject *fargs,
- CTypeDescrObject *fresult, int ellipsis, int fabi)
-{
- Py_ssize_t i, nargs = PyTuple_GET_SIZE(fargs);
+static int fb_build_name(struct funcbuilder_s *fb, const char *repl,
+ CTypeDescrObject **pfargs, Py_ssize_t nargs,
+ CTypeDescrObject *fresult, int ellipsis)
+{
+ Py_ssize_t i;
fb->nargs = nargs;
/* name: the function type name we build here is, like in C, made
@@ -4682,25 +4684,22 @@
RESULT_TYPE_HEAD (*)(ARG_1_TYPE, ARG_2_TYPE, etc) RESULT_TYPE_TAIL
*/
fb_cat_name(fb, fresult->ct_name, fresult->ct_name_position);
+ if (repl[0] != '(' &&
+ fresult->ct_name[fresult->ct_name_position - 1] != '*')
+ fb_cat_name(fb, " ", 1); /* add a space */
+ fb_cat_name(fb, repl, strlen(repl));
+ if (fb->fct) {
+ i = strlen(repl) - 1; /* between '(*' and ')' */
+ assert(repl[i] == ')');
+ fb->fct->ct_name_position = fresult->ct_name_position + i;
+ }
fb_cat_name(fb, "(", 1);
- i = 2;
-#if defined(MS_WIN32) && !defined(_WIN64)
- if (fabi == FFI_STDCALL) {
- fb_cat_name(fb, "__stdcall ", 10);
- i += 10;
- }
-#endif
- fb_cat_name(fb, "*)(", 3);
- if (fb->fct) {
- i = fresult->ct_name_position + i; /* between '(*' and ')(' */
- fb->fct->ct_name_position = i;
- }
/* loop over the arguments */
for (i=0; i<nargs; i++) {
CTypeDescrObject *farg;
- farg = (CTypeDescrObject *)PyTuple_GET_ITEM(fargs, i);
+ farg = pfargs[i];
if (!CTypeDescr_Check(farg)) {
PyErr_SetString(PyExc_TypeError, "expected a tuple of ctypes");
return -1;
@@ -4730,14 +4729,23 @@
CTypeDescrObject *fresult,
int ellipsis, int fabi)
{
- CTypeDescrObject *fct;
+ CTypeDescrObject *fct, **pfargs;
+ Py_ssize_t nargs;
+ char *repl = "(*)";
fb->nb_bytes = 0;
fb->bufferp = NULL;
fb->fct = NULL;
+ pfargs = (CTypeDescrObject **)&PyTuple_GET_ITEM(fargs, 0);
+ nargs = PyTuple_GET_SIZE(fargs);
+#if defined(MS_WIN32) && !defined(_WIN64)
+ if (fabi == FFI_STDCALL)
+ repl = "(__stdcall *)";
+#endif
+
/* compute the total size needed for the name */
- if (fb_build_name(fb, fargs, fresult, ellipsis, fabi) < 0)
+ if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0)
return NULL;
/* allocate the function type */
@@ -4748,7 +4756,7 @@
/* call again fb_build_name() to really build the ct_name */
fb->bufferp = fct->ct_name;
- if (fb_build_name(fb, fargs, fresult, ellipsis, fabi) < 0)
+ if (fb_build_name(fb, repl, pfargs, nargs, fresult, ellipsis) < 0)
goto error;
assert(fb->bufferp == fct->ct_name + fb->nb_bytes);
diff --git a/c/lib_obj.c b/c/lib_obj.c
--- a/c/lib_obj.c
+++ b/c/lib_obj.c
@@ -3,8 +3,12 @@
module originally created by recompile().
A Lib object is special in the sense that it has a custom
- __getattr__ which returns C globals, functions and constants. It
- raises AttributeError for anything else, even attrs like '__class__'.
+ __getattr__ which returns C globals, functions and constants. The
+ original idea was to raise AttributeError for anything else, even
+ attrs like '__class__', but it breaks various things; now, standard
+ attrs are returned, but in the unlikely case where a user cdef()s
+ the same name, then the standard attr is hidden (and the various
+ things like introspection might break).
A Lib object has got a reference to the _cffi_type_context_s
structure, which is used to create lazily the objects returned by
@@ -15,9 +19,8 @@
PyMethodDef md;
void *direct_fn;
int type_index;
+ char doc[1];
};
-static const char cpyextfunc_doc[] =
- "direct call to the C function of the same name";
struct LibObject_s {
PyObject_HEAD
@@ -30,18 +33,22 @@
static struct CPyExtFunc_s *_cpyextfunc_get(PyObject *x)
{
- struct CPyExtFunc_s *exf;
+ PyObject *y;
+ LibObject *lo;
+ PyCFunctionObject *fo;
if (!PyCFunction_Check(x))
return NULL;
- if (!LibObject_Check(PyCFunction_GET_SELF(x)))
+ y = PyCFunction_GET_SELF(x);
+ if (!LibObject_Check(y))
return NULL;
- exf = (struct CPyExtFunc_s *)(((PyCFunctionObject *)x) -> m_ml);
- if (exf->md.ml_doc != cpyextfunc_doc)
+ fo = (PyCFunctionObject *)x;
+ lo = (LibObject *)y;
+ if (lo->l_libname != fo->m_module)
return NULL;
- return exf;
+ return (struct CPyExtFunc_s *)(fo->m_ml);
}
static PyObject *_cpyextfunc_type(LibObject *lib, struct CPyExtFunc_s *exf)
@@ -111,56 +118,82 @@
built. The C extension code can then assume that they are,
by calling _cffi_type().
*/
- CTypeDescrObject *ct;
+ PyObject *result = NULL;
+ CTypeDescrObject **pfargs;
+ CTypeDescrObject *fresult;
+ Py_ssize_t nargs = 0;
struct CPyExtFunc_s *xfunc;
int i, type_index = _CFFI_GETARG(g->type_op);
_cffi_opcode_t *opcodes = lib->l_types_builder->ctx.types;
+ static const char *const format = ";\n\nCFFI C function from %s.lib";
+ char *libname = PyText_AS_UTF8(lib->l_libname);
+ struct funcbuilder_s funcbuilder;
- if ((((uintptr_t)opcodes[type_index]) & 1) == 0) {
- /* the function type was already built. No need to force
- the arg and return value to be built again. */
+ /* return type: */
+ fresult = realize_c_func_return_type(lib->l_types_builder, opcodes,
+ type_index);
+ if (fresult == NULL)
+ goto error;
+
+ /* argument types: */
+ /* note that if the arguments are already built, they have a
+ pointer in the 'opcodes' array, and GETOP() returns a
+ random even value. But OP_FUNCTION_END is odd, so the
+ condition below still works correctly. */
+ i = type_index + 1;
+ while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END)
+ i++;
+ pfargs = alloca(sizeof(CTypeDescrObject *) * (i - type_index - 1));
+ i = type_index + 1;
+ while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) {
+ CTypeDescrObject *ct = realize_c_type(lib->l_types_builder, opcodes,
i);
+ if (ct == NULL)
+ goto error;
+ pfargs[nargs++] = ct;
+ i++;
}
- else {
- assert(_CFFI_GETOP(opcodes[type_index]) == _CFFI_OP_FUNCTION);
- /* return type: */
- ct = realize_c_type(lib->l_types_builder, opcodes,
- _CFFI_GETARG(opcodes[type_index]));
- if (ct == NULL)
- return NULL;
- Py_DECREF(ct);
-
- /* argument types: */
- i = type_index + 1;
- while (_CFFI_GETOP(opcodes[i]) != _CFFI_OP_FUNCTION_END) {
- ct = realize_c_type(lib->l_types_builder, opcodes, i);
- if (ct == NULL)
- return NULL;
- Py_DECREF(ct);
- i++;
- }
- }
+ memset(&funcbuilder, 0, sizeof(funcbuilder));
+ if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0)
+ goto error;
/* xxx the few bytes of memory we allocate here leak, but it's a
minor concern because it should only occur for CPYTHON_BLTN.
There is one per real C function in a CFFI C extension module.
CPython never unloads its C extension modules anyway.
*/
- xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s));
+ xfunc = PyMem_Malloc(sizeof(struct CPyExtFunc_s) +
+ funcbuilder.nb_bytes +
+ strlen(format) + strlen(libname));
if (xfunc == NULL) {
PyErr_NoMemory();
- return NULL;
+ goto error;
}
memset((char *)xfunc, 0, sizeof(struct CPyExtFunc_s));
assert(g->address);
xfunc->md.ml_meth = (PyCFunction)g->address;
xfunc->md.ml_flags = flags;
xfunc->md.ml_name = g->name;
- xfunc->md.ml_doc = cpyextfunc_doc;
+ xfunc->md.ml_doc = xfunc->doc;
xfunc->direct_fn = g->size_or_direct_fn;
xfunc->type_index = type_index;
- return PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname);
+ /* build the docstring */
+ funcbuilder.bufferp = xfunc->doc;
+ if (fb_build_name(&funcbuilder, g->name, pfargs, nargs, fresult, 0) < 0)
+ goto error;
+ sprintf(funcbuilder.bufferp - 1, format, libname);
+ /* done building the docstring */
+
+ result = PyCFunction_NewEx(&xfunc->md, (PyObject *)lib, lib->l_libname);
+ /* fall-through */
+ error:
+ Py_XDECREF(fresult);
+ while (nargs > 0) {
+ --nargs;
+ Py_DECREF(pfargs[nargs]);
+ }
+ return result;
}
static PyObject *lib_build_and_cache_attr(LibObject *lib, PyObject *name,
diff --git a/c/realize_c_type.c b/c/realize_c_type.c
--- a/c/realize_c_type.c
+++ b/c/realize_c_type.c
@@ -645,6 +645,33 @@
return x;
};
+static CTypeDescrObject *
+realize_c_func_return_type(builder_c_t *builder,
+ _cffi_opcode_t opcodes[], int index)
+{
+ PyObject *x;
+ CTypeDescrObject *ct;
+ _cffi_opcode_t op = opcodes[index];
+
+ if ((((uintptr_t)op) & 1) == 0) {
+ /* already built: assert that it is a function and fish
+ for the return type */
+ x = (PyObject *)op;
+ assert(PyTuple_Check(x)); /* from _CFFI_OP_FUNCTION */
+ x = PyTuple_GET_ITEM(x, 0);
+ assert(CTypeDescr_Check(x));
+ assert(((CTypeDescrObject *)x)->ct_flags & CT_FUNCTIONPTR);
+ x = PyTuple_GET_ITEM(((CTypeDescrObject *)x)->ct_stuff, 1);
+ assert(CTypeDescr_Check(x));
+ Py_INCREF(x);
+ return (CTypeDescrObject *)x;
+ }
+ else {
+ assert(_CFFI_GETOP(op) == _CFFI_OP_FUNCTION);
+ return realize_c_type(builder, opcodes, _CFFI_GETARG(opcodes[index]));
+ }
+}
+
static int do_realize_lazy_struct(CTypeDescrObject *ct)
{
/* This is called by force_lazy_struct() in _cffi_backend.c */
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -10,6 +10,13 @@
* ffi.unpack()
+* extern "Python+C"
+
+* in API mode, ``help(lib.foo)`` returns a docstring containing the C
+ signature now. Note that ``help(lib)`` itself is still useless; I
+ haven't figured out the hacks needed to convince ``pydoc`` of
+ showing more. You can use ``dir(lib)`` but it is not most helpful.
+
v1.5.2
======
diff --git a/testing/cffi1/test_recompiler.py b/testing/cffi1/test_recompiler.py
--- a/testing/cffi1/test_recompiler.py
+++ b/testing/cffi1/test_recompiler.py
@@ -416,8 +416,11 @@
def test_math_sin_type():
ffi = FFI()
- ffi.cdef("double sin(double);")
- lib = verify(ffi, 'test_math_sin_type', '#include <math.h>')
+ ffi.cdef("double sin(double); void *xxtestfunc();")
+ lib = verify(ffi, 'test_math_sin_type', """
+ #include <math.h>
+ void *xxtestfunc(void) { return 0; }
+ """)
# 'lib.sin' is typed as a <built-in method> object on lib
assert ffi.typeof(lib.sin).cname == "double(*)(double)"
# 'x' is another <built-in method> object on lib, made very indirectly
@@ -427,7 +430,16 @@
# present on built-in functions on CPython; must be emulated on PyPy:
assert lib.sin.__name__ == 'sin'
assert lib.sin.__module__ == '_CFFI_test_math_sin_type'
- assert lib.sin.__doc__ == 'direct call to the C function of the same name'
+ assert lib.sin.__doc__ == (
+ "double sin(double);\n"
+ "\n"
+ "CFFI C function from _CFFI_test_math_sin_type.lib")
+
+ assert ffi.typeof(lib.xxtestfunc).cname == "void *(*)()"
+ assert lib.xxtestfunc.__doc__ == (
+ "void *xxtestfunc();\n"
+ "\n"
+ "CFFI C function from _CFFI_test_math_sin_type.lib")
def test_verify_anonymous_struct_with_typedef():
ffi = FFI()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit