Armin Rigo avatar Armin Rigo committed bde32d4

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

Comments (0)

Files changed (5)

c/_cffi_backend.c

     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 {
     {"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},
 import py
 from _cffi_backend import *
-from _cffi_backend import _getfields, _testfunc, _get_types
+from _cffi_backend import _testfunc, _get_types
 
 # ____________________________________________________________
 
     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)
     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)
     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'>"
     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")
     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
     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
     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")
 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))
                                        ('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
                                        ('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
                 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

doc/source/index.rst

 
 .. "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
 ----------------------

testing/test_ffi_backend.py

                            "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)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.