[pypy-commit] cffi default: Issue #22: Add ffi.inspecttype(). See the doc.

arigo noreply at buildbot.pypy.org
Sat Nov 10 18:09:31 CET 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r1037:bde32d4f45e8
Date: 2012-11-10 18:09 +0100
http://bitbucket.org/cffi/cffi/changeset/bde32d4f45e8/

Log:	Issue #22: Add ffi.inspecttype(). See the doc.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -3285,49 +3285,85 @@
     return NULL;
 }
 
-static PyObject *b__getfields(PyObject *self, PyObject *arg)
+static PyObject *b_inspecttype(PyObject *self, PyObject *arg)
 {
     CTypeDescrObject *ct = (CTypeDescrObject *)arg;
-    PyObject *d, *res;
 
     if (!CTypeDescr_Check(arg)) {
         PyErr_SetString(PyExc_TypeError,"expected a 'ctype' object");
         return NULL;
     }
-    d = (PyObject *)ct->ct_stuff;
-    if (d == NULL) {
-        res = Py_None;
-        Py_INCREF(res);
-    }
-    else if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
-        CFieldObject *cf;
-        res = PyList_New(0);
-        if (res == NULL)
-            return NULL;
-        for (cf = (CFieldObject *)ct->ct_extra; cf != NULL; cf = cf->cf_next) {
-            int err;
-            PyObject *o = PyTuple_Pack(2, get_field_name(ct, cf),
-                                       (PyObject *)cf);
-            err = (o != NULL) ? PyList_Append(res, o) : -1;
-            Py_XDECREF(o);
-            if (err < 0) {
-                Py_DECREF(res);
+    if ((ct->ct_flags & CT_PRIMITIVE_ANY) && !(ct->ct_flags & CT_IS_ENUM)) {
+        return Py_BuildValue("ss", "primitive", ct->ct_name);
+    }
+    if (ct->ct_flags & CT_POINTER) {
+        return Py_BuildValue("sO", "pointer", ct->ct_itemdescr);
+    }
+    if (ct->ct_flags & CT_ARRAY) {
+        if (ct->ct_length < 0)
+            return Py_BuildValue("sOO", "array", ct->ct_itemdescr, Py_None);
+        else
+            return Py_BuildValue("sOn", "array", ct->ct_itemdescr,
+                                 ct->ct_length);
+    }
+    if (ct->ct_flags & CT_VOID) {
+        return Py_BuildValue("(s)", "void");
+    }
+    if (ct->ct_flags & (CT_STRUCT|CT_UNION)) {
+        PyObject *res;
+        char *kind, *name;
+        kind = (ct->ct_flags & CT_STRUCT) ? "struct" : "union";
+        name = ct->ct_name;
+        while (*name != ' ')
+            name++;
+        name++;
+        if (ct->ct_flags & CT_IS_OPAQUE) {
+            return Py_BuildValue("ssOOO", kind, name,
+                                 Py_None, Py_None, Py_None);
+        }
+        else {
+            CFieldObject *cf;
+            res = PyList_New(0);
+            if (res == NULL)
                 return NULL;
+            for (cf = (CFieldObject *)ct->ct_extra;
+                 cf != NULL; cf = cf->cf_next) {
+                PyObject *o = PyTuple_Pack(2, get_field_name(ct, cf),
+                                           (PyObject *)cf);
+                int err = (o != NULL) ? PyList_Append(res, o) : -1;
+                Py_XDECREF(o);
+                if (err < 0) {
+                    Py_DECREF(res);
+                    return NULL;
+                }
             }
+            return Py_BuildValue("ssOnn", kind, name,
+                                 res, ct->ct_size, ct->ct_length);
         }
     }
-    else if (ct->ct_flags & CT_IS_ENUM) {
-        res = PyDict_Items(PyTuple_GET_ITEM(d, 1));
+    if (ct->ct_flags & CT_IS_ENUM) {
+        PyObject *res = PyDict_Items(PyTuple_GET_ITEM(ct->ct_stuff, 1));
         if (res == NULL)
             return NULL;
         if (PyList_Sort(res) < 0)
             Py_CLEAR(res);
-    }
-    else {
-        res = d;
-        Py_INCREF(res);
-    }
-    return res;
+        return Py_BuildValue("sO", "enum", res);
+    }
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *t = ct->ct_stuff;
+        PyObject *s = PyTuple_GetSlice(t, 2, PyTuple_GET_SIZE(t));
+        PyObject *o;
+        if (s == NULL)
+            return NULL;
+        o = Py_BuildValue("sOOOO", "function", s,
+                          PyTuple_GET_ITEM(t, 1),
+                          ct->ct_extra ? Py_False : Py_True,
+                          PyTuple_GET_ITEM(t, 0));
+        Py_DECREF(s);
+        return o;
+    }
+    PyErr_SetObject(PyExc_NotImplementedError, (PyObject *)ct);
+    return NULL;
 }
 
 struct funcbuilder_s {
@@ -4587,7 +4623,7 @@
     {"complete_struct_or_union", b_complete_struct_or_union, METH_VARARGS},
     {"new_function_type", b_new_function_type, METH_VARARGS},
     {"new_enum_type", b_new_enum_type, METH_VARARGS},
-    {"_getfields", b__getfields, METH_O},
+    {"inspecttype", b_inspecttype, METH_O},
     {"newp", b_newp, METH_VARARGS},
     {"cast", b_cast, METH_VARARGS},
     {"callback", b_callback, METH_VARARGS},
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1,6 +1,6 @@
 import py
 from _cffi_backend import *
-from _cffi_backend import _getfields, _testfunc, _get_types
+from _cffi_backend import _testfunc, _get_types
 
 # ____________________________________________________________
 
@@ -82,6 +82,10 @@
     p = new_primitive_type("signed char")
     assert repr(p) == "<ctype 'signed char'>"
 
+def test_inspect_primitive_type():
+    p = new_primitive_type("signed char")
+    assert inspecttype(p) == ("primitive", "signed char")
+
 def test_cast_to_signed_char():
     p = new_primitive_type("signed char")
     x = cast(p, -65 + 17*256)
@@ -219,6 +223,13 @@
     p = new_pointer_type(p)
     assert repr(p) == "<ctype 'int * * *'>"
 
+def test_inspect_pointer_type():
+    p1 = new_primitive_type("int")
+    p2 = new_pointer_type(p1)
+    assert inspecttype(p2) == ("pointer", p1)
+    p3 = new_pointer_type(p2)
+    assert inspecttype(p3) == ("pointer", p2)
+
 def test_pointer_to_int():
     BInt = new_primitive_type("int")
     py.test.raises(TypeError, newp, BInt)
@@ -420,6 +431,10 @@
     z = cast(BInt, y)
     assert int(z) == 42
 
+def test_void_type():
+    p = new_void_type()
+    assert inspecttype(p) == ("void",)
+
 def test_array_type():
     p = new_primitive_type("int")
     assert repr(p) == "<ctype 'int'>"
@@ -442,6 +457,13 @@
     py.test.raises(OverflowError,
                    new_array_type, new_pointer_type(p), sys.maxsize // 3)
 
+def test_inspect_array_type():
+    p = new_primitive_type("int")
+    p1 = new_array_type(new_pointer_type(p), None)
+    assert inspecttype(p1) == ("array", p, None)
+    p1 = new_array_type(new_pointer_type(p), 42)
+    assert inspecttype(p1) == ("array", p, 42)
+
 def test_array_instance():
     LENGTH = 1423
     p = new_primitive_type("int")
@@ -622,11 +644,14 @@
     BChar = new_primitive_type("char")
     BShort = new_primitive_type("short")
     BStruct = new_struct_type("foo")
-    assert _getfields(BStruct) is None
+    assert inspecttype(BStruct) == ("struct", "foo", None, None, None)
     complete_struct_or_union(BStruct, [('a1', BLong, -1),
                                        ('a2', BChar, -1),
                                        ('a3', BShort, -1)])
-    d = _getfields(BStruct)
+    k, n, d, s, a = inspecttype(BStruct)
+    assert k == "struct" and n == "foo"
+    assert s == sizeof(BLong) + 2 * sizeof(BShort)
+    assert a == sizeof(BLong)
     assert len(d) == 3
     assert d[0][0] == 'a1'
     assert d[0][1].type is BLong
@@ -650,10 +675,12 @@
     BLong = new_primitive_type("long")
     BChar = new_primitive_type("char")
     BUnion = new_union_type("foo")
-    assert _getfields(BUnion) is None
+    assert inspecttype(BUnion) == ("union", "foo", None, None, None)
     complete_struct_or_union(BUnion, [('a1', BLong, -1),
                                       ('a2', BChar, -1)])
-    d = _getfields(BUnion)
+    k, n, d, s, a = inspecttype(BUnion)
+    assert k == "union" and n == "foo"
+    assert s == a == sizeof(BLong)
     assert len(d) == 2
     assert d[0][0] == 'a1'
     assert d[0][1].type is BLong
@@ -774,6 +801,12 @@
     BFunc2 = new_function_type((), BFunc, False)
     assert repr(BFunc2) == "<ctype 'int(*(*)())(int, int)'>"
 
+def test_inspect_function_type():
+    BInt = new_primitive_type("int")
+    BFunc = new_function_type((BInt, BInt), BInt, False)
+    assert inspecttype(BFunc) == ("function", (BInt, BInt), BInt, False,
+                                  FFI_DEFAULT_ABI)
+
 def test_function_type_taking_struct():
     BChar = new_primitive_type("char")
     BShort = new_primitive_type("short")
@@ -1190,10 +1223,10 @@
 def test_enum_type():
     BEnum = new_enum_type("foo", (), ())
     assert repr(BEnum) == "<ctype 'enum foo'>"
-    assert _getfields(BEnum) == []
+    assert inspecttype(BEnum) == ("enum", [])
     #
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
-    assert _getfields(BEnum) == [(-20, 'ab'), (0, 'def'), (1, 'c')]
+    assert inspecttype(BEnum) == ("enum", [(-20, 'ab'), (0, 'def'), (1, 'c')])
 
 def test_cast_to_enum():
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
@@ -1287,7 +1320,7 @@
                                        ('a2', BLong, 2),
                                        ('a3', BLong, 3),
                                        ('a4', BLong, LONGBITS - 5)])
-    d = _getfields(BStruct)
+    d = inspecttype(BStruct)[2]
     assert d[0][1].offset == d[1][1].offset == d[2][1].offset == 0
     assert d[3][1].offset == sizeof(BLong)
     assert d[0][1].bitshift == 0
@@ -2164,7 +2197,7 @@
                                        ('a3', BChar, -1)])
     assert sizeof(BInnerStruct) == sizeof(BInt) * 2   # with alignment
     assert sizeof(BStruct) == sizeof(BInt) * 3        # 'a3' is placed after
-    d = _getfields(BStruct)
+    d = inspecttype(BStruct)[2]
     assert len(d) == 3
     assert d[0][0] == 'a1'
     assert d[0][1].type is BInt
diff --git a/cffi/api.py b/cffi/api.py
--- a/cffi/api.py
+++ b/cffi/api.py
@@ -312,6 +312,11 @@
                 self._typeof(self.getctype(ctype, '*')))
         return self._backend.rawaddressof(ctypeptr, cdata, offset)
 
+    def inspecttype(self, cdecl):
+        if isinstance(cdecl, str):
+            cdecl = self._typeof(cdecl)
+        return self._backend.inspecttype(cdecl)
+
 
 def _make_ffi_library(ffi, libname, flags):
     import os
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -1085,6 +1085,14 @@
 
 .. "versionadded:: 0.4" --- inlined in the previous paragraph
 
+``ffi.inspecttype(ctype)``: half-internal API.  Returns a tuple whose
+first item is a string describing the kind of ``ctype``, and whose
+remaining items give a full deconstruction of the type.  (Note that in
+the future the returned tuples may grow new items, as needed to
+represent new details of the type.)  *New in version 0.4.*
+
+.. "versionadded:: 0.4" --- inlined in the previous paragraph
+
 
 Unimplemented features
 ----------------------
diff --git a/testing/test_ffi_backend.py b/testing/test_ffi_backend.py
--- a/testing/test_ffi_backend.py
+++ b/testing/test_ffi_backend.py
@@ -20,3 +20,15 @@
                            "struct foo_s foo(void)", lambda: 42)
         assert str(e.value) == ("<struct foo_s(*)(void)>: "
             "cannot pass as argument or return value a struct with bit fields")
+
+    def test_inspecttype(self):
+        ffi = FFI(backend=self.Backend())
+        assert ffi.inspecttype("long") == ("primitive", "long")
+        assert ffi.inspecttype(ffi.typeof("long")) == ("primitive", "long")
+        pointer, LongP = ffi.inspecttype("long**")
+        assert pointer == "pointer"
+        pointer, Long = ffi.inspecttype(LongP)
+        assert pointer == "pointer"
+        assert ffi.inspecttype(Long) == ("primitive", "long")
+        assert ffi.inspecttype("long(*)(long, long, ...)")[:4] == (
+            "function", (Long, Long), Long, True)


More information about the pypy-commit mailing list