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

Reply via email to